Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ios27-menopause-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kingstinct/react-native-healthkit": minor
---

Add iOS 27 category types `HKCategoryTypeIdentifierMenopausalState` and `HKCategoryTypeIdentifierBleedingAfterMenopause`. Fix the schema generator to read `HKWorkoutActivityType` from its relocated header (`HKWorkoutActivityType.h`) on iOS 27 SDKs — where it moved out of `HKWorkout.h` — and add a verify guard so the enum can't silently drop again.
Original file line number Diff line number Diff line change
Expand Up @@ -1049,12 +1049,15 @@ export function buildHealthkitSchemaFromSources(sources: {
readonly metadataHeader: string
readonly metadataEnumsHeader: string
readonly workoutHeader: string
readonly workoutActivityTypeHeader: string
}): HealthkitSchema {
const enumsByName = new Map<string, EnumSchema>()
for (const enumSchema of [
...parseNsEnums(sources.categoryValuesHeader),
...parseNsEnums(sources.metadataEnumsHeader),
...parseNsEnums(sources.workoutHeader).filter(
...parseNsEnums(
`${sources.workoutHeader}\n${sources.workoutActivityTypeHeader}`,
).filter(
(schema) =>
schema.name === 'WorkoutActivityType' ||
schema.name === 'WorkoutEventType',
Expand Down
20 changes: 20 additions & 0 deletions packages/react-native-healthkit/scripts/healthkit-sdk.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { execFileSync } from 'node:child_process'
import {
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
Expand Down Expand Up @@ -47,6 +48,7 @@ export interface HealthkitSdkSources {
readonly metadataHeader: string
readonly metadataEnumsHeader: string
readonly workoutHeader: string
readonly workoutActivityTypeHeader: string
}

export interface GeneratedArtifactPaths {
Expand Down Expand Up @@ -110,6 +112,20 @@ function readHealthkitHeader(relativePath: string, sdkPath: string): string {
)
}

// Some enums (e.g. HKWorkoutActivityType, relocated into its own header in newer
// SDKs) only exist in certain SDK versions; missing headers read as empty.
function readOptionalHealthkitHeader(
relativePath: string,
sdkPath: string,
): string {
const headerPath = join(
sdkPath,
'System/Library/Frameworks/HealthKit.framework',
relativePath,
)
return existsSync(headerPath) ? readFileSync(headerPath, 'utf8') : ''
}

function readHealthkitSymbolGraph(sdkPath: string): SymbolGraphDocument {
const outputDir = mkdtempSync(join(tmpdir(), 'healthkit-symbolgraph-'))

Expand Down Expand Up @@ -161,6 +177,10 @@ export function loadHealthkitSdkSources(
sdkPath,
),
workoutHeader: readHealthkitHeader('Headers/HKWorkout.h', sdkPath),
workoutActivityTypeHeader: readOptionalHealthkitHeader(
'Headers/HKWorkoutActivityType.h',
sdkPath,
),
}
}

Expand Down
14 changes: 14 additions & 0 deletions packages/react-native-healthkit/scripts/verify-healthkit-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ function main() {
'Blood glucose should retain canonical unit mapping',
)

// Regression guard: iOS 27 relocated HKWorkoutActivityType into its own header,
// so a generator that only reads HKWorkout.h drops the whole enum.
const workoutActivityType = schema.enums.find(
(schemaEnum) => schemaEnum.name === 'WorkoutActivityType',
)
assert.ok(
workoutActivityType,
'WorkoutActivityType enum should be present regardless of which header defines it',
)
assert.ok(
workoutActivityType.members.length > 50,
'WorkoutActivityType should retain its full member set, not a stub',
)

const workoutBrandName = findMetadataKey(
schema,
'HKMetadataKeyWorkoutBrandName',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
"name": "HKQuantityTypeIdentifierCyclingCadence",
"ios": "17.0",
"canonicalUnit": "count/min",
"aggregationStyle": "Discrete (Arithmetic)",
"aggregationStyle": "Discrete (Temporally Weighted)",
"writeable": true,
"legacy": false
},
Expand All @@ -172,15 +172,15 @@
"name": "HKQuantityTypeIdentifierCyclingPower",
"ios": "17.0",
"canonicalUnit": "W",
"aggregationStyle": "Discrete (Arithmetic)",
"aggregationStyle": "Discrete (Temporally Weighted)",
"writeable": true,
"legacy": false
},
{
"name": "HKQuantityTypeIdentifierCyclingSpeed",
"ios": "17.0",
"canonicalUnit": "m/s",
"aggregationStyle": "Discrete (Arithmetic)",
"aggregationStyle": "Discrete (Temporally Weighted)",
"writeable": true,
"legacy": false
},
Expand Down Expand Up @@ -772,7 +772,7 @@
"name": "HKQuantityTypeIdentifierRestingHeartRate",
"ios": "11.0",
"canonicalUnit": "count/min",
"aggregationStyle": "Discrete (Arithmetic)",
"aggregationStyle": "Discrete (Temporally Weighted)",
"writeable": true,
"legacy": false
},
Expand Down Expand Up @@ -924,7 +924,7 @@
"name": "HKQuantityTypeIdentifierWalkingHeartRateAverage",
"ios": "11.0",
"canonicalUnit": "count/min",
"aggregationStyle": "Discrete (Arithmetic)",
"aggregationStyle": "Discrete (Temporally Weighted)",
"writeable": false,
"legacy": false
},
Expand Down Expand Up @@ -1011,6 +1011,13 @@
"writeable": true,
"legacy": false
},
{
"name": "HKCategoryTypeIdentifierBleedingAfterMenopause",
"ios": "27.0",
"valueEnum": "HKCategoryValueVaginalBleeding",
"writeable": true,
"legacy": false
},
{
"name": "HKCategoryTypeIdentifierBleedingAfterPregnancy",
"ios": "18.0",
Expand Down Expand Up @@ -1270,6 +1277,13 @@
"writeable": true,
"legacy": false
},
{
"name": "HKCategoryTypeIdentifierMenopausalState",
"ios": "27.0",
"valueEnum": "HKCategoryValueMenopausalState",
"writeable": true,
"legacy": false
},
{
"name": "HKCategoryTypeIdentifierMenstrualFlow",
"ios": "9.0",
Expand Down Expand Up @@ -1747,6 +1761,27 @@
}
]
},
{
"name": "CategoryValueMenopausalState",
"ios": "27.0",
"members": [
{
"name": "menopause",
"swiftName": "HKCategoryValueMenopausalStateMenopause",
"rawValue": 1
},
{
"name": "perimenopause",
"swiftName": "HKCategoryValueMenopausalStatePerimenopause",
"rawValue": 2
},
{
"name": "none",
"swiftName": "HKCategoryValueMenopausalStateNone",
"rawValue": 3
}
]
},
{
"name": "CategoryValueMenstrualFlow",
"ios": "9.0",
Expand Down Expand Up @@ -2806,6 +2841,16 @@
"swiftName": "HKWorkoutActivityTypeUnderwaterDiving",
"rawValue": 84
},
{
"name": "rest",
"swiftName": "HKWorkoutActivityTypeRest",
"rawValue": 2998
},
{
"name": "group",
"swiftName": "HKWorkoutActivityTypeGroup",
"rawValue": 2999
},
{
"name": "other",
"swiftName": "HKWorkoutActivityTypeOther",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export type CategoryTypeIdentifierWriteable =
| 'HKCategoryTypeIdentifierAppleWalkingSteadinessEvent'
| 'HKCategoryTypeIdentifierAudioExposureEvent'
| 'HKCategoryTypeIdentifierBladderIncontinence'
| 'HKCategoryTypeIdentifierBleedingAfterMenopause'
| 'HKCategoryTypeIdentifierBleedingAfterPregnancy'
| 'HKCategoryTypeIdentifierBleedingDuringPregnancy'
| 'HKCategoryTypeIdentifierBloating'
Expand Down Expand Up @@ -186,6 +187,7 @@ export type CategoryTypeIdentifierWriteable =
| 'HKCategoryTypeIdentifierLowCardioFitnessEvent'
| 'HKCategoryTypeIdentifierLowerBackPain'
| 'HKCategoryTypeIdentifierMemoryLapse'
| 'HKCategoryTypeIdentifierMenopausalState'
| 'HKCategoryTypeIdentifierMenstrualFlow'
| 'HKCategoryTypeIdentifierMindfulSession'
| 'HKCategoryTypeIdentifierMoodChanges'
Expand Down Expand Up @@ -345,6 +347,7 @@ export const CATEGORY_IDENTIFIER_IOS_AVAILABILITY = {
HKCategoryTypeIdentifierAppleWalkingSteadinessEvent: '15.0',
HKCategoryTypeIdentifierAudioExposureEvent: '13.0',
HKCategoryTypeIdentifierBladderIncontinence: '14.0',
HKCategoryTypeIdentifierBleedingAfterMenopause: '27.0',
HKCategoryTypeIdentifierBleedingAfterPregnancy: '18.0',
HKCategoryTypeIdentifierBleedingDuringPregnancy: '18.0',
HKCategoryTypeIdentifierBloating: '13.6',
Expand Down Expand Up @@ -382,6 +385,7 @@ export const CATEGORY_IDENTIFIER_IOS_AVAILABILITY = {
HKCategoryTypeIdentifierLowerBackPain: '13.6',
HKCategoryTypeIdentifierLowHeartRateEvent: '12.2',
HKCategoryTypeIdentifierMemoryLapse: '14.0',
HKCategoryTypeIdentifierMenopausalState: '27.0',
HKCategoryTypeIdentifierMenstrualFlow: '9.0',
HKCategoryTypeIdentifierMindfulSession: '10.0',
HKCategoryTypeIdentifierMoodChanges: '13.6',
Expand Down Expand Up @@ -540,6 +544,8 @@ export const CATEGORY_IDENTIFIER_VALUE_ENUMS = {
'CategoryValueAppleWalkingSteadinessEvent',
HKCategoryTypeIdentifierAudioExposureEvent: null,
HKCategoryTypeIdentifierBladderIncontinence: 'CategoryValueSeverity',
HKCategoryTypeIdentifierBleedingAfterMenopause:
'CategoryValueVaginalBleeding',
HKCategoryTypeIdentifierBleedingAfterPregnancy:
'CategoryValueVaginalBleeding',
HKCategoryTypeIdentifierBleedingDuringPregnancy:
Expand Down Expand Up @@ -583,6 +589,7 @@ export const CATEGORY_IDENTIFIER_VALUE_ENUMS = {
HKCategoryTypeIdentifierLowerBackPain: 'CategoryValueSeverity',
HKCategoryTypeIdentifierLowHeartRateEvent: 'CategoryValue',
HKCategoryTypeIdentifierMemoryLapse: 'CategoryValueSeverity',
HKCategoryTypeIdentifierMenopausalState: 'CategoryValueMenopausalState',
HKCategoryTypeIdentifierMenstrualFlow: 'CategoryValueMenstrualFlow',
HKCategoryTypeIdentifierMindfulSession: 'CategoryValue',
HKCategoryTypeIdentifierMoodChanges: 'CategoryValuePresence',
Expand Down Expand Up @@ -683,6 +690,11 @@ export enum CategoryValueHeadphoneAudioExposureEvent {
export enum CategoryValueLowCardioFitnessEvent {
lowFitness = 1,
}
export enum CategoryValueMenopausalState {
menopause = 1,
perimenopause = 2,
none = 3,
}
export enum CategoryValueMenstrualFlow {
unspecified = 1,
light = 2,
Expand Down Expand Up @@ -911,6 +923,8 @@ export enum WorkoutActivityType {
swimBikeRun = 82,
transition = 83,
underwaterDiving = 84,
rest = 2998,
group = 2999,
other = 3000,
}
export enum WorkoutEventType {
Expand All @@ -935,6 +949,7 @@ export interface CategoryValueByIdentifierMap {
readonly HKCategoryTypeIdentifierAppleStandHour: CategoryValueAppleStandHour
readonly HKCategoryTypeIdentifierAppleWalkingSteadinessEvent: CategoryValueAppleWalkingSteadinessEvent
readonly HKCategoryTypeIdentifierBladderIncontinence: CategoryValueSeverity
readonly HKCategoryTypeIdentifierBleedingAfterMenopause: CategoryValueVaginalBleeding
readonly HKCategoryTypeIdentifierBleedingAfterPregnancy: CategoryValueVaginalBleeding
readonly HKCategoryTypeIdentifierBleedingDuringPregnancy: CategoryValueVaginalBleeding
readonly HKCategoryTypeIdentifierBloating: CategoryValueSeverity
Expand Down Expand Up @@ -972,6 +987,7 @@ export interface CategoryValueByIdentifierMap {
readonly HKCategoryTypeIdentifierLowerBackPain: CategoryValueSeverity
readonly HKCategoryTypeIdentifierLowHeartRateEvent: CategoryValue
readonly HKCategoryTypeIdentifierMemoryLapse: CategoryValueSeverity
readonly HKCategoryTypeIdentifierMenopausalState: CategoryValueMenopausalState
readonly HKCategoryTypeIdentifierMenstrualFlow: CategoryValueMenstrualFlow
readonly HKCategoryTypeIdentifierMindfulSession: CategoryValue
readonly HKCategoryTypeIdentifierMoodChanges: CategoryValuePresence
Expand Down