What a femtech iOS developer checks first when a cycle app crashes on a HealthKit write

A femtech iOS developer on the sexual-activity write that crashes HealthKit, and the line between a cycle estimate and a medical claim.

You ship a cycle and fertility app, a user logs an intimate-health entry, and the app dies on the write. No Swift error to catch, no line in the crash log that names a function you wrote. As a femtech iOS developer this is the call I get most often, and the cause is almost never where the team is looking. The team is reading their own save path. The bug is upstream of it, inside a framework they assume is well-behaved.

A sexual-activity write in a cycle app reaches HKObject validation, which raises an NSException that a do/catch never sees, and the process goes down.
The write looks ordinary. The framework underneath it does not fail the way Swift code fails.

Femtech sits on top of two systems that were never designed for it: HealthKit, which models bodies as a fixed list of sample types, and App Review, which classifies apps by the content in their bundle. The expensive bugs in this category come from treating either as if it behaves like ordinary application code. What follows is the set of things I check before I touch a line of the team's own logic.

Why does HealthKit crash when my app writes a sexual-activity sample?

Because .sexualActivity only accepts HKCategoryValue.notApplicable, and HealthKit enforces that with an Objective-C NSException rather than a Swift Error. Pass any other category value, or a sample whose start or end date is in the future, and the exception fires from inside HKObject validation before the sample is ever stored. A do/catch around your save call is built to catch a Swift Error; it is structurally incapable of catching an NSException, so the exception propagates past it and the process is terminated.

A wrong category value or a future date makes HealthKit raise an Objective-C NSException rather than a Swift Error, so the do/catch around the write is bypassed and the process is killed unless the date is clamped and the value forced before building the sample.
The exception is an Objective-C one, raised below the Swift layer your error handling is written in.

This matters because the trigger is almost never the obvious one. Nobody deliberately writes a future date; it arrives sideways. A user edits an entry for last Tuesday, a background sync rebases the timestamp, a time zone shift pushes a 23:00 local entry past midnight UTC, and the date sits a few minutes ahead of Date() on the device performing the write. The first crash report has no reproduction steps because the person who filed it did nothing unusual.

The category value is the quieter half of the trap. HealthKit's category samples carry an integer value, meaningful for most types - sleep stages, menstrual flow levels. For .sexualActivity it is not: the only legal value is notApplicable, which is zero, and any code reaching for a "1 means yes" convention constructs a sample that validation rejects on contact.[1]1. HKCategoryValueNotApplicable is the only defined value for the .sexualActivity category type; see Apple's HealthKit documentation for HKCategoryValueNotApplicable and HKCategorySample. The "protection used" fact is recorded separately via the HKMetadataKeySexualActivityProtectionUsed metadata key. The fact you actually want to record - whether protection was used - lives in metadata under HKMetadataKeySexualActivityProtectionUsed, not the category value. Teams routinely conflate the two and crash on the value while the metadata sits empty.

How do you stop the sexual-activity write from killing the app?

You make the sample legal before it is ever constructed, because once HKCategorySample exists with bad inputs the exception is inevitable. None of this is in the documentation; you learn it by reading a crash report you can't reproduce. In SilkSilkSilkPrivate intimate wellness trackerView app the save path clamps the date with a plain min(data.date, Date()) so a sample can never be dated ahead of the device clock, forces the category value to notApplicable rather than trusting the caller, and puts the protection flag in metadata where it belongs. Those few lines separate a logged entry from a dead process, and they only get written after the first crash teaches you that validation here throws across the language boundary.

The reason a "wrap it in a try" instinct fails generalises. Swift's error model and Objective-C's exception model are separate mechanisms that share a runtime. try/catch is for the former; NSException belongs to the latter, and the only tool that intercepts it is an Objective-C @try/@catch in a bridging layer - which even then leaves the framework in an undefined state you should not trust. The correct posture is to never produce it: validate and clamp the inputs so the exception has no reason to fire. When the failure mode is an exception you were never meant to recover from, the only handling that works is not constructing the bad sample in the first place.

This is why it is the first thing I look for. HealthKit is consistent about expressing "you used me wrong" - a future date, an illegal value, a write before requestAuthorization resolves - as a hard exception rather than a recoverable error. If your femtech app crashes inside a write and your own code is clean, the framework is telling you the inputs were illegal a layer up, so the fix is rarely in the function the bug report points at.

When does a cycle estimate become a medical claim?

The moment the wording asserts a fact about the user's body instead of a number derived from a calendar. A cycle tracking app developer who writes "ovulation typically occurs around 14 days before your next period," offsets the recorded cycle length, and shows a fertility window of a few days either side has built a calendar estimate, and that is fine. The identical maths becomes a medical claim the instant the copy, the App Store metadata, or a push notification phrases it as certainty: "you are fertile now," "your period is late." The prediction is the same calendar arithmetic in both cases; only the grammar moved, from a hedged estimate to a flat statement of fact, and App Review reads a declarative health statement very differently from a conditional one.[2]2. App Store Review Guideline 1.4.1 (Physical Harm) and 5.1.1 (Data Collection and Storage) govern health, medical, and personal-data handling; apps that make medical or diagnostic claims face additional scrutiny under the Health and Health Research provisions of guideline 5.1.3.

The grammar carries real consequences. App Review treats apps that diagnose, infer, or make medical claims as a distinct category, and a fertility window described as fact can pull your submission into the medical-app review path whether or not you set out to build a medical device. The legal exposure compounds the platform one: in several US states reproductive-health inference now carries real consequence for whoever holds the data, and "you are fertile now," stored on a server you control, is an inference lawyers and regulators have started reading literally. The cheapest place to fix this is in the string, before it ships, by keeping every prediction in the conditional voice the maths can support. I wrote about how arbitrary the App Store's classifier looks from outside in Apple's Problem with Bodies; the system keys on words, so the words are where the risk lives.

Where does intimate health data leak, when the app never meant to send it anywhere?

Through the four channels a build adds by default without anyone deciding to: an analytics SDK, a sync container, a misconfigured Keychain item, and a backup that captures plaintext. The default failure mode for an intimate health app is rarely a dramatic breach. It is a quiet export of the most sensitive record a person keeps, added by a dependency doing its ordinary job. An analytics or crash SDK that logs screen names will record that a user opened "Intimacy Log," and the fact of the data now exists in a third party's pipeline even if the values never left the device. A CloudKit container switched on for "free sync" quietly copies the journal into iCloud. A Keychain item written with the wrong accessibility class, kSecAttrAccessibleAfterFirstUnlock instead of a ThisDeviceOnly variant, lets the encryption key sync to the user's other devices through iCloud Keychain, so the key that protects the data travels off the phone you promised it would never leave. And unencrypted local storage is captured wholesale by an unencrypted Finder backup. Any one of them moves the data somewhere you no longer control, and none of them announces itself.

Silk's architecture is a set of decisions against each channel. Entries are encrypted at rest with a key derived from the user's PIN through PBKDF2 and stored with AES-GCM,[3]3. Silk derives its encryption key from the user's PIN using PBKDF2 (PKCS5.PBKDF2) and encrypts entries with AES-GCM via CryptoSwift, with key and salt held in the Keychain - a local-only design with no sync container and no analytics on sensitive screens. the key material lives in the Keychain, and nothing about the content is sent anywhere - no analytics on the sensitive screens, no sync container holding the journal, no plaintext for a backup to scoop up. That posture is easy to undo by accident in a later sprint. Someone adds a crash reporter to chase a bug, toggles iCloud sync for a feature request, or changes a Keychain accessibility flag to fix a "key not found after restore" complaint, and the privacy guarantee evaporates with the commit that fixed something else. The initial design is the easy part; holding it means knowing which routine changes silently breach it, which is the security-audit work where the finding usually traces to a default left on rather than a bad algorithm.

What changes for femtech in iOS 27?

The store gets a new way for data to arrive that you didn't write. In iOS 27, Visual Intelligence can write medical-device readings - blood pressure, glucose, weight - straight into HKHealthStore for any app already authorized to read those types.[4]4. WWDC 2026 session 297 - Visual Intelligence can write medical-device readings (blood pressure, glucose, weight) into HealthKit via HKHealthStore as a free input source for apps that already read those types. If your femtech app reads weight or glucose, you inherit an input source you never built or tested, and samples can land in your store from a path that bypasses your UI entirely, carrying a value you never validated. The defensive posture from the crash earlier applies: assume incoming samples carry dates, units, or metadata your read path didn't anticipate, and validate on the way in. Every new inlet is something your privacy model now has to expect.

Where the data lives, when an estimate stops being an estimate, and which inlets your store has to defend against are build-specific calls. They turn on your threat model and the jurisdictions your users sit in, which is why a generic checklist gets them wrong: a local-only journal and a synced clinical tool need different answers to each one. If you are building in this category and want the privacy architecture and the HealthKit layer to hold up before someone audits them, that is the femtech and cycle-health work I do.


  1. HKCategoryValueNotApplicable is the only defined value for the .sexualActivity category type; see Apple's HealthKit documentation for HKCategoryValueNotApplicable and HKCategorySample. The "protection used" fact is recorded separately via the HKMetadataKeySexualActivityProtectionUsed metadata key. ↩︎

  2. App Store Review Guideline 1.4.1 (Physical Harm) and 5.1.1 (Data Collection and Storage) govern health, medical, and personal-data handling; apps that make medical or diagnostic claims face additional scrutiny under the Health and Health Research provisions of guideline 5.1.3. ↩︎

  3. Silk derives its encryption key from the user's PIN using PBKDF2 (PKCS5.PBKDF2) and encrypts entries with AES-GCM via CryptoSwift, with key and salt held in the Keychain - a local-only design with no sync container and no analytics on sensitive screens. ↩︎

  4. WWDC 2026 session 297 - Visual Intelligence can write medical-device readings (blood pressure, glucose, weight) into HealthKit via HKHealthStore as a free input source for apps that already read those types. ↩︎