diff --git a/.changeset/typed-healthkit-bindings.md b/.changeset/typed-healthkit-bindings.md
new file mode 100644
index 00000000..5a1f87aa
--- /dev/null
+++ b/.changeset/typed-healthkit-bindings.md
@@ -0,0 +1,21 @@
+---
+'@kingstinct/react-native-healthkit': major
+---
+
+Make typed `metadata` the canonical metadata API and generate more of the HealthKit type surface from Apple’s SDK.
+
+This release introduces generated identifier/value/unit metadata, typed metadata payloads on returned models, generic quantity sample typing, and SDK-backed schema verification to keep the surfaced API aligned with the pinned Xcode HealthKit SDK.
+
+Breaking changes:
+
+- Remove the legacy flattened `metadataX` fields from returned models.
+- Make `metadata` the single canonical metadata surface.
+
+Migration examples:
+
+- `sample.metadataExternalUUID` -> `sample.metadata.HKExternalUUID`
+- `sample.metadataWeatherCondition` -> `sample.metadata.HKWeatherCondition`
+- `workout.metadataAverageMETs` -> `workout.metadata.HKAverageMETs`
+- `categorySample.metadataMenstrualCycleStart` -> `categorySample.metadata.HKMenstrualCycleStart`
+
+This is intended to make the library easier to extend over time: Apple SDK metadata flows into the generated schema and typed `metadata` surface with much less hand-maintained code.
diff --git a/.github/workflows/package-preview.yml b/.github/workflows/package-preview.yml
index 6c5d17f0..6690bbc7 100644
--- a/.github/workflows/package-preview.yml
+++ b/.github/workflows/package-preview.yml
@@ -1,15 +1,15 @@
name: Package Preview
on:
- pull_request_target:
+ pull_request:
+
+permissions: {}
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- with:
- ref: refs/pull/${{ github.event.number }}/merge
- uses: oven-sh/setup-bun@v2
with:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 64866117..c85f9646 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -38,6 +38,47 @@ on:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
+ changes:
+ runs-on: ubuntu-latest
+ outputs:
+ healthkit_contracts: ${{ steps.filter.outputs.healthkit_contracts }}
+ swift_native: ${{ steps.filter.outputs.swift_native }}
+ permissions:
+ pull-requests: read
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: dorny/paths-filter@v3
+ id: filter
+ with:
+ filters: |
+ healthkit_contracts:
+ - '.bun-version'
+ - 'bun.lock'
+ - 'package.json'
+ - 'packages/react-native-healthkit/src/**'
+ - 'packages/react-native-healthkit/ios/**'
+ - 'packages/react-native-healthkit/cpp/**'
+ - 'packages/react-native-healthkit/scripts/**'
+ - 'packages/react-native-healthkit/nitrogen/**'
+ - 'packages/react-native-healthkit/package.json'
+ - 'packages/react-native-healthkit/tsconfig*.json'
+ - 'apps/example/contracts/**'
+ - 'apps/example/scripts/run-healthkit-contracts.sh'
+ - 'apps/example/app.json'
+ - 'apps/example/app/contracts.tsx'
+ - 'apps/example/app/_layout.tsx'
+ - 'apps/example/app/auth.tsx'
+ - 'apps/example/constants/AllUsedIdentifiersInApp.ts'
+ - 'apps/example/ios/**'
+ - 'apps/example/package.json'
+ - '.github/workflows/test.yml'
+ swift_native:
+ - 'packages/react-native-healthkit/ios/**'
+ - 'packages/react-native-healthkit/cpp/**'
+ - 'apps/example/ios/**'
+
test:
# The type of runner that the job will run on
runs-on: ${{ inputs.os || 'ubuntu-latest' }}
@@ -95,8 +136,10 @@ jobs:
run: bun run lint
swiftlint:
+ needs: changes
+ if: needs.changes.outputs.swift_native == 'true'
# The type of runner that the job will run on
- runs-on: ${{ inputs.os || 'macos-latest' }}
+ runs-on: macos-15
timeout-minutes: 10
# Steps represent a sequence of tasks that will be executed as part of the job
@@ -139,9 +182,11 @@ jobs:
# xcodebuild -downloadPlatform iOS
# # optional but useful:
build-ios:
+ needs: changes
+ if: needs.changes.outputs.healthkit_contracts == 'true'
# Only run on macOS since we need Xcode
runs-on: macos-15
- timeout-minutes: 50
+ timeout-minutes: 25
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
@@ -157,13 +202,45 @@ jobs:
## Xcode 26.2 (Swift 6.2) - latest stable
- run: sudo xcode-select -s /Applications/Xcode_26.2.app
+ - name: Install simulator tooling
+ run: |
+ brew tap wix/brew
+ brew install applesimutils
+
+ - name: Verify generated HealthKit schema and bindings
+ working-directory: packages/react-native-healthkit
+ run: bun run check:generated
+
- run: bun run codegen
working-directory: packages/react-native-healthkit
- name: Expo Prebuild
working-directory: apps/example
- run: bunx expo prebuild --platform ios
+ run: bunx expo prebuild --platform ios --non-interactive
- name: Build iOS project
- working-directory: apps/example
- run: bun run build-sim
+ working-directory: apps/example/ios
+ run: |
+ SIMULATOR_ID="$(
+ xcrun simctl list devices available |
+ sed -n 's/^[[:space:]]*iPhone[^()]* (\([0-9A-F-][0-9A-F-]*\)) (.*/\1/p' |
+ head -n 1
+ )"
+ if [ -z "$SIMULATOR_ID" ]; then
+ echo "Unable to find an available iPhone simulator." >&2
+ exit 1
+ fi
+ xcodebuild \
+ -quiet \
+ -workspace RNHealthKit.xcworkspace \
+ -scheme RNHealthKit \
+ -configuration Debug \
+ -sdk iphonesimulator \
+ -destination "id=$SIMULATOR_ID" \
+ ONLY_ACTIVE_ARCH=YES \
+ COMPILER_INDEX_STORE_ENABLE=NO \
+ DEBUG_INFORMATION_FORMAT=dwarf \
+ build
+
+ - name: Run HealthKit contracts
+ run: bun run test:contracts
diff --git a/.gitignore b/.gitignore
index f26b5327..6e8db7c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,8 @@ DerivedData
*.ipa
*.xcuserstate
project.xcworkspace
+**/.build
+**/.swiftpm
# Android/IJ
#
@@ -79,4 +81,4 @@ lib/
packages/react-native-healthkit/nitrogen/generated
apps/example/android
-tsconfig.tsbuildinfo
\ No newline at end of file
+tsconfig.tsbuildinfo
diff --git a/apps/example/app/(tabs)/index.tsx b/apps/example/app/(tabs)/index.tsx
index 4e229c50..4eacea94 100644
--- a/apps/example/app/(tabs)/index.tsx
+++ b/apps/example/app/(tabs)/index.tsx
@@ -17,6 +17,7 @@ import {
FitzpatrickSkinType,
WheelchairUse,
} from '@kingstinct/react-native-healthkit/types/Characteristics'
+import { router } from 'expo-router'
import { useEffect, useMemo, useState } from 'react'
import { ListItem, type ListItemProps } from '@/components/SwiftListItem'
import { enumKeyLookup } from '@/utils/enumKeyLookup'
@@ -175,6 +176,15 @@ const CoreTab = () => {
/>
))}
+
+ {
+ router.push('/contracts')
+ }}
+ />
+
)
diff --git a/apps/example/app/_layout.tsx b/apps/example/app/_layout.tsx
index 4b2a2871..46aa149a 100644
--- a/apps/example/app/_layout.tsx
+++ b/apps/example/app/_layout.tsx
@@ -4,11 +4,13 @@ import {
ThemeProvider,
} from '@react-navigation/native'
import { useFonts } from 'expo-font'
-import { Stack } from 'expo-router'
+import { Stack, usePathname, useRouter } from 'expo-router'
import { StatusBar } from 'expo-status-bar'
+import { useEffect } from 'react'
import 'react-native-reanimated'
import { GestureHandlerRootView } from 'react-native-gesture-handler'
+import { readLaunchCommand } from '@/contracts/launchCommand'
import { useColorScheme } from '@/hooks/useColorScheme'
export const unstable_settings = {
@@ -17,10 +19,37 @@ export const unstable_settings = {
export default function RootLayout() {
const colorScheme = useColorScheme()
+ const pathname = usePathname()
+ const router = useRouter()
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
})
+ useEffect(() => {
+ if (!loaded) {
+ return
+ }
+
+ const launchCommand = readLaunchCommand()
+ if (!launchCommand) {
+ return
+ }
+
+ if (
+ launchCommand.route === 'contracts' &&
+ pathname !== '/contracts' &&
+ pathname !== '/auth'
+ ) {
+ router.replace({
+ pathname: '/contracts',
+ params: {
+ autorun: launchCommand.autorun,
+ scenario: launchCommand.scenario,
+ },
+ })
+ }
+ }, [loaded, pathname, router])
+
if (!loaded) {
// Async font loading only occurs in development.
return null
@@ -32,6 +61,12 @@ export default function RootLayout() {
+
{
try {
const res = await requestAuthorization({
@@ -32,11 +38,23 @@ export default function AuthScreen() {
initSubscriptions()
alert(`response: ${res}`)
+ if (launchCommand?.route === 'contracts') {
+ clearLaunchCommand()
+ router.replace({
+ pathname: '/contracts',
+ params: {
+ autorun: launchCommand.autorun,
+ scenario: launchCommand.scenario,
+ },
+ })
+ return
+ }
+
router.replace('/')
} catch (error) {
console.error('Error requesting authorization:', error)
}
- }, [])
+ }, [launchCommand])
const [status, setStatus] = useState(null)
@@ -63,6 +81,29 @@ export default function AuthScreen() {
updateStatus()
}, [])
+ useEffect(() => {
+ if (status === AuthorizationRequestStatus.unnecessary) {
+ if (launchCommand?.route === 'contracts') {
+ clearLaunchCommand()
+ router.replace({
+ pathname: '/contracts',
+ params: {
+ autorun: launchCommand.autorun,
+ scenario: launchCommand.scenario,
+ },
+ })
+ }
+ return
+ }
+
+ if (
+ status === AuthorizationRequestStatus.shouldRequest &&
+ launchCommand?.route === 'contracts'
+ ) {
+ void requestAuth()
+ }
+ }, [launchCommand, requestAuth, status])
+
return (
diff --git a/apps/example/app/contracts.tsx b/apps/example/app/contracts.tsx
new file mode 100644
index 00000000..844d70f7
--- /dev/null
+++ b/apps/example/app/contracts.tsx
@@ -0,0 +1,313 @@
+import {
+ getRequestStatusForAuthorization,
+ isHealthDataAvailable,
+ requestAuthorization,
+} from '@kingstinct/react-native-healthkit'
+import { AuthorizationRequestStatus } from '@kingstinct/react-native-healthkit/types/Auth'
+import { useLocalSearchParams } from 'expo-router'
+import { useCallback, useEffect, useMemo, useState } from 'react'
+import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'
+import {
+ AllObjectTypesInApp,
+ AllSampleTypesInApp,
+} from '@/constants/AllUsedIdentifiersInApp'
+import { clearLaunchCommand } from '@/contracts/launchCommand'
+import { writeContractReport } from '@/contracts/report'
+import {
+ contractScenarios,
+ runAllContractScenarios,
+} from '@/contracts/scenarios'
+import { enumKeyLookup } from '@/utils/enumKeyLookup'
+
+const requestStatusLookup = enumKeyLookup(AuthorizationRequestStatus)
+
+type ScenarioState = {
+ readonly ok: boolean
+ readonly details: readonly string[]
+ readonly payload?: unknown
+} | null
+
+function stringifyPayload(value: unknown) {
+ return JSON.stringify(
+ value,
+ (_key, currentValue) =>
+ currentValue instanceof Date ? currentValue.toISOString() : currentValue,
+ 2,
+ )
+}
+
+export default function ContractsScreen() {
+ const params = useLocalSearchParams<{
+ autorun?: string
+ scenario?: string
+ }>()
+ const [authStatus, setAuthStatus] =
+ useState(null)
+ const [results, setResults] = useState>({})
+ const [isRunning, setIsRunning] = useState(false)
+ const [output, setOutput] = useState('')
+ const [hasAutoRunStarted, setHasAutoRunStarted] = useState(false)
+ const [hasAutoRequestStarted, setHasAutoRequestStarted] = useState(false)
+ const wantsAutoRun =
+ params.autorun === 'all' || typeof params.scenario === 'string'
+
+ const refreshAuthStatus = useCallback(async () => {
+ const status = await getRequestStatusForAuthorization({
+ toRead: AllObjectTypesInApp,
+ toShare: AllSampleTypesInApp,
+ })
+ setAuthStatus(status)
+ }, [])
+
+ useEffect(() => {
+ void refreshAuthStatus()
+ }, [refreshAuthStatus])
+
+ const requestAuth = useCallback(async () => {
+ await requestAuthorization({
+ toRead: AllObjectTypesInApp,
+ toShare: AllSampleTypesInApp,
+ })
+ await refreshAuthStatus()
+ }, [refreshAuthStatus])
+
+ const runScenario = useCallback(async (scenarioId: string) => {
+ const scenario = contractScenarios.find((item) => item.id === scenarioId)
+ if (!scenario) {
+ return
+ }
+
+ setIsRunning(true)
+ try {
+ const result = await scenario.run()
+ setResults((current) => ({
+ ...current,
+ [scenario.id]: {
+ ok: result.ok,
+ details: result.details,
+ payload: result.payload,
+ },
+ }))
+ setOutput(stringifyPayload(result))
+ writeContractReport(result)
+ } finally {
+ setIsRunning(false)
+ }
+ }, [])
+
+ const runAll = useCallback(async () => {
+ setIsRunning(true)
+ try {
+ const allResults = await runAllContractScenarios()
+ setResults(
+ Object.fromEntries(
+ allResults.map((result) => [
+ result.id,
+ {
+ ok: result.ok,
+ details: result.details,
+ payload: result.payload,
+ },
+ ]),
+ ),
+ )
+ setOutput(stringifyPayload(allResults))
+ writeContractReport(allResults)
+ } finally {
+ setIsRunning(false)
+ }
+ }, [])
+
+ useEffect(() => {
+ if (wantsAutoRun) {
+ clearLaunchCommand()
+ }
+ }, [wantsAutoRun])
+
+ useEffect(() => {
+ if (!wantsAutoRun) {
+ return
+ }
+ if (authStatus !== AuthorizationRequestStatus.shouldRequest) {
+ return
+ }
+ if (hasAutoRequestStarted) {
+ return
+ }
+
+ setHasAutoRequestStarted(true)
+ void requestAuth()
+ }, [authStatus, hasAutoRequestStarted, requestAuth, wantsAutoRun])
+
+ useEffect(() => {
+ if (authStatus !== AuthorizationRequestStatus.unnecessary) {
+ return
+ }
+ if (hasAutoRunStarted) {
+ return
+ }
+
+ if (params.autorun === 'all') {
+ setHasAutoRunStarted(true)
+ void runAll()
+ return
+ }
+
+ if (typeof params.scenario === 'string') {
+ setHasAutoRunStarted(true)
+ void runScenario(params.scenario)
+ }
+ }, [
+ authStatus,
+ hasAutoRunStarted,
+ params.autorun,
+ params.scenario,
+ runAll,
+ runScenario,
+ ])
+
+ const overallStatus = useMemo(() => {
+ const values = Object.values(results)
+ if (values.length === 0) {
+ return 'Idle'
+ }
+ return values.every((value) => value?.ok) ? 'PASS' : 'FAIL'
+ }, [results])
+
+ return (
+
+ HealthKit Contracts
+
+ Runtime smoke validation for the public JS API using the example app.
+
+
+
+ Environment
+
+ Health data available: {isHealthDataAvailable() ? 'YES' : 'NO'}
+
+
+ Auth status:{' '}
+ {authStatus == null ? 'LOADING' : requestStatusLookup[authStatus]}
+
+ {
+ void requestAuth()
+ }}
+ style={styles.button}
+ testID="contracts.requestAuth"
+ >
+ Request Authorization
+
+
+
+
+ Run
+ {
+ void runAll()
+ }}
+ style={[styles.button, isRunning && styles.disabledButton]}
+ disabled={isRunning}
+ testID="contracts.runAll"
+ >
+
+ {isRunning ? 'Running…' : 'Run All'}
+
+
+ Overall: {overallStatus}
+
+
+ {contractScenarios.map((scenario) => {
+ const result = results[scenario.id]
+
+ return (
+
+ {scenario.title}
+
+ Status: {result == null ? 'Idle' : result.ok ? 'PASS' : 'FAIL'}
+
+ {result?.details?.length ? (
+ {result.details.join('\n')}
+ ) : null}
+ {
+ void runScenario(scenario.id)
+ }}
+ style={[styles.button, isRunning && styles.disabledButton]}
+ disabled={isRunning}
+ testID={`contracts.scenario.${scenario.id}.run`}
+ >
+ Run
+
+
+ )
+ })}
+
+
+ Output
+
+ {output || 'No output yet.'}
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ gap: 16,
+ padding: 16,
+ paddingBottom: 48,
+ },
+ title: {
+ fontSize: 28,
+ fontWeight: '700',
+ },
+ subtitle: {
+ color: '#666',
+ lineHeight: 20,
+ },
+ card: {
+ gap: 8,
+ padding: 16,
+ borderWidth: 1,
+ borderColor: '#ddd',
+ borderRadius: 12,
+ backgroundColor: '#fff',
+ },
+ cardTitle: {
+ fontSize: 18,
+ fontWeight: '600',
+ },
+ button: {
+ alignSelf: 'flex-start',
+ paddingHorizontal: 14,
+ paddingVertical: 10,
+ borderRadius: 999,
+ backgroundColor: '#111',
+ },
+ disabledButton: {
+ opacity: 0.5,
+ },
+ buttonLabel: {
+ color: '#fff',
+ fontWeight: '600',
+ },
+ details: {
+ color: '#444',
+ lineHeight: 20,
+ },
+ output: {
+ fontFamily: 'Menlo',
+ fontSize: 12,
+ lineHeight: 18,
+ },
+})
diff --git a/apps/example/constants/AllUsedIdentifiersInApp.ts b/apps/example/constants/AllUsedIdentifiersInApp.ts
index 52ddc1b0..26fc8c4c 100644
--- a/apps/example/constants/AllUsedIdentifiersInApp.ts
+++ b/apps/example/constants/AllUsedIdentifiersInApp.ts
@@ -71,6 +71,7 @@ export const AllQuantityTypeIdentifierInApp: QuantityTypeIdentifierWriteable[] =
export const AllCategorySampleTypeIdentifierInApp: CategoryTypeIdentifierWriteable[] =
[
+ 'HKCategoryTypeIdentifierMenstrualFlow',
'HKCategoryTypeIdentifierSleepAnalysis',
'HKCategoryTypeIdentifierSleepChanges',
'HKCategoryTypeIdentifierMindfulSession',
diff --git a/apps/example/contracts/generated/healthkit.contract.generated.ts b/apps/example/contracts/generated/healthkit.contract.generated.ts
new file mode 100644
index 00000000..b71e9bf4
--- /dev/null
+++ b/apps/example/contracts/generated/healthkit.contract.generated.ts
@@ -0,0 +1,361 @@
+/*
+ * AUTO-GENERATED FILE. DO NOT EDIT.
+ * Source: scripts/generate-healthkit.ts
+ */
+
+import type { CategoryTypeIdentifier } from '@kingstinct/react-native-healthkit/types/CategoryTypeIdentifier'
+import type { QuantityTypeIdentifier } from '@kingstinct/react-native-healthkit/types/QuantityTypeIdentifier'
+import { z } from 'zod'
+
+export type ContractMetadataValueKind =
+ | 'string'
+ | 'boolean'
+ | 'number'
+ | 'quantity'
+ | 'enum'
+
+export const KNOWN_OBJECT_METADATA_KIND_BY_KEY = {
+ HKDeviceManufacturerName: 'string',
+ HKDeviceName: 'string',
+ HKDeviceSerialNumber: 'string',
+ HKDigitalSignature: 'string',
+ HKExternalUUID: 'string',
+ HKFoodType: 'string',
+ HKReferenceRangeLowerLimit: 'number',
+ HKReferenceRangeUpperLimit: 'number',
+ HKSyncIdentifier: 'string',
+ HKSyncVersion: 'number',
+ HKTimeZone: 'string',
+ HKUDIDeviceIdentifier: 'string',
+ HKUDIProductionIdentifier: 'string',
+ HKWasTakenInLab: 'boolean',
+ HKWasUserEntered: 'boolean',
+} as const
+
+export const KNOWN_SAMPLE_METADATA_KIND_BY_KEY = {
+ HKActivityType: 'enum',
+ HKAlgorithmVersion: 'number',
+ HKAppleDeviceCalibrated: 'boolean',
+ HKAudioExposureDuration: 'quantity',
+ HKBarometricPressure: 'quantity',
+ HKDateOfEarliestDataUsedForEstimate: 'string',
+ HKMaximumLightIntensity: 'quantity',
+ HKPhysicalEffortEstimationType: 'enum',
+ HKUserMotionContext: 'enum',
+ HKWaterSalinity: 'enum',
+ HKWeatherCondition: 'enum',
+ HKWeatherHumidity: 'quantity',
+ HKWeatherTemperature: 'quantity',
+} as const
+
+export const KNOWN_WORKOUT_METADATA_KIND_BY_KEY = {
+ HKAlpineSlopeGrade: 'quantity',
+ HKAppleFitnessPlusCatalogIdentifier: 'string',
+ HKAppleFitnessPlusSession: 'boolean',
+ HKAverageMETs: 'quantity',
+ HKAverageSpeed: 'quantity',
+ HKCoachedWorkout: 'boolean',
+ HKCrossTrainerDistance: 'quantity',
+ HKElevationAscended: 'quantity',
+ HKElevationDescended: 'quantity',
+ HKFitnessMachineDuration: 'quantity',
+ HKGroupFitness: 'boolean',
+ HKIndoorBikeDistance: 'quantity',
+ HKIndoorWorkout: 'boolean',
+ HKLapLength: 'quantity',
+ HKMaximumSpeed: 'quantity',
+ HKSwimmingLocationType: 'enum',
+ HKSWOLFScore: 'number',
+ HKWeatherCondition: 'enum',
+ HKWeatherHumidity: 'quantity',
+ HKWeatherTemperature: 'quantity',
+ HKWorkoutBrandName: 'string',
+} as const
+
+export const KNOWN_WORKOUT_EVENT_METADATA_KIND_BY_KEY = {
+ HKSwimmingStrokeStyle: 'enum',
+} as const
+
+export const KNOWN_CATEGORY_METADATA_KIND_BY_IDENTIFIER = {
+ HKCategoryTypeIdentifierAudioExposureEvent: {
+ HKAudioExposureLevel: 'quantity',
+ HKHeadphoneGain: 'quantity',
+ },
+ HKCategoryTypeIdentifierEnvironmentalAudioExposureEvent: {
+ HKAudioExposureLevel: 'quantity',
+ },
+ HKCategoryTypeIdentifierHeadphoneAudioExposureEvent: {
+ HKAudioExposureLevel: 'quantity',
+ HKHeadphoneGain: 'quantity',
+ },
+ HKCategoryTypeIdentifierHighHeartRateEvent: {
+ HKHeartRateEventThreshold: 'quantity',
+ },
+ HKCategoryTypeIdentifierLowCardioFitnessEvent: {
+ HKLowCardioFitnessEventThreshold: 'quantity',
+ HKVO2MaxValue: 'quantity',
+ },
+ HKCategoryTypeIdentifierLowHeartRateEvent: {
+ HKHeartRateEventThreshold: 'quantity',
+ },
+ HKCategoryTypeIdentifierMenstrualFlow: {
+ HKMenstrualCycleStart: 'boolean',
+ },
+ HKCategoryTypeIdentifierSexualActivity: {
+ HKSexualActivityProtectionUsed: 'boolean',
+ },
+} as const
+
+export const KNOWN_QUANTITY_METADATA_KIND_BY_IDENTIFIER = {
+ HKQuantityTypeIdentifierBasalBodyTemperature: {
+ HKBodyTemperatureSensorLocation: 'enum',
+ },
+ HKQuantityTypeIdentifierBloodGlucose: {
+ HKBloodGlucoseMealTime: 'enum',
+ },
+ HKQuantityTypeIdentifierBodyTemperature: {
+ HKBodyTemperatureSensorLocation: 'enum',
+ },
+ HKQuantityTypeIdentifierInsulinDelivery: {
+ HKInsulinDeliveryReason: 'enum',
+ },
+ HKQuantityTypeIdentifierVO2Max: {
+ HKVO2MaxTestType: 'enum',
+ },
+} as const
+
+const CATEGORY_METADATA_KIND_LOOKUP =
+ KNOWN_CATEGORY_METADATA_KIND_BY_IDENTIFIER as Readonly<
+ Record>>
+ >
+const QUANTITY_METADATA_KIND_LOOKUP =
+ KNOWN_QUANTITY_METADATA_KIND_BY_IDENTIFIER as Readonly<
+ Record>>
+ >
+
+export const contractQuantitySchema = z
+ .object({
+ unit: z.string(),
+ quantity: z.number(),
+ })
+ .passthrough()
+
+export const contractSourceSchema = z
+ .object({
+ name: z.string(),
+ bundleIdentifier: z.string(),
+ })
+ .passthrough()
+
+export const contractSourceRevisionSchema = z
+ .object({
+ source: contractSourceSchema,
+ operatingSystemVersion: z.string(),
+ version: z.string().optional(),
+ productType: z.string().optional(),
+ })
+ .passthrough()
+
+export const contractDeviceSchema = z
+ .object({
+ name: z.string().optional(),
+ firmwareVersion: z.string().optional(),
+ hardwareVersion: z.string().optional(),
+ localIdentifier: z.string().optional(),
+ manufacturer: z.string().optional(),
+ model: z.string().optional(),
+ softwareVersion: z.string().optional(),
+ udiDeviceIdentifier: z.string().optional(),
+ })
+ .passthrough()
+
+export const contractSampleTypeSchema = z
+ .object({
+ identifier: z.string(),
+ allowsRecalibrationForEstimates: z.boolean(),
+ isMinimumDurationRestricted: z.boolean(),
+ isMaximumDurationRestricted: z.boolean(),
+ })
+ .passthrough()
+
+export const contractWorkoutActivitySchema = z
+ .object({
+ startDate: z.date(),
+ endDate: z.date(),
+ uuid: z.string(),
+ duration: z.number(),
+ })
+ .passthrough()
+
+function contractSchemaForMetadataKind(
+ kind: ContractMetadataValueKind,
+): z.ZodTypeAny {
+ switch (kind) {
+ case 'string':
+ return z.string()
+ case 'boolean':
+ return z.boolean()
+ case 'number':
+ case 'enum':
+ return z.number()
+ case 'quantity':
+ return contractQuantitySchema
+ }
+}
+
+function contractMetadataSchemaFromKinds(
+ kinds: Readonly>,
+) {
+ const shape: Record = {}
+
+ for (const [key, kind] of Object.entries(kinds)) {
+ shape[key] = contractSchemaForMetadataKind(kind).optional()
+ }
+
+ return z.object(shape).passthrough()
+}
+
+function mergeContractMetadataKinds(
+ ...sources: ReadonlyArray>>
+): Record {
+ return Object.assign({}, ...sources)
+}
+
+export const contractObjectMetadataSchema = contractMetadataSchemaFromKinds(
+ KNOWN_OBJECT_METADATA_KIND_BY_KEY,
+)
+export const contractSampleMetadataSchema = contractMetadataSchemaFromKinds(
+ mergeContractMetadataKinds(
+ KNOWN_OBJECT_METADATA_KIND_BY_KEY,
+ KNOWN_SAMPLE_METADATA_KIND_BY_KEY,
+ ),
+)
+export const contractWorkoutMetadataSchema = contractMetadataSchemaFromKinds(
+ mergeContractMetadataKinds(
+ KNOWN_OBJECT_METADATA_KIND_BY_KEY,
+ KNOWN_SAMPLE_METADATA_KIND_BY_KEY,
+ KNOWN_WORKOUT_METADATA_KIND_BY_KEY,
+ ),
+)
+export const contractWorkoutEventMetadataSchema =
+ contractMetadataSchemaFromKinds(KNOWN_WORKOUT_EVENT_METADATA_KIND_BY_KEY)
+
+const categoryMetadataSchemaLookup = Object.fromEntries(
+ Object.entries(CATEGORY_METADATA_KIND_LOOKUP).map(([identifier, kinds]) => [
+ identifier,
+ contractMetadataSchemaFromKinds(
+ mergeContractMetadataKinds(
+ KNOWN_OBJECT_METADATA_KIND_BY_KEY,
+ KNOWN_SAMPLE_METADATA_KIND_BY_KEY,
+ kinds,
+ ),
+ ),
+ ]),
+) as Record
+
+const quantityMetadataSchemaLookup = Object.fromEntries(
+ Object.entries(QUANTITY_METADATA_KIND_LOOKUP).map(([identifier, kinds]) => [
+ identifier,
+ contractMetadataSchemaFromKinds(
+ mergeContractMetadataKinds(
+ KNOWN_OBJECT_METADATA_KIND_BY_KEY,
+ KNOWN_SAMPLE_METADATA_KIND_BY_KEY,
+ kinds,
+ ),
+ ),
+ ]),
+) as Record
+
+export function getKnownCategoryMetadataKindMap(
+ identifier: CategoryTypeIdentifier,
+): Readonly> {
+ return CATEGORY_METADATA_KIND_LOOKUP[identifier] ?? {}
+}
+
+export function getKnownQuantityMetadataKindMap(
+ identifier: QuantityTypeIdentifier,
+): Readonly> {
+ return QUANTITY_METADATA_KIND_LOOKUP[identifier] ?? {}
+}
+
+export function getCategoryMetadataContractSchema(
+ identifier: CategoryTypeIdentifier,
+): z.ZodTypeAny {
+ return (
+ categoryMetadataSchemaLookup[identifier] ?? contractSampleMetadataSchema
+ )
+}
+
+export function getQuantityMetadataContractSchema(
+ identifier: QuantityTypeIdentifier,
+): z.ZodTypeAny {
+ return (
+ quantityMetadataSchemaLookup[identifier] ?? contractSampleMetadataSchema
+ )
+}
+
+function createBaseSampleContractSchema(metadataSchema: z.ZodTypeAny) {
+ return z
+ .object({
+ uuid: z.string(),
+ sourceRevision: contractSourceRevisionSchema,
+ device: contractDeviceSchema.optional(),
+ metadata: metadataSchema,
+ sampleType: contractSampleTypeSchema,
+ startDate: z.date(),
+ endDate: z.date(),
+ hasUndeterminedDuration: z.boolean(),
+ })
+ .passthrough()
+}
+
+export function getQuantitySampleContractSchema(
+ identifier: QuantityTypeIdentifier,
+) {
+ return createBaseSampleContractSchema(
+ getQuantityMetadataContractSchema(identifier),
+ )
+ .extend({
+ quantityType: z.literal(identifier),
+ quantity: z.number(),
+ unit: z.string(),
+ })
+ .passthrough()
+}
+
+export function getCategorySampleContractSchema(
+ identifier: CategoryTypeIdentifier,
+) {
+ return createBaseSampleContractSchema(
+ getCategoryMetadataContractSchema(identifier),
+ )
+ .extend({
+ categoryType: z.literal(identifier),
+ value: z.number(),
+ })
+ .passthrough()
+}
+
+export const contractWorkoutEventSchema = z
+ .object({
+ type: z.number(),
+ startDate: z.date(),
+ endDate: z.date(),
+ metadata: contractWorkoutEventMetadataSchema.optional(),
+ })
+ .passthrough()
+
+export const contractWorkoutSampleSchema = createBaseSampleContractSchema(
+ contractWorkoutMetadataSchema,
+)
+ .extend({
+ workoutActivityType: z.number(),
+ duration: contractQuantitySchema,
+ totalEnergyBurned: contractQuantitySchema.optional(),
+ totalDistance: contractQuantitySchema.optional(),
+ totalSwimmingStrokeCount: contractQuantitySchema.optional(),
+ totalFlightsClimbed: contractQuantitySchema.optional(),
+ events: z.array(contractWorkoutEventSchema).optional(),
+ activities: z.array(contractWorkoutActivitySchema).optional(),
+ })
+ .passthrough()
diff --git a/apps/example/contracts/index.ts b/apps/example/contracts/index.ts
new file mode 100644
index 00000000..6df73438
--- /dev/null
+++ b/apps/example/contracts/index.ts
@@ -0,0 +1,152 @@
+import type { CategorySampleTyped } from '@kingstinct/react-native-healthkit/types/CategoryType'
+import type { CategoryTypeIdentifier } from '@kingstinct/react-native-healthkit/types/CategoryTypeIdentifier'
+import type { QuantitySampleTyped } from '@kingstinct/react-native-healthkit/types/QuantitySample'
+import type { QuantityTypeIdentifier } from '@kingstinct/react-native-healthkit/types/QuantityTypeIdentifier'
+import type {
+ WorkoutEventTyped,
+ WorkoutSampleTyped,
+} from '@kingstinct/react-native-healthkit/types/Workouts'
+import type { z } from 'zod'
+import {
+ contractWorkoutEventSchema,
+ contractWorkoutSampleSchema,
+ getCategorySampleContractSchema,
+ getQuantitySampleContractSchema,
+} from './generated/healthkit.contract.generated'
+
+export {
+ contractWorkoutEventSchema,
+ contractWorkoutSampleSchema,
+ getCategorySampleContractSchema,
+ getQuantitySampleContractSchema,
+} from './generated/healthkit.contract.generated'
+
+export interface ContractValidationIssue {
+ readonly path: string
+ readonly message: string
+}
+
+export interface ContractValidationResult {
+ readonly ok: boolean
+ readonly issues: readonly ContractValidationIssue[]
+}
+
+function formatPathSegment(segment: string | number): string {
+ if (typeof segment === 'number') {
+ return `[${segment}]`
+ }
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(segment)
+ ? `.${segment}`
+ : `[${JSON.stringify(segment)}]`
+}
+
+function formatIssuePath(
+ rootPath: string,
+ path: readonly (string | number)[],
+): string {
+ if (path.length === 0) {
+ return rootPath
+ }
+
+ return `${rootPath}${path.map(formatPathSegment).join('')}`
+}
+
+function issuesFromError(
+ rootPath: string,
+ error: z.ZodError,
+): readonly ContractValidationIssue[] {
+ return error.issues.map((issue) => ({
+ path: formatIssuePath(rootPath, issue.path as Array),
+ message: issue.message,
+ }))
+}
+
+function safeParseContract(
+ rootPath: string,
+ schema: z.ZodTypeAny,
+ value: unknown,
+): ContractValidationResult {
+ const result = schema.safeParse(value)
+ if (result.success) {
+ return { ok: true, issues: [] }
+ }
+
+ return {
+ ok: false,
+ issues: issuesFromError(rootPath, result.error),
+ }
+}
+
+function formatIssues(issues: readonly ContractValidationIssue[]): string {
+ return issues.map((issue) => `${issue.path}: ${issue.message}`).join('\n')
+}
+
+export function validateQuantitySampleContract<
+ T extends QuantityTypeIdentifier,
+>(identifier: T, sample: unknown): ContractValidationResult {
+ return safeParseContract(
+ 'quantitySample',
+ getQuantitySampleContractSchema(identifier),
+ sample,
+ )
+}
+
+export function validateCategorySampleContract<
+ T extends CategoryTypeIdentifier,
+>(identifier: T, sample: unknown): ContractValidationResult {
+ return safeParseContract(
+ 'categorySample',
+ getCategorySampleContractSchema(identifier),
+ sample,
+ )
+}
+
+export function validateWorkoutEventContract(
+ event: unknown,
+): ContractValidationResult {
+ return safeParseContract('workoutEvent', contractWorkoutEventSchema, event)
+}
+
+export function validateWorkoutSampleContract(
+ sample: unknown,
+): ContractValidationResult {
+ return safeParseContract('workoutSample', contractWorkoutSampleSchema, sample)
+}
+
+export function assertQuantitySampleContract(
+ identifier: T,
+ sample: unknown,
+): asserts sample is QuantitySampleTyped {
+ const result = validateQuantitySampleContract(identifier, sample)
+ if (!result.ok) {
+ throw new Error(formatIssues(result.issues))
+ }
+}
+
+export function assertCategorySampleContract(
+ identifier: T,
+ sample: unknown,
+): asserts sample is CategorySampleTyped {
+ const result = validateCategorySampleContract(identifier, sample)
+ if (!result.ok) {
+ throw new Error(formatIssues(result.issues))
+ }
+}
+
+export function assertWorkoutSampleContract(
+ sample: unknown,
+): asserts sample is WorkoutSampleTyped {
+ const result = validateWorkoutSampleContract(sample)
+ if (!result.ok) {
+ throw new Error(formatIssues(result.issues))
+ }
+}
+
+export function assertWorkoutEventContract(
+ event: unknown,
+): asserts event is WorkoutEventTyped {
+ const result = validateWorkoutEventContract(event)
+ if (!result.ok) {
+ throw new Error(formatIssues(result.issues))
+ }
+}
diff --git a/apps/example/contracts/launchCommand.ts b/apps/example/contracts/launchCommand.ts
new file mode 100644
index 00000000..596cbfb7
--- /dev/null
+++ b/apps/example/contracts/launchCommand.ts
@@ -0,0 +1,50 @@
+import { File, Paths } from 'expo-file-system'
+
+export interface LaunchCommand {
+ readonly route: 'contracts'
+ readonly autorun?: 'all'
+ readonly scenario?: string
+}
+
+export const contractLaunchCommandFile = new File(
+ Paths.document,
+ 'healthkit-contract-command.json',
+)
+
+export function readLaunchCommand(): LaunchCommand | null {
+ if (!contractLaunchCommandFile.exists) {
+ return null
+ }
+
+ try {
+ const value = JSON.parse(
+ contractLaunchCommandFile.textSync(),
+ ) as LaunchCommand
+
+ return value
+ } catch (error) {
+ console.error('Failed to read contract launch command:', error)
+ return null
+ }
+}
+
+export function clearLaunchCommand() {
+ if (!contractLaunchCommandFile.exists) {
+ return
+ }
+
+ try {
+ contractLaunchCommandFile.delete()
+ } catch (error) {
+ console.error('Failed to delete contract launch command:', error)
+ }
+}
+
+export function readAndClearLaunchCommand(): LaunchCommand | null {
+ const command = readLaunchCommand()
+ if (!command) {
+ return null
+ }
+ clearLaunchCommand()
+ return command
+}
diff --git a/apps/example/contracts/report.ts b/apps/example/contracts/report.ts
new file mode 100644
index 00000000..95403f93
--- /dev/null
+++ b/apps/example/contracts/report.ts
@@ -0,0 +1,23 @@
+import { File, Paths } from 'expo-file-system'
+
+export const contractReportFile = new File(
+ Paths.document,
+ 'healthkit-contract-report.json',
+)
+
+export function writeContractReport(value: unknown) {
+ contractReportFile.create({
+ overwrite: true,
+ intermediates: true,
+ })
+ contractReportFile.write(
+ JSON.stringify(
+ value,
+ (_key, currentValue) =>
+ currentValue instanceof Date
+ ? currentValue.toISOString()
+ : currentValue,
+ 2,
+ ),
+ )
+}
diff --git a/apps/example/contracts/scenarios.ts b/apps/example/contracts/scenarios.ts
new file mode 100644
index 00000000..f25b3590
--- /dev/null
+++ b/apps/example/contracts/scenarios.ts
@@ -0,0 +1,294 @@
+import {
+ CategoryValueMenstrualFlow,
+ deleteObjects,
+ queryCategorySamples,
+ queryQuantitySamples,
+ queryWorkoutSamples,
+ saveCategorySample,
+ saveQuantitySample,
+ saveWorkoutSample,
+ WorkoutActivityType,
+} from '@kingstinct/react-native-healthkit'
+import { BloodGlucoseMealTime } from '@kingstinct/react-native-healthkit/types/MetadataEnums'
+import {
+ assertCategorySampleContract,
+ assertQuantitySampleContract,
+ assertWorkoutSampleContract,
+} from '@/contracts'
+
+export type ContractScenarioId =
+ | 'quantity-roundtrip'
+ | 'category-roundtrip'
+ | 'workout-roundtrip'
+
+export interface ContractScenarioResult {
+ readonly id: ContractScenarioId
+ readonly title: string
+ readonly ok: boolean
+ readonly details: readonly string[]
+ readonly payload?: unknown
+}
+
+export interface ContractScenario {
+ readonly id: ContractScenarioId
+ readonly title: string
+ readonly run: () => Promise
+}
+
+function success(
+ id: ContractScenarioId,
+ title: string,
+ payload: unknown,
+ details: string[] = [],
+): ContractScenarioResult {
+ return {
+ id,
+ title,
+ ok: true,
+ details,
+ payload,
+ }
+}
+
+function failure(
+ id: ContractScenarioId,
+ title: string,
+ error: unknown,
+): ContractScenarioResult {
+ return {
+ id,
+ title,
+ ok: false,
+ details: [error instanceof Error ? error.message : String(error)],
+ }
+}
+
+async function cleanupSample(identifier: string, uuid: string) {
+ try {
+ await deleteObjects(identifier as never, {
+ uuids: [uuid],
+ })
+ } catch (error) {
+ console.warn(`Failed to delete sample ${identifier}/${uuid}:`, error)
+ }
+}
+
+const quantityScenario: ContractScenario = {
+ id: 'quantity-roundtrip',
+ title: 'Quantity Round-trip',
+ run: async () => {
+ const start = new Date(Date.now() - 60_000)
+ const end = new Date()
+
+ try {
+ const saved = await saveQuantitySample(
+ 'HKQuantityTypeIdentifierBloodGlucose',
+ 'mg/dL',
+ 94,
+ start,
+ end,
+ {
+ HKBloodGlucoseMealTime: BloodGlucoseMealTime.preprandial,
+ HKWasUserEntered: true,
+ },
+ )
+
+ if (!saved) {
+ throw new Error('saveQuantitySample returned undefined')
+ }
+
+ assertQuantitySampleContract(
+ 'HKQuantityTypeIdentifierBloodGlucose',
+ saved,
+ )
+
+ const queried = await queryQuantitySamples(
+ 'HKQuantityTypeIdentifierBloodGlucose',
+ {
+ limit: 1,
+ unit: 'mg/dL',
+ filter: {
+ uuids: [saved.uuid],
+ },
+ },
+ )
+
+ const roundTripped = queried[0]
+ if (!roundTripped) {
+ throw new Error('queryQuantitySamples returned no matching sample')
+ }
+
+ assertQuantitySampleContract(
+ 'HKQuantityTypeIdentifierBloodGlucose',
+ roundTripped,
+ )
+
+ if (
+ roundTripped.metadata.HKBloodGlucoseMealTime !==
+ BloodGlucoseMealTime.preprandial
+ ) {
+ throw new Error('Expected HKBloodGlucoseMealTime to survive round-trip')
+ }
+
+ await cleanupSample('HKQuantityTypeIdentifierBloodGlucose', saved.uuid)
+
+ return success(
+ quantityScenario.id,
+ quantityScenario.title,
+ roundTripped,
+ ['saveQuantitySample', 'queryQuantitySamples'],
+ )
+ } catch (error) {
+ return failure(quantityScenario.id, quantityScenario.title, error)
+ }
+ },
+}
+
+const categoryScenario: ContractScenario = {
+ id: 'category-roundtrip',
+ title: 'Category Round-trip',
+ run: async () => {
+ const start = new Date(Date.now() - 60_000)
+ const end = new Date()
+
+ try {
+ const saved = await saveCategorySample(
+ 'HKCategoryTypeIdentifierMenstrualFlow',
+ CategoryValueMenstrualFlow.light,
+ start,
+ end,
+ {
+ HKMenstrualCycleStart: true,
+ HKWasUserEntered: true,
+ },
+ )
+
+ if (!saved) {
+ throw new Error('saveCategorySample returned undefined')
+ }
+
+ assertCategorySampleContract(
+ 'HKCategoryTypeIdentifierMenstrualFlow',
+ saved,
+ )
+
+ const queried = await queryCategorySamples(
+ 'HKCategoryTypeIdentifierMenstrualFlow',
+ {
+ limit: 1,
+ filter: {
+ uuids: [saved.uuid],
+ },
+ },
+ )
+
+ const roundTripped = queried[0]
+ if (!roundTripped) {
+ throw new Error('queryCategorySamples returned no matching sample')
+ }
+
+ assertCategorySampleContract(
+ 'HKCategoryTypeIdentifierMenstrualFlow',
+ roundTripped,
+ )
+
+ if (roundTripped.metadata.HKMenstrualCycleStart !== true) {
+ throw new Error('Expected HKMenstrualCycleStart to survive round-trip')
+ }
+
+ await cleanupSample('HKCategoryTypeIdentifierMenstrualFlow', saved.uuid)
+
+ return success(
+ categoryScenario.id,
+ categoryScenario.title,
+ roundTripped,
+ ['saveCategorySample', 'queryCategorySamples'],
+ )
+ } catch (error) {
+ return failure(categoryScenario.id, categoryScenario.title, error)
+ }
+ },
+}
+
+const workoutScenario: ContractScenario = {
+ id: 'workout-roundtrip',
+ title: 'Workout Round-trip',
+ run: async () => {
+ const start = new Date(Date.now() - 30 * 60_000)
+ const end = new Date()
+
+ try {
+ const saved = await saveWorkoutSample(
+ WorkoutActivityType.running,
+ [
+ {
+ quantityType: 'HKQuantityTypeIdentifierDistanceWalkingRunning',
+ quantity: 1250,
+ unit: 'm',
+ startDate: start,
+ endDate: end,
+ metadata: {},
+ },
+ ],
+ start,
+ end,
+ {
+ distance: 1250,
+ },
+ {
+ HKWorkoutBrandName: 'contract-harness',
+ HKIndoorWorkout: true,
+ },
+ )
+
+ const savedJson = saved.toJSON()
+ assertWorkoutSampleContract(savedJson)
+
+ const queried = await queryWorkoutSamples({
+ limit: 1,
+ filter: {
+ uuids: [saved.uuid],
+ },
+ })
+
+ const roundTripped = queried[0]
+ if (!roundTripped) {
+ throw new Error('queryWorkoutSamples returned no matching workout')
+ }
+
+ const roundTrippedJson = roundTripped.toJSON()
+ assertWorkoutSampleContract(roundTrippedJson)
+
+ if (roundTrippedJson.metadata.HKWorkoutBrandName !== 'contract-harness') {
+ throw new Error('Expected HKWorkoutBrandName to survive round-trip')
+ }
+
+ await cleanupSample('HKWorkoutTypeIdentifier', saved.uuid)
+
+ return success(
+ workoutScenario.id,
+ workoutScenario.title,
+ roundTrippedJson,
+ ['saveWorkoutSample', 'queryWorkoutSamples'],
+ )
+ } catch (error) {
+ return failure(workoutScenario.id, workoutScenario.title, error)
+ }
+ },
+}
+
+export const contractScenarios: readonly ContractScenario[] = [
+ quantityScenario,
+ categoryScenario,
+ workoutScenario,
+]
+
+export async function runAllContractScenarios() {
+ const results: ContractScenarioResult[] = []
+
+ for (const scenario of contractScenarios) {
+ results.push(await scenario.run())
+ }
+
+ return results
+}
diff --git a/apps/example/package.json b/apps/example/package.json
index 1832154c..36a75c31 100644
--- a/apps/example/package.json
+++ b/apps/example/package.json
@@ -4,6 +4,7 @@
"version": "1.0.0",
"scripts": {
"start": "expo start",
+ "contracts": "sh ./scripts/run-healthkit-contracts.sh",
"reset-project": "node ./scripts/reset-project.js",
"android": "expo run:android",
"ios": "expo run:ios",
@@ -45,7 +46,8 @@
"react-native-safe-area-context": "~5.6.2",
"react-native-screens": "~4.16.0",
"react-native-web": "^0.21.2",
- "react-native-webview": "13.15.0"
+ "react-native-webview": "13.15.0",
+ "zod": "^4.2.1"
},
"devDependencies": {
"@babel/core": "^7.28.5",
diff --git a/apps/example/scripts/run-healthkit-contracts.sh b/apps/example/scripts/run-healthkit-contracts.sh
new file mode 100644
index 00000000..779cb179
--- /dev/null
+++ b/apps/example/scripts/run-healthkit-contracts.sh
@@ -0,0 +1,181 @@
+#!/usr/bin/env bash
+
+set -Eeuo pipefail
+
+ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
+APP_DIR="$ROOT_DIR/apps/example"
+SIMULATOR_ID="${SIMULATOR_ID:-${1:-}}"
+APP_ID="com.kingstinct.reactnativehealthkitexample"
+INITIAL_URL="exp+react-native-healthkit-example://expo-development-client/?url=http%3A%2F%2F127.0.0.1%3A8081"
+METRO_LOG="${TMPDIR:-/tmp}/healthkit-contract-metro.log"
+APP_DATA=""
+REPORT_PATH=""
+COMMAND_PATH=""
+METRO_PID=""
+
+run_with_timeout() {
+ local seconds="$1"
+ shift
+ python3 - "$seconds" "$@" <<'PY'
+import subprocess
+import sys
+
+timeout = int(sys.argv[1])
+command = sys.argv[2:]
+
+try:
+ subprocess.run(command, check=True, timeout=timeout)
+except subprocess.TimeoutExpired:
+ print(
+ f"Timed out after {timeout}s: {' '.join(command)}",
+ file=sys.stderr,
+ )
+ sys.exit(124)
+except subprocess.CalledProcessError as error:
+ sys.exit(error.returncode)
+PY
+}
+
+dump_debug_artifacts() {
+ local reason="$1"
+
+ echo "$reason" >&2
+
+ if [ -n "$SIMULATOR_ID" ]; then
+ local screenshot_path
+ screenshot_path="$(mktemp -t healthkit-contracts-XXXXXX).png"
+ if xcrun simctl io "$SIMULATOR_ID" screenshot "$screenshot_path" >/dev/null 2>&1; then
+ echo "Simulator screenshot: $screenshot_path" >&2
+ fi
+ fi
+
+ if [ -n "$APP_DATA" ]; then
+ echo "App data container: $APP_DATA" >&2
+ fi
+
+ if [ -n "$REPORT_PATH" ] && [ -f "$REPORT_PATH" ]; then
+ echo "Partial contract report:" >&2
+ cat "$REPORT_PATH" >&2
+ fi
+
+ if [ -f "$METRO_LOG" ]; then
+ echo "Metro log tail:" >&2
+ tail -n 200 "$METRO_LOG" >&2
+ fi
+}
+
+cleanup() {
+ if [ -n "$METRO_PID" ] && kill -0 "$METRO_PID" 2>/dev/null; then
+ kill "$METRO_PID" 2>/dev/null || true
+ fi
+}
+
+trap cleanup EXIT INT TERM
+trap 'dump_debug_artifacts "Contract runner failed at line $LINENO"' ERR
+
+find_booted_simulator() {
+ xcrun simctl list devices |
+ sed -n 's/.*(\([0-9A-F-][0-9A-F-]*\)) (Booted).*/\1/p' |
+ head -n 1
+}
+
+find_available_simulator() {
+ xcrun simctl list devices available |
+ sed -n 's/^[[:space:]]*iPhone[^()]* (\([0-9A-F-][0-9A-F-]*\)) (.*/\1/p' |
+ head -n 1
+}
+
+if [ -z "$SIMULATOR_ID" ]; then
+ SIMULATOR_ID="$(find_booted_simulator)"
+fi
+
+if [ -z "$SIMULATOR_ID" ]; then
+ SIMULATOR_ID="$(find_available_simulator)"
+fi
+
+if [ -z "$SIMULATOR_ID" ]; then
+ echo "No usable simulator found. Set SIMULATOR_ID or install an iPhone simulator runtime." >&2
+ exit 1
+fi
+
+if ! xcrun simctl list devices | grep -q "$SIMULATOR_ID.*Booted"; then
+ run_with_timeout 30 xcrun simctl boot "$SIMULATOR_ID"
+ run_with_timeout 120 xcrun simctl bootstatus "$SIMULATOR_ID" -b
+fi
+
+if ! command -v applesimutils >/dev/null 2>&1; then
+ echo "applesimutils is required for HealthKit contract runs." >&2
+ exit 1
+fi
+
+APP_BUNDLE="${APP_BUNDLE:-}"
+if [ -z "$APP_BUNDLE" ]; then
+ APP_BUNDLE="$(find "$HOME/Library/Developer/Xcode/DerivedData" -path '*Debug-iphonesimulator/RNHealthKit.app' | sort | tail -n 1)"
+fi
+
+if [ ! -d "$APP_BUNDLE" ]; then
+ echo "Could not find built RNHealthKit.app. Set APP_BUNDLE or build the example app first." >&2
+ exit 1
+fi
+
+rm -f "$METRO_LOG"
+
+if ! curl -fsS --max-time 2 "http://127.0.0.1:8081/status" >/dev/null 2>&1; then
+ (
+ cd "$APP_DIR"
+ bun start --clear --non-interactive >"$METRO_LOG" 2>&1
+ ) &
+ METRO_PID="$!"
+
+ ATTEMPT=0
+ until curl -fsS --max-time 2 "http://127.0.0.1:8081/status" >/dev/null 2>&1; do
+ ATTEMPT=$((ATTEMPT + 1))
+ if [ "$ATTEMPT" -ge 60 ]; then
+ dump_debug_artifacts "Metro did not start in time."
+ exit 1
+ fi
+ sleep 1
+ done
+fi
+
+run_with_timeout 20 xcrun simctl terminate "$SIMULATOR_ID" "$APP_ID" >/dev/null 2>&1 || true
+run_with_timeout 20 xcrun simctl uninstall "$SIMULATOR_ID" "$APP_ID" >/dev/null 2>&1 || true
+run_with_timeout 60 xcrun simctl install "$SIMULATOR_ID" "$APP_BUNDLE"
+
+APP_DATA="$(xcrun simctl get_app_container "$SIMULATOR_ID" "$APP_ID" data)"
+mkdir -p "$APP_DATA/Documents"
+REPORT_PATH="$APP_DATA/Documents/healthkit-contract-report.json"
+COMMAND_PATH="$APP_DATA/Documents/healthkit-contract-command.json"
+rm -f "$REPORT_PATH"
+printf '%s\n' '{"route":"contracts","autorun":"all"}' >"$COMMAND_PATH"
+
+run_with_timeout 20 applesimutils \
+ --byId "$SIMULATOR_ID" \
+ --bundle "$APP_ID" \
+ --setPermissions 'health=YES,motion=YES'
+
+run_with_timeout 30 xcrun simctl launch "$SIMULATOR_ID" "$APP_ID" --initialUrl "$INITIAL_URL" >/dev/null
+
+ATTEMPT=0
+until [ -f "$REPORT_PATH" ]; do
+ ATTEMPT=$((ATTEMPT + 1))
+ if [ "$ATTEMPT" -ge 90 ]; then
+ dump_debug_artifacts "Contract report was not produced."
+ exit 1
+ fi
+ sleep 1
+done
+
+cat "$REPORT_PATH"
+
+REPORT_PATH="$REPORT_PATH" bun -e '
+ import { readFileSync } from "node:fs"
+
+ const report = JSON.parse(readFileSync(process.env.REPORT_PATH, "utf8"))
+ const results = Array.isArray(report) ? report : [report]
+ const failures = results.filter((entry) => entry?.ok !== true)
+ if (failures.length > 0) {
+ console.error(`Contract failures: ${failures.map((entry) => entry.id).join(", ")}`)
+ process.exit(1)
+ }
+'
diff --git a/biome.json b/biome.json
index fe5e02d7..5c3e6ef3 100644
--- a/biome.json
+++ b/biome.json
@@ -55,9 +55,12 @@
"files": {
"includes": [
"**/*",
+ "!**/.build",
+ "!**/.swiftpm",
"!**/docs",
"!**/lib",
"!**/.expo",
+ "!apps/example/expo-env.d.ts",
"!**/ios",
"!.vscode/*",
"!.claude/*"
diff --git a/bun.lock b/bun.lock
index 11afc947..9fd3479b 100644
--- a/bun.lock
+++ b/bun.lock
@@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
+ "configVersion": 0,
"workspaces": {
"": {
"name": "react-native-healthkit-mono",
@@ -55,6 +56,7 @@
"react-native-screens": "~4.16.0",
"react-native-web": "^0.21.2",
"react-native-webview": "13.15.0",
+ "zod": "^4.2.1",
},
"devDependencies": {
"@babel/core": "^7.28.5",
@@ -65,7 +67,7 @@
},
"packages/react-native-healthkit": {
"name": "@kingstinct/react-native-healthkit",
- "version": "13.2.3",
+ "version": "13.4.0",
"devDependencies": {
"@testing-library/react-native": "^13.3.3",
"@types/react": "~19.1.17",
diff --git a/package.json b/package.json
index 7729d52e..97f54f39 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"codegen": "cd packages/react-native-healthkit && rm -rf nitrogen/generated && bun run nitrogen && cd ../../ && bun run fix-codegen && cd apps/example/ios && pod install",
"fix-codegen": "perl -i -pe 's/Bool\\(fromCxx: cachedCxxPart\\)/cachedCxxPart.use_count() > 0/g' packages/react-native-healthkit/nitrogen/generated/ios/swift/*Spec_cxx.swift",
"test": "cd packages/react-native-healthkit && bun run test",
+ "test:contracts": "cd apps/example && bun run contracts",
"prepare": "husky",
"reinstall": "rm -rf node_modules/ && find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + && bun install",
"reinstall-with-nuked-lockfile": "rm -rf node_modules/ && rm bun.lock && find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + && bun install"
diff --git a/packages/react-native-healthkit/ios/CategoryTypeModule.swift b/packages/react-native-healthkit/ios/CategoryTypeModule.swift
index dcb31e26..b3e2f8c4 100644
--- a/packages/react-native-healthkit/ios/CategoryTypeModule.swift
+++ b/packages/react-native-healthkit/ios/CategoryTypeModule.swift
@@ -3,7 +3,7 @@ import NitroModules
class CategoryTypeModule: HybridCategoryTypeModuleSpec {
func saveCategorySample(
- identifier: CategoryTypeIdentifier,
+ identifier: CategoryTypeIdentifierWriteable,
value: Double,
startDate: Date,
endDate: Date,
diff --git a/packages/react-native-healthkit/ios/CorrelationTypeModule.swift b/packages/react-native-healthkit/ios/CorrelationTypeModule.swift
index 323ec927..cf088707 100644
--- a/packages/react-native-healthkit/ios/CorrelationTypeModule.swift
+++ b/packages/react-native-healthkit/ios/CorrelationTypeModule.swift
@@ -24,47 +24,14 @@ func serializeCorrelationSample(correlation: HKCorrelation, unitMap: [HKQuantity
return CorrelationSample(
correlationType: CorrelationTypeIdentifier(fromString: correlation.correlationType.identifier)!,
objects: objects,
- metadataFoodType: correlation.metadata?[HKMetadataKeyFoodType] as? String,
sampleType: serializeSampleType(correlation.sampleType),
startDate: correlation.startDate,
endDate: correlation.endDate,
hasUndeterminedDuration: correlation.hasUndeterminedDuration,
-
- metadataWeatherCondition: serializeWeatherCondition(
- correlation.metadata?[HKMetadataKeyWeatherCondition] as? HKWeatherCondition),
- metadataWeatherHumidity: serializeUnknownQuantityTyped(
- quantity: correlation.metadata?[HKMetadataKeyWeatherHumidity] as? HKQuantity),
- metadataWeatherTemperature: serializeUnknownQuantityTyped(
- quantity: correlation.metadata?[HKMetadataKeyWeatherTemperature] as? HKQuantity),
- metadataInsulinDeliveryReason: serializeInsulinDeliveryReason(
- correlation.metadata?[HKMetadataKeyInsulinDeliveryReason] as? HKInsulinDeliveryReason),
- metadataHeartRateMotionContext: serializeHeartRateMotionContext(
- correlation.metadata?[HKMetadataKeyHeartRateMotionContext] as? HKHeartRateMotionContext),
-
+ metadata: serializeMetadata(correlation.metadata),
uuid: correlation.uuid.uuidString,
sourceRevision: serializeSourceRevision(correlation.sourceRevision),
- device: serializeDevice(hkDevice: correlation.device),
- metadata: serializeMetadata(correlation.metadata),
-
- metadataExternalUUID: correlation.metadata?[HKMetadataKeyExternalUUID] as? String,
- metadataTimeZone: correlation.metadata?[HKMetadataKeyTimeZone] as? String,
- metadataWasUserEntered: correlation.metadata?[HKMetadataKeyWasUserEntered] as? Bool,
- metadataDeviceSerialNumber: correlation.metadata?[HKMetadataKeyDeviceSerialNumber] as? String,
- metadataUdiDeviceIdentifier: correlation.metadata?[HKMetadataKeyUDIDeviceIdentifier] as? String,
- metadataUdiProductionIdentifier: correlation.metadata?[HKMetadataKeyUDIProductionIdentifier]
- as? String,
- metadataDigitalSignature: correlation.metadata?[HKMetadataKeyDigitalSignature] as? String,
- metadataDeviceName: correlation.metadata?[HKMetadataKeyDeviceName] as? String,
- metadataDeviceManufacturerName: correlation.metadata?[HKMetadataKeyDeviceManufacturerName]
- as? String,
- metadataSyncIdentifier: correlation.metadata?[HKMetadataKeySyncIdentifier] as? String,
- metadataSyncVersion: correlation.metadata?[HKMetadataKeySyncVersion] as? Double,
- metadataWasTakenInLab: correlation.metadata?[HKMetadataKeyWasTakenInLab] as? Bool,
- metadataReferenceRangeLowerLimit: correlation.metadata?[HKMetadataKeyReferenceRangeLowerLimit]
- as? Double,
- metadataReferenceRangeUpperLimit: correlation.metadata?[HKMetadataKeyReferenceRangeUpperLimit]
- as? Double,
- metadataAlgorithmVersion: correlation.metadata?[HKMetadataKeyAlgorithmVersion] as? Double
+ device: serializeDevice(hkDevice: correlation.device)
)
}
diff --git a/packages/react-native-healthkit/ios/ElectrocardiogramModule.swift b/packages/react-native-healthkit/ios/ElectrocardiogramModule.swift
index bb131c23..6c9d4b79 100644
--- a/packages/react-native-healthkit/ios/ElectrocardiogramModule.swift
+++ b/packages/react-native-healthkit/ios/ElectrocardiogramModule.swift
@@ -38,42 +38,10 @@ private func serializeECGSample(sample: HKElectrocardiogram, includeVoltages: Bo
startDate: sample.startDate,
endDate: sample.endDate,
hasUndeterminedDuration: sample.hasUndeterminedDuration,
-
- metadataWeatherCondition: serializeWeatherCondition(
- sample.metadata?[HKMetadataKeyWeatherCondition] as? HKWeatherCondition),
- metadataWeatherHumidity: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherHumidity] as? HKQuantity),
- metadataWeatherTemperature: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherTemperature] as? HKQuantity),
- metadataInsulinDeliveryReason: serializeInsulinDeliveryReason(
- sample.metadata?[HKMetadataKeyInsulinDeliveryReason] as? HKInsulinDeliveryReason),
- metadataHeartRateMotionContext: serializeHeartRateMotionContext(
- sample.metadata?[HKMetadataKeyHeartRateMotionContext] as? HKHeartRateMotionContext),
-
+ metadata: serializeMetadata(sample.metadata),
uuid: sample.uuid.uuidString,
sourceRevision: serializeSourceRevision(sample.sourceRevision),
- device: serializeDevice(hkDevice: sample.device),
- metadata: serializeMetadata(sample.metadata),
-
- metadataExternalUUID: sample.metadata?[HKMetadataKeyExternalUUID] as? String,
- metadataTimeZone: sample.metadata?[HKMetadataKeyTimeZone] as? String,
- metadataWasUserEntered: sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool,
- metadataDeviceSerialNumber: sample.metadata?[HKMetadataKeyDeviceSerialNumber] as? String,
- metadataUdiDeviceIdentifier: sample.metadata?[HKMetadataKeyUDIDeviceIdentifier] as? String,
- metadataUdiProductionIdentifier: sample.metadata?[HKMetadataKeyUDIProductionIdentifier]
- as? String,
- metadataDigitalSignature: sample.metadata?[HKMetadataKeyDigitalSignature] as? String,
- metadataDeviceName: sample.metadata?[HKMetadataKeyDeviceName] as? String,
- metadataDeviceManufacturerName: sample.metadata?[HKMetadataKeyDeviceManufacturerName]
- as? String,
- metadataSyncIdentifier: sample.metadata?[HKMetadataKeySyncIdentifier] as? String,
- metadataSyncVersion: sample.metadata?[HKMetadataKeySyncVersion] as? Double,
- metadataWasTakenInLab: sample.metadata?[HKMetadataKeyWasTakenInLab] as? Bool,
- metadataReferenceRangeLowerLimit: sample.metadata?[HKMetadataKeyReferenceRangeLowerLimit]
- as? Double,
- metadataReferenceRangeUpperLimit: sample.metadata?[HKMetadataKeyReferenceRangeUpperLimit]
- as? Double,
- metadataAlgorithmVersion: sample.metadata?[HKMetadataKeyAlgorithmVersion] as? Double
+ device: serializeDevice(hkDevice: sample.device)
)
}
diff --git a/packages/react-native-healthkit/ios/HeartbeatSeriesModule.swift b/packages/react-native-healthkit/ios/HeartbeatSeriesModule.swift
index e89d9c47..b47d3cd0 100644
--- a/packages/react-native-healthkit/ios/HeartbeatSeriesModule.swift
+++ b/packages/react-native-healthkit/ios/HeartbeatSeriesModule.swift
@@ -11,42 +11,10 @@ func serializeHeartbeatSeriesSample(sample: HKHeartbeatSeriesSample) async throw
startDate: sample.startDate,
endDate: sample.endDate,
hasUndeterminedDuration: sample.hasUndeterminedDuration,
-
- metadataWeatherCondition: serializeWeatherCondition(
- sample.metadata?[HKMetadataKeyWeatherCondition] as? HKWeatherCondition),
- metadataWeatherHumidity: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherHumidity] as? HKQuantity),
- metadataWeatherTemperature: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherTemperature] as? HKQuantity),
- metadataInsulinDeliveryReason: serializeInsulinDeliveryReason(
- sample.metadata?[HKMetadataKeyInsulinDeliveryReason] as? HKInsulinDeliveryReason),
- metadataHeartRateMotionContext: serializeHeartRateMotionContext(
- sample.metadata?[HKMetadataKeyHeartRateMotionContext] as? HKHeartRateMotionContext),
-
+ metadata: serializeMetadata(sample.metadata),
uuid: sample.uuid.uuidString,
sourceRevision: serializeSourceRevision(sample.sourceRevision),
- device: serializeDevice(hkDevice: sample.device),
- metadata: serializeMetadata(sample.metadata),
-
- metadataExternalUUID: sample.metadata?[HKMetadataKeyExternalUUID] as? String,
- metadataTimeZone: sample.metadata?[HKMetadataKeyTimeZone] as? String,
- metadataWasUserEntered: sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool,
- metadataDeviceSerialNumber: sample.metadata?[HKMetadataKeyDeviceSerialNumber] as? String,
- metadataUdiDeviceIdentifier: sample.metadata?[HKMetadataKeyUDIDeviceIdentifier] as? String,
- metadataUdiProductionIdentifier: sample.metadata?[HKMetadataKeyUDIProductionIdentifier]
- as? String,
- metadataDigitalSignature: sample.metadata?[HKMetadataKeyDigitalSignature] as? String,
- metadataDeviceName: sample.metadata?[HKMetadataKeyDeviceName] as? String,
- metadataDeviceManufacturerName: sample.metadata?[HKMetadataKeyDeviceManufacturerName]
- as? String,
- metadataSyncIdentifier: sample.metadata?[HKMetadataKeySyncIdentifier] as? String,
- metadataSyncVersion: sample.metadata?[HKMetadataKeySyncVersion] as? Double,
- metadataWasTakenInLab: sample.metadata?[HKMetadataKeyWasTakenInLab] as? Bool,
- metadataReferenceRangeLowerLimit: sample.metadata?[HKMetadataKeyReferenceRangeLowerLimit]
- as? Double,
- metadataReferenceRangeUpperLimit: sample.metadata?[HKMetadataKeyReferenceRangeUpperLimit]
- as? Double,
- metadataAlgorithmVersion: sample.metadata?[HKMetadataKeyAlgorithmVersion] as? Double
+ device: serializeDevice(hkDevice: sample.device)
)
}
diff --git a/packages/react-native-healthkit/ios/MedicationModule.swift b/packages/react-native-healthkit/ios/MedicationModule.swift
index 39369c65..834aa60c 100644
--- a/packages/react-native-healthkit/ios/MedicationModule.swift
+++ b/packages/react-native-healthkit/ios/MedicationModule.swift
@@ -97,21 +97,6 @@ import NitroModules
startDate: sample.startDate,
endDate: sample.endDate,
hasUndeterminedDuration: sample.hasUndeterminedDuration,
-
- metadataWeatherCondition: serializeWeatherCondition(
- sample.metadata?[HKMetadataKeyWeatherCondition] as? HKWeatherCondition),
- metadataWeatherHumidity: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherHumidity] as? HKQuantity),
- metadataWeatherTemperature: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherTemperature] as? HKQuantity),
- metadataInsulinDeliveryReason: serializeInsulinDeliveryReason(
- sample.metadata?[HKMetadataKeyInsulinDeliveryReason] as? HKInsulinDeliveryReason),
- metadataHeartRateMotionContext: serializeHeartRateMotionContext(
- sample.metadata?[HKMetadataKeyHeartRateMotionContext] as? HKHeartRateMotionContext),
-
- uuid: sample.uuid.uuidString,
- sourceRevision: serializeSourceRevision(sample.sourceRevision),
- device: serializeDevice(hkDevice: sample.device),
metadata: {
var meta = serializeMetadata(sample.metadata)
if let nick = info?.nickname {
@@ -119,26 +104,9 @@ import NitroModules
}
return meta
}(),
-
- metadataExternalUUID: sample.metadata?[HKMetadataKeyExternalUUID] as? String,
- metadataTimeZone: sample.metadata?[HKMetadataKeyTimeZone] as? String,
- metadataWasUserEntered: sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool,
- metadataDeviceSerialNumber: sample.metadata?[HKMetadataKeyDeviceSerialNumber] as? String,
- metadataUdiDeviceIdentifier: sample.metadata?[HKMetadataKeyUDIDeviceIdentifier] as? String,
- metadataUdiProductionIdentifier: sample.metadata?[HKMetadataKeyUDIProductionIdentifier]
- as? String,
- metadataDigitalSignature: sample.metadata?[HKMetadataKeyDigitalSignature] as? String,
- metadataDeviceName: sample.metadata?[HKMetadataKeyDeviceName] as? String,
- metadataDeviceManufacturerName: sample.metadata?[HKMetadataKeyDeviceManufacturerName]
- as? String,
- metadataSyncIdentifier: sample.metadata?[HKMetadataKeySyncIdentifier] as? String,
- metadataSyncVersion: sample.metadata?[HKMetadataKeySyncVersion] as? Double,
- metadataWasTakenInLab: sample.metadata?[HKMetadataKeyWasTakenInLab] as? Bool,
- metadataReferenceRangeLowerLimit: sample.metadata?[HKMetadataKeyReferenceRangeLowerLimit]
- as? Double,
- metadataReferenceRangeUpperLimit: sample.metadata?[HKMetadataKeyReferenceRangeUpperLimit]
- as? Double,
- metadataAlgorithmVersion: sample.metadata?[HKMetadataKeyAlgorithmVersion] as? Double
+ uuid: sample.uuid.uuidString,
+ sourceRevision: serializeSourceRevision(sample.sourceRevision),
+ device: serializeDevice(hkDevice: sample.device)
)
}
diff --git a/packages/react-native-healthkit/ios/PredicateHelpers.swift b/packages/react-native-healthkit/ios/PredicateHelpers.swift
index c2f3daa1..f3c3d65b 100644
--- a/packages/react-native-healthkit/ios/PredicateHelpers.swift
+++ b/packages/react-native-healthkit/ios/PredicateHelpers.swift
@@ -71,10 +71,10 @@ func getPredicateForWorkoutBase(_ filter: FilterForWorkouts?) -> FilterForWorkou
return FilterForWorkoutsBase(
workoutActivityType: filter.workoutActivityType,
duration: filter.duration,
- sources: filter.sources,
+ metadata: filter.metadata,
uuid: filter.uuid,
+ sources: filter.sources,
uuids: filter.uuids,
- metadata: filter.metadata,
date: filter.date,
)
}
diff --git a/packages/react-native-healthkit/ios/QuantityTypeModule.swift b/packages/react-native-healthkit/ios/QuantityTypeModule.swift
index cdbac0bb..9a3cc9bd 100644
--- a/packages/react-native-healthkit/ios/QuantityTypeModule.swift
+++ b/packages/react-native-healthkit/ios/QuantityTypeModule.swift
@@ -19,7 +19,7 @@ func emptyStatisticsResponse(from: Date?, to: Date?) -> QueryStatisticsResponse
func queryStatisticsForQuantityInternal(
quantityType: HKQuantityType,
statistics: [StatisticsOptions],
- options: StatisticsQueryOptions?
+ options: StatisticsQueryOptionsWithStringUnit?
) async throws -> HKStatistics? {
let predicate = createPredicateForSamples(options?.filter)
@@ -128,7 +128,7 @@ func queryStatisticsCollectionForQuantityInternal(
statistics: [StatisticsOptions],
anchorDate: Date,
intervalComponents: IntervalComponents,
- options: StatisticsQueryOptions?
+ options: StatisticsQueryOptionsWithStringUnit?
) async throws -> HKStatisticsCollection? {
let predicate = createPredicateForSamples(options?.filter)
@@ -245,15 +245,15 @@ func serializeStatisticsPerSource(gottenStats: HKStatistics, unit: HKUnit)
return QueryStatisticsResponseFromSingleSource(
source: serializeSource(source),
+ startDate: gottenStats.startDate,
+ endDate: gottenStats.endDate,
duration: duration,
averageQuantity: averageQuantity,
maximumQuantity: maximumQuantity,
minimumQuantity: minimumQuantity,
sumQuantity: sumQuantity,
mostRecentQuantity: mostRecentQuantity,
- mostRecentQuantityDateInterval: mostRecentQuantityDateInterval,
- startDate: gottenStats.startDate,
- endDate: gottenStats.endDate
+ mostRecentQuantityDateInterval: mostRecentQuantityDateInterval
)
}
}
@@ -308,7 +308,7 @@ func handleHKNoDataOrThrow(
class QuantityTypeModule: HybridQuantityTypeModuleSpec {
func queryStatisticsForQuantitySeparateBySource(
identifier: QuantityTypeIdentifier, statistics: [StatisticsOptions],
- options: StatisticsQueryOptions?
+ options: StatisticsQueryOptionsWithStringUnit?
) -> Promise<[QueryStatisticsResponseFromSingleSource]> {
return Promise.async {
let quantityType = try initializeQuantityType(identifier.stringValue)
@@ -331,7 +331,7 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
func queryStatisticsCollectionForQuantitySeparateBySource(
identifier: QuantityTypeIdentifier, statistics: [StatisticsOptions], anchorDate: Date,
- intervalComponents: IntervalComponents, options: StatisticsQueryOptions?
+ intervalComponents: IntervalComponents, options: StatisticsQueryOptionsWithStringUnit?
) -> Promise<[QueryStatisticsResponseFromSingleSource]> {
return Promise.async {
let quantityType = try initializeQuantityType(identifier.stringValue)
@@ -380,7 +380,7 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
func queryStatisticsForQuantity(
identifier: QuantityTypeIdentifier,
statistics: [StatisticsOptions],
- options: StatisticsQueryOptions?
+ options: StatisticsQueryOptionsWithStringUnit?
) -> Promise {
return Promise.async {
let quantityType = try initializeQuantityType(identifier.stringValue)
@@ -407,7 +407,7 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
func queryStatisticsCollectionForQuantity(
identifier: QuantityTypeIdentifier, statistics: [StatisticsOptions], anchorDate: Date,
- intervalComponents: IntervalComponents, options: StatisticsQueryOptions?
+ intervalComponents: IntervalComponents, options: StatisticsQueryOptionsWithStringUnit?
) -> Promise<[QueryStatisticsResponse]> {
return Promise.async {
let quantityType = try initializeQuantityType(identifier.stringValue)
@@ -434,7 +434,7 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
}
func queryQuantitySamplesWithAnchor(
- identifier: QuantityTypeIdentifier, options: QueryOptionsWithAnchorAndUnit
+ identifier: QuantityTypeIdentifier, options: QueryOptionsWithAnchorAndStringUnit
) -> Promise {
return Promise.async {
let quantityType = try initializeQuantityType(identifier.stringValue)
@@ -505,7 +505,7 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
}
func queryQuantitySamples(
- identifier: QuantityTypeIdentifier, options: QueryOptionsWithSortOrderAndUnit
+ identifier: QuantityTypeIdentifier, options: QueryOptionsWithSortOrderAndStringUnit
) -> Promise<[QuantitySample]> {
return Promise.async {
let quantityType = try initializeQuantityType(identifier.stringValue)
diff --git a/packages/react-native-healthkit/ios/Serializers.swift b/packages/react-native-healthkit/ios/Serializers.swift
index 554c6b96..438bce1a 100644
--- a/packages/react-native-healthkit/ios/Serializers.swift
+++ b/packages/react-native-healthkit/ios/Serializers.swift
@@ -9,6 +9,12 @@ import Foundation
import HealthKit
import NitroModules
+private let metadataDateFormatter: ISO8601DateFormatter = {
+ let formatter = ISO8601DateFormatter()
+ formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
+ return formatter
+}()
+
func serializeQuantityTyped(unit: HKUnit, quantityNullable: HKQuantity?) -> Quantity? {
guard let q = quantityNullable else {
return nil
@@ -20,28 +26,6 @@ func serializeQuantityTyped(unit: HKUnit, quantityNullable: HKQuantity?) -> Quan
)
}
-func serializeWeatherCondition(_ hkWeatherCondition: HKWeatherCondition?) -> WeatherCondition? {
- guard let hkWeatherCondition = hkWeatherCondition else {
- return nil
- }
- return WeatherCondition(rawValue: Int32(hkWeatherCondition.rawValue))
-}
-
-func serializeInsulinDeliveryReason(_ hkReason: HKInsulinDeliveryReason?) -> InsulinDeliveryReason? {
- guard let hkReason = hkReason else {
- return nil
- }
- return InsulinDeliveryReason(rawValue: Int32(hkReason.rawValue))
-}
-
-func serializeHeartRateMotionContext(_ hkContext: HKHeartRateMotionContext?)
- -> HeartRateMotionContext? {
- guard let hkContext = hkContext else {
- return nil
- }
- return HeartRateMotionContext(rawValue: Int32(hkContext.rawValue))
-}
-
func serializeQuantityTyped(unit: HKUnit, quantity: HKQuantity) -> Quantity {
return Quantity(
unit: unit.unitString,
@@ -59,42 +43,10 @@ func serializeQuantitySample(sample: HKQuantitySample, unit: HKUnit) throws -> Q
startDate: sample.startDate,
endDate: sample.endDate,
hasUndeterminedDuration: sample.hasUndeterminedDuration,
-
- metadataWeatherCondition: serializeWeatherCondition(
- sample.metadata?[HKMetadataKeyWeatherCondition] as? HKWeatherCondition),
- metadataWeatherHumidity: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherHumidity] as? HKQuantity),
- metadataWeatherTemperature: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherTemperature] as? HKQuantity),
- metadataInsulinDeliveryReason: serializeInsulinDeliveryReason(
- sample.metadata?[HKMetadataKeyInsulinDeliveryReason] as? HKInsulinDeliveryReason),
- metadataHeartRateMotionContext: serializeHeartRateMotionContext(
- sample.metadata?[HKMetadataKeyHeartRateMotionContext] as? HKHeartRateMotionContext),
-
+ metadata: serializeMetadata(sample.metadata),
uuid: sample.uuid.uuidString,
sourceRevision: serializeSourceRevision(sample.sourceRevision),
- device: serializeDevice(hkDevice: sample.device),
- metadata: serializeMetadata(sample.metadata),
-
- metadataExternalUUID: sample.metadata?[HKMetadataKeyExternalUUID] as? String,
- metadataTimeZone: sample.metadata?[HKMetadataKeyTimeZone] as? String,
- metadataWasUserEntered: sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool,
- metadataDeviceSerialNumber: sample.metadata?[HKMetadataKeyDeviceSerialNumber] as? String,
- metadataUdiDeviceIdentifier: sample.metadata?[HKMetadataKeyUDIDeviceIdentifier] as? String,
- metadataUdiProductionIdentifier: sample.metadata?[HKMetadataKeyUDIProductionIdentifier]
- as? String,
- metadataDigitalSignature: sample.metadata?[HKMetadataKeyDigitalSignature] as? String,
- metadataDeviceName: sample.metadata?[HKMetadataKeyDeviceName] as? String,
- metadataDeviceManufacturerName: sample.metadata?[HKMetadataKeyDeviceManufacturerName]
- as? String,
- metadataSyncIdentifier: sample.metadata?[HKMetadataKeySyncIdentifier] as? String,
- metadataSyncVersion: sample.metadata?[HKMetadataKeySyncVersion] as? Double,
- metadataWasTakenInLab: sample.metadata?[HKMetadataKeyWasTakenInLab] as? Bool,
- metadataReferenceRangeLowerLimit: sample.metadata?[HKMetadataKeyReferenceRangeLowerLimit]
- as? Double,
- metadataReferenceRangeUpperLimit: sample.metadata?[HKMetadataKeyReferenceRangeUpperLimit]
- as? Double,
- metadataAlgorithmVersion: sample.metadata?[HKMetadataKeyAlgorithmVersion] as? Double
+ device: serializeDevice(hkDevice: sample.device)
)
}
throw runtimeErrorWithPrefix(
@@ -116,42 +68,10 @@ func serializeCategorySample(sample: HKCategorySample) -> CategorySample {
startDate: sample.startDate,
endDate: sample.endDate,
hasUndeterminedDuration: sample.hasUndeterminedDuration,
-
- metadataWeatherCondition: serializeWeatherCondition(
- sample.metadata?[HKMetadataKeyWeatherCondition] as? HKWeatherCondition),
- metadataWeatherHumidity: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherHumidity] as? HKQuantity),
- metadataWeatherTemperature: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherTemperature] as? HKQuantity),
- metadataInsulinDeliveryReason: serializeInsulinDeliveryReason(
- sample.metadata?[HKMetadataKeyInsulinDeliveryReason] as? HKInsulinDeliveryReason),
- metadataHeartRateMotionContext: serializeHeartRateMotionContext(
- sample.metadata?[HKMetadataKeyHeartRateMotionContext] as? HKHeartRateMotionContext),
-
+ metadata: serializeMetadata(sample.metadata),
uuid: sample.uuid.uuidString,
sourceRevision: serializeSourceRevision(sample.sourceRevision),
- device: serializeDevice(hkDevice: sample.device),
- metadata: serializeMetadata(sample.metadata),
-
- metadataExternalUUID: sample.metadata?[HKMetadataKeyExternalUUID] as? String,
- metadataTimeZone: sample.metadata?[HKMetadataKeyTimeZone] as? String,
- metadataWasUserEntered: sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool,
- metadataDeviceSerialNumber: sample.metadata?[HKMetadataKeyDeviceSerialNumber] as? String,
- metadataUdiDeviceIdentifier: sample.metadata?[HKMetadataKeyUDIDeviceIdentifier] as? String,
- metadataUdiProductionIdentifier: sample.metadata?[HKMetadataKeyUDIProductionIdentifier]
- as? String,
- metadataDigitalSignature: sample.metadata?[HKMetadataKeyDigitalSignature] as? String,
- metadataDeviceName: sample.metadata?[HKMetadataKeyDeviceName] as? String,
- metadataDeviceManufacturerName: sample.metadata?[HKMetadataKeyDeviceManufacturerName]
- as? String,
- metadataSyncIdentifier: sample.metadata?[HKMetadataKeySyncIdentifier] as? String,
- metadataSyncVersion: sample.metadata?[HKMetadataKeySyncVersion] as? Double,
- metadataWasTakenInLab: sample.metadata?[HKMetadataKeyWasTakenInLab] as? Bool,
- metadataReferenceRangeLowerLimit: sample.metadata?[HKMetadataKeyReferenceRangeLowerLimit]
- as? Double,
- metadataReferenceRangeUpperLimit: sample.metadata?[HKMetadataKeyReferenceRangeUpperLimit]
- as? Double,
- metadataAlgorithmVersion: sample.metadata?[HKMetadataKeyAlgorithmVersion] as? Double
+ device: serializeDevice(hkDevice: sample.device)
)
}
@@ -182,6 +102,16 @@ func serializeUnknownQuantityTyped(quantity: HKQuantity?) -> Quantity? {
return serializeQuantityTyped(unit: HKUnit.count(), quantity: quantity)
}
+ let countPerMinuteUnit = HKUnit(from: "count/min")
+ if quantity.is(compatibleWith: countPerMinuteUnit) {
+ return serializeQuantityTyped(unit: countPerMinuteUnit, quantity: quantity)
+ }
+
+ let countPerSecondUnit = HKUnit(from: "count/s")
+ if quantity.is(compatibleWith: countPerSecondUnit) {
+ return serializeQuantityTyped(unit: countPerSecondUnit, quantity: quantity)
+ }
+
if quantity.is(compatibleWith: HKUnit.meter()) {
return serializeQuantityTyped(unit: HKUnit.meter(), quantity: quantity)
}
@@ -210,6 +140,14 @@ func serializeUnknownQuantityTyped(quantity: HKQuantity?) -> Quantity? {
return serializeQuantityTyped(unit: HKUnit.hertz(), quantity: quantity)
}
+ let decibelAWeightedSoundPressureLevelUnit = HKUnit(from: "dBASPL")
+ if quantity.is(compatibleWith: decibelAWeightedSoundPressureLevelUnit) {
+ return serializeQuantityTyped(
+ unit: decibelAWeightedSoundPressureLevelUnit,
+ quantity: quantity
+ )
+ }
+
if quantity.is(compatibleWith: SpeedUnit) {
return serializeQuantityTyped(unit: SpeedUnit, quantity: quantity)
}
@@ -218,6 +156,11 @@ func serializeUnknownQuantityTyped(quantity: HKQuantity?) -> Quantity? {
return serializeQuantityTyped(unit: METUnit, quantity: quantity)
}
+ let vo2MaxUnit = HKUnit(from: "ml/(kg*min)")
+ if quantity.is(compatibleWith: vo2MaxUnit) {
+ return serializeQuantityTyped(unit: vo2MaxUnit, quantity: quantity)
+ }
+
if quantity.is(compatibleWith: HKUnit.internationalUnit()) {
return serializeQuantityTyped(unit: HKUnit.internationalUnit(), quantity: quantity)
}
@@ -271,22 +214,47 @@ func serializeMetadata(_ metadata: [String: Any]?) -> AnyMap {
let serialized = AnyMap()
if let m = metadata {
for item in m {
+ if let number = item.value as? NSNumber {
+ if isKnownBooleanMetadataKey(item.key) {
+ serialized.setBoolean(key: item.key, value: number.boolValue)
+ } else if isKnownNumericMetadataKey(item.key) {
+ serialized.setDouble(key: item.key, value: number.doubleValue)
+ } else if CFGetTypeID(number) == CFBooleanGetTypeID() {
+ serialized.setBoolean(key: item.key, value: number.boolValue)
+ } else {
+ serialized.setDouble(key: item.key, value: number.doubleValue)
+ }
+ continue
+ }
+
if let bool = item.value as? Bool {
serialized.setBoolean(key: item.key, value: bool)
+ continue
}
if let str = item.value as? String {
serialized.setString(key: item.key, value: str)
+ continue
}
if let double = item.value as? Double {
serialized.setDouble(key: item.key, value: double)
+ continue
+ }
+
+ if let date = item.value as? Date {
+ serialized.setString(
+ key: item.key,
+ value: metadataDateFormatter.string(from: date)
+ )
+ continue
}
if let quantity = item.value as? HKQuantity {
if let s = serializeUnknownQuantity(quantity: quantity) {
serialized.setObject(key: item.key, value: s)
}
+ continue
}
if let dict = item.value as? [String: AnyValue] {
diff --git a/packages/react-native-healthkit/ios/StateOfMindModule.swift b/packages/react-native-healthkit/ios/StateOfMindModule.swift
index b95eb25b..ad8c3d8f 100644
--- a/packages/react-native-healthkit/ios/StateOfMindModule.swift
+++ b/packages/react-native-healthkit/ios/StateOfMindModule.swift
@@ -40,42 +40,10 @@ import NitroModules
startDate: sample.startDate,
endDate: sample.endDate,
hasUndeterminedDuration: sample.hasUndeterminedDuration,
-
- metadataWeatherCondition: serializeWeatherCondition(
- sample.metadata?[HKMetadataKeyWeatherCondition] as? HKWeatherCondition),
- metadataWeatherHumidity: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherHumidity] as? HKQuantity),
- metadataWeatherTemperature: serializeUnknownQuantityTyped(
- quantity: sample.metadata?[HKMetadataKeyWeatherTemperature] as? HKQuantity),
- metadataInsulinDeliveryReason: serializeInsulinDeliveryReason(
- sample.metadata?[HKMetadataKeyInsulinDeliveryReason] as? HKInsulinDeliveryReason),
- metadataHeartRateMotionContext: serializeHeartRateMotionContext(
- sample.metadata?[HKMetadataKeyHeartRateMotionContext] as? HKHeartRateMotionContext),
-
+ metadata: serializeMetadata(sample.metadata),
uuid: sample.uuid.uuidString,
sourceRevision: serializeSourceRevision(sample.sourceRevision),
- device: serializeDevice(hkDevice: sample.device),
- metadata: serializeMetadata(sample.metadata),
-
- metadataExternalUUID: sample.metadata?[HKMetadataKeyExternalUUID] as? String,
- metadataTimeZone: sample.metadata?[HKMetadataKeyTimeZone] as? String,
- metadataWasUserEntered: sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool,
- metadataDeviceSerialNumber: sample.metadata?[HKMetadataKeyDeviceSerialNumber] as? String,
- metadataUdiDeviceIdentifier: sample.metadata?[HKMetadataKeyUDIDeviceIdentifier] as? String,
- metadataUdiProductionIdentifier: sample.metadata?[HKMetadataKeyUDIProductionIdentifier]
- as? String,
- metadataDigitalSignature: sample.metadata?[HKMetadataKeyDigitalSignature] as? String,
- metadataDeviceName: sample.metadata?[HKMetadataKeyDeviceName] as? String,
- metadataDeviceManufacturerName: sample.metadata?[HKMetadataKeyDeviceManufacturerName]
- as? String,
- metadataSyncIdentifier: sample.metadata?[HKMetadataKeySyncIdentifier] as? String,
- metadataSyncVersion: sample.metadata?[HKMetadataKeySyncVersion] as? Double,
- metadataWasTakenInLab: sample.metadata?[HKMetadataKeyWasTakenInLab] as? Bool,
- metadataReferenceRangeLowerLimit: sample.metadata?[HKMetadataKeyReferenceRangeLowerLimit]
- as? Double,
- metadataReferenceRangeUpperLimit: sample.metadata?[HKMetadataKeyReferenceRangeUpperLimit]
- as? Double,
- metadataAlgorithmVersion: sample.metadata?[HKMetadataKeyAlgorithmVersion] as? Double
+ device: serializeDevice(hkDevice: sample.device)
)
}
diff --git a/packages/react-native-healthkit/ios/WorkoutProxy.swift b/packages/react-native-healthkit/ios/WorkoutProxy.swift
index 34075bc6..83e11e8d 100644
--- a/packages/react-native-healthkit/ios/WorkoutProxy.swift
+++ b/packages/react-native-healthkit/ios/WorkoutProxy.swift
@@ -222,40 +222,14 @@ class WorkoutProxy: HybridWorkoutProxySpec {
totalFlightsClimbed: self.totalFlightsClimbed,
events: self.events,
activities: self.activities,
- metadataAverageMETs: self.metadataAverageMETs,
- metadataElevationAscended: self.metadataElevationAscended,
- metadataElevationDescended: self.metadataElevationDescended,
- metadataIndoorWorkout: self.metadataIndoorWorkout,
- metadataAverageSpeed: self.metadataAverageSpeed,
- metadataMaximumSpeed: self.metadataMaximumSpeed,
+ metadata: self.metadata,
sampleType: self.sampleType,
startDate: self.startDate,
endDate: self.endDate,
hasUndeterminedDuration: self.hasUndeterminedDuration,
- metadataWeatherCondition: self.metadataWeatherCondition,
- metadataWeatherHumidity: self.metadataWeatherHumidity,
- metadataWeatherTemperature: self.metadataWeatherTemperature,
- metadataInsulinDeliveryReason: self.metadataInsulinDeliveryReason,
- metadataHeartRateMotionContext: self.metadataHeartRateMotionContext,
uuid: self.uuid,
sourceRevision: self.sourceRevision,
- device: self.device,
- metadata: self.metadata,
- metadataExternalUUID: self.metadataExternalUUID,
- metadataTimeZone: self.metadataTimeZone,
- metadataWasUserEntered: self.metadataWasUserEntered,
- metadataDeviceSerialNumber: self.metadataDeviceSerialNumber,
- metadataUdiDeviceIdentifier: self.metadataUdiDeviceIdentifier,
- metadataUdiProductionIdentifier: self.metadataUdiProductionIdentifier,
- metadataDigitalSignature: self.metadataDigitalSignature,
- metadataDeviceName: self.metadataDeviceName,
- metadataDeviceManufacturerName: self.metadataDeviceManufacturerName,
- metadataSyncIdentifier: self.metadataSyncIdentifier,
- metadataSyncVersion: self.metadataSyncVersion,
- metadataWasTakenInLab: self.metadataWasTakenInLab,
- metadataReferenceRangeLowerLimit: self.metadataReferenceRangeLowerLimit,
- metadataReferenceRangeUpperLimit: self.metadataReferenceRangeUpperLimit,
- metadataAlgorithmVersion: self.metadataAlgorithmVersion
+ device: self.device
)
}
@@ -369,126 +343,6 @@ class WorkoutProxy: HybridWorkoutProxySpec {
return workout.hasUndeterminedDuration
}
- // Weather metadata
- var metadataWeatherCondition: WeatherCondition? {
- return serializeWeatherCondition(
- workout.metadata?[HKMetadataKeyWeatherCondition] as? HKWeatherCondition)
- }
-
- var metadataWeatherHumidity: Quantity? {
- return serializeUnknownQuantityTyped(
- quantity: workout.metadata?[HKMetadataKeyWeatherHumidity] as? HKQuantity)
- }
-
- var metadataWeatherTemperature: Quantity? {
- return serializeUnknownQuantityTyped(
- quantity: workout.metadata?[HKMetadataKeyWeatherTemperature] as? HKQuantity)
- }
-
- var metadataInsulinDeliveryReason: InsulinDeliveryReason? {
- return serializeInsulinDeliveryReason(
- workout.metadata?[HKMetadataKeyInsulinDeliveryReason] as? HKInsulinDeliveryReason)
- }
-
- var metadataHeartRateMotionContext: HeartRateMotionContext? {
- return serializeHeartRateMotionContext(
- workout.metadata?[HKMetadataKeyHeartRateMotionContext] as? HKHeartRateMotionContext)
- }
-
- // Workout-specific metadata
- var metadataAverageMETs: Quantity? {
- return serializeUnknownQuantityTyped(
- quantity: workout.metadata?[HKMetadataKeyAverageMETs] as? HKQuantity)
- }
-
- var metadataElevationAscended: Quantity? {
- return serializeUnknownQuantityTyped(
- quantity: workout.metadata?[HKMetadataKeyElevationAscended] as? HKQuantity)
- }
-
- var metadataElevationDescended: Quantity? {
- if #available(iOS 18.0, *) {
- return serializeUnknownQuantityTyped(
- quantity: workout.metadata?[HKMetadataKeyElevationDescended] as? HKQuantity)
- }
- return nil
- }
-
- var metadataIndoorWorkout: Bool? {
- return workout.metadata?[HKMetadataKeyIndoorWorkout] as? Bool
- }
-
- var metadataAverageSpeed: Quantity? {
- return serializeUnknownQuantityTyped(
- quantity: workout.metadata?[HKMetadataKeyAverageSpeed] as? HKQuantity)
- }
-
- var metadataMaximumSpeed: Quantity? {
- return serializeUnknownQuantityTyped(
- quantity: workout.metadata?[HKMetadataKeyMaximumSpeed] as? HKQuantity)
- }
-
- // BaseObject metadata
- var metadataExternalUUID: String? {
- return workout.metadata?[HKMetadataKeyExternalUUID] as? String
- }
-
- var metadataTimeZone: String? {
- return workout.metadata?[HKMetadataKeyTimeZone] as? String
- }
-
- var metadataWasUserEntered: Bool? {
- return workout.metadata?[HKMetadataKeyWasUserEntered] as? Bool
- }
-
- var metadataDeviceSerialNumber: String? {
- return workout.metadata?[HKMetadataKeyDeviceSerialNumber] as? String
- }
-
- var metadataUdiDeviceIdentifier: String? {
- return workout.metadata?[HKMetadataKeyUDIDeviceIdentifier] as? String
- }
-
- var metadataUdiProductionIdentifier: String? {
- return workout.metadata?[HKMetadataKeyUDIProductionIdentifier] as? String
- }
-
- var metadataDigitalSignature: String? {
- return workout.metadata?[HKMetadataKeyDigitalSignature] as? String
- }
-
- var metadataDeviceName: String? {
- return workout.metadata?[HKMetadataKeyDeviceName] as? String
- }
-
- var metadataDeviceManufacturerName: String? {
- return workout.metadata?[HKMetadataKeyDeviceManufacturerName] as? String
- }
-
- var metadataSyncIdentifier: String? {
- return workout.metadata?[HKMetadataKeySyncIdentifier] as? String
- }
-
- var metadataSyncVersion: Double? {
- return workout.metadata?[HKMetadataKeySyncVersion] as? Double
- }
-
- var metadataWasTakenInLab: Bool? {
- return workout.metadata?[HKMetadataKeyWasTakenInLab] as? Bool
- }
-
- var metadataReferenceRangeLowerLimit: Double? {
- return workout.metadata?[HKMetadataKeyReferenceRangeLowerLimit] as? Double
- }
-
- var metadataReferenceRangeUpperLimit: Double? {
- return workout.metadata?[HKMetadataKeyReferenceRangeUpperLimit] as? Double
- }
-
- var metadataAlgorithmVersion: Double? {
- return workout.metadata?[HKMetadataKeyAlgorithmVersion] as? Double
- }
-
var events: [WorkoutEvent]? {
if let hkWorkoutEvents = workout.workoutEvents {
return hkWorkoutEvents.compactMap { event in
@@ -555,4 +409,4 @@ class WorkoutProxy: HybridWorkoutProxySpec {
return try await getSerializedWorkoutLocations(workout: self.workout)
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/react-native-healthkit/ios/generated/HealthkitGenerated.swift b/packages/react-native-healthkit/ios/generated/HealthkitGenerated.swift
new file mode 100644
index 00000000..de4a8af1
--- /dev/null
+++ b/packages/react-native-healthkit/ios/generated/HealthkitGenerated.swift
@@ -0,0 +1,52 @@
+// AUTO-GENERATED FILE. DO NOT EDIT.
+// Source: scripts/generate-healthkit.ts
+
+import Foundation
+
+private let healthkitBooleanMetadataKeys: Set = [
+ "HKAppleDeviceCalibrated",
+ "HKAppleFitnessPlusSession",
+ "HKCoachedWorkout",
+ "HKGroupFitness",
+ "HKIndoorWorkout",
+ "HKMenstrualCycleStart",
+ "HKQuantityClampedToLowerBound",
+ "HKQuantityClampedToUpperBound",
+ "HKSexualActivityProtectionUsed",
+ "HKWasTakenInLab",
+ "HKWasUserEntered",
+]
+
+private let healthkitNumericMetadataKeys: Set = [
+ "HKActivityType",
+ "HKAlgorithmVersion",
+ "HKAppleECGAlgorithmVersion",
+ "HKBloodGlucoseMealTime",
+ "HKBodyTemperatureSensorLocation",
+ "HKCyclingFunctionalThresholdPowerTestType",
+ "HKDevicePlacementSide",
+ "HKHeartRateMotionContext",
+ "HKHeartRateRecoveryActivityType",
+ "HKHeartRateRecoveryTestType",
+ "HKHeartRateSensorLocation",
+ "HKInsulinDeliveryReason",
+ "HKPhysicalEffortEstimationType",
+ "HKReferenceRangeLowerLimit",
+ "HKReferenceRangeUpperLimit",
+ "HKSwimmingLocationType",
+ "HKSwimmingStrokeStyle",
+ "HKSWOLFScore",
+ "HKSyncVersion",
+ "HKUserMotionContext",
+ "HKVO2MaxTestType",
+ "HKWaterSalinity",
+ "HKWeatherCondition",
+]
+
+func isKnownBooleanMetadataKey(_ key: String) -> Bool {
+ healthkitBooleanMetadataKeys.contains(key)
+}
+
+func isKnownNumericMetadataKey(_ key: String) -> Bool {
+ healthkitNumericMetadataKeys.contains(key)
+}
diff --git a/packages/react-native-healthkit/package.json b/packages/react-native-healthkit/package.json
index 07e63a47..0a7e5caa 100644
--- a/packages/react-native-healthkit/package.json
+++ b/packages/react-native-healthkit/package.json
@@ -52,13 +52,16 @@
"README.md"
],
"scripts": {
+ "generate:healthkit": "bun ./scripts/generate-healthkit-cli.ts",
+ "verify:healthkit-sdk": "bun ./scripts/verify-healthkit-sdk.ts",
"clean": "rm -rf lib",
"build:cjs": "tsc -p tsconfig.cjs.json",
"build:esm": "tsc -p tsconfig.esm.json",
"build:types": "tsc -p tsconfig.declarations.json",
"build": "bun run clean && bun run build:cjs && bun run build:esm && bun run build:types",
- "typecheck": "tsc --noEmit -p tsconfig.json",
+ "typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.type-tests.json",
"lint": "bunx @biomejs/biome format --write",
+ "check:generated": "bun run generate:healthkit && bun run verify:healthkit-sdk && git diff --exit-code -- src/generated/healthkit.generated.ts src/generated/healthkit-schema.json ../../apps/example/contracts/generated ios/generated",
"codegen": "bun run build && nitrogen --logLevel=\"debug\" && perl -i -pe 's/Bool\\(fromCxx: cachedCxxPart\\)/cachedCxxPart.use_count() > 0/g' nitrogen/generated/ios/swift/*Spec_cxx.swift",
"test": "bun test",
"prepublishOnly": "bun run build && bun run codegen"
diff --git a/packages/react-native-healthkit/scripts/generate-healthkit-cli.ts b/packages/react-native-healthkit/scripts/generate-healthkit-cli.ts
new file mode 100644
index 00000000..2397981a
--- /dev/null
+++ b/packages/react-native-healthkit/scripts/generate-healthkit-cli.ts
@@ -0,0 +1,28 @@
+import {
+ buildHealthkitSchemaFromSources,
+ renderGeneratedContracts,
+ renderGeneratedSwift,
+ renderGeneratedTypescript,
+} from './generate-healthkit'
+import {
+ formatGeneratedArtifacts,
+ getGeneratedArtifactPaths,
+ loadHealthkitSdkSources,
+ writeGeneratedArtifacts,
+} from './healthkit-sdk'
+
+function main() {
+ const generatedArtifactPaths = getGeneratedArtifactPaths()
+ const sources = loadHealthkitSdkSources()
+ const schema = buildHealthkitSchemaFromSources(sources)
+ writeGeneratedArtifacts(
+ schema,
+ renderGeneratedTypescript(schema),
+ renderGeneratedContracts(schema),
+ renderGeneratedSwift(schema),
+ generatedArtifactPaths,
+ )
+ formatGeneratedArtifacts(generatedArtifactPaths)
+}
+
+main()
diff --git a/packages/react-native-healthkit/scripts/generate-healthkit.ts b/packages/react-native-healthkit/scripts/generate-healthkit.ts
new file mode 100644
index 00000000..b58452db
--- /dev/null
+++ b/packages/react-native-healthkit/scripts/generate-healthkit.ts
@@ -0,0 +1,2118 @@
+import ts from 'typescript'
+
+type ObjectType =
+ | 'common'
+ | 'sample'
+ | 'categorySample'
+ | 'quantitySample'
+ | 'workout'
+ | 'workoutEvent'
+
+interface EnumMemberSchema {
+ readonly name: string
+ readonly swiftName: string
+ readonly rawValue: number
+}
+
+interface EnumSchema {
+ readonly name: string
+ readonly ios: string | null
+ readonly members: readonly EnumMemberSchema[]
+}
+
+interface QuantityIdentifierSchema {
+ readonly name: string
+ readonly ios: string | null
+ readonly canonicalUnit: string | null
+ readonly aggregationStyle: string | null
+ readonly writeable: boolean
+ readonly legacy: boolean
+}
+
+interface CategoryIdentifierSchema {
+ readonly name: string
+ readonly ios: string | null
+ readonly valueEnum: string | null
+ readonly writeable: boolean
+ readonly legacy: boolean
+}
+
+type MetadataValueKind = 'string' | 'boolean' | 'number' | 'quantity' | 'enum'
+
+interface MetadataKeySchema {
+ readonly keyConstant: string
+ readonly rawKey: string
+ readonly ios: string | null
+ readonly expectedType: string | null
+ readonly tsType: string
+ readonly valueKind: MetadataValueKind
+ readonly enumName: string | null
+ readonly objectTypes: readonly ObjectType[]
+ readonly identifiers: readonly string[]
+}
+
+export interface HealthkitSchema {
+ readonly quantityIdentifiers: readonly QuantityIdentifierSchema[]
+ readonly categoryIdentifiers: readonly CategoryIdentifierSchema[]
+ readonly enums: readonly EnumSchema[]
+ readonly metadataKeys: readonly MetadataKeySchema[]
+}
+
+interface MetadataOverride {
+ readonly objectTypes?: readonly ObjectType[]
+ readonly identifiers?: readonly string[]
+ readonly tsType?: string
+ readonly valueKind?: MetadataValueKind
+ readonly enumName?: string | null
+ readonly skip?: boolean
+}
+
+export interface IdentifierOverrides {
+ readonly quantity: {
+ readonly readOnly: readonly string[]
+ }
+ readonly category: {
+ readonly readOnly: readonly string[]
+ }
+}
+
+interface SymbolGraphAvailabilityItem {
+ readonly domain?: string
+ readonly introduced?: {
+ readonly major?: number
+ readonly minor?: number
+ }
+ readonly deprecated?: {
+ readonly major?: number
+ readonly minor?: number
+ }
+ readonly renamed?: string
+}
+
+interface SymbolGraphSymbol {
+ readonly kind?: {
+ readonly identifier?: string
+ }
+ readonly identifier?: {
+ readonly precise?: string
+ }
+ readonly pathComponents?: readonly string[]
+ readonly names?: {
+ readonly title?: string
+ }
+ readonly availability?: readonly SymbolGraphAvailabilityItem[]
+}
+
+export interface SymbolGraphDocument {
+ readonly symbols: readonly SymbolGraphSymbol[]
+}
+
+interface SymbolGraphNamedConstant {
+ readonly name: string
+ readonly ios: string | null
+ readonly legacy: boolean
+}
+
+const METADATA_OVERRIDES: Record = {
+ HKMetadataKeyDeviceSerialNumber: {
+ objectTypes: ['common'],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeyUDIDeviceIdentifier: {
+ objectTypes: ['common'],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeyUDIProductionIdentifier: {
+ objectTypes: ['common'],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeyDigitalSignature: {
+ objectTypes: ['common'],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeyExternalUUID: {
+ objectTypes: ['common'],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeySyncIdentifier: {
+ objectTypes: ['common'],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeySyncVersion: {
+ objectTypes: ['common'],
+ tsType: 'number',
+ valueKind: 'number',
+ enumName: null,
+ },
+ HKMetadataKeyTimeZone: {
+ objectTypes: ['common'],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeyDeviceName: {
+ objectTypes: ['common'],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeyDeviceManufacturerName: {
+ objectTypes: ['common'],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeyWasTakenInLab: {
+ objectTypes: ['common'],
+ tsType: 'boolean',
+ valueKind: 'boolean',
+ enumName: null,
+ },
+ HKMetadataKeyReferenceRangeLowerLimit: {
+ objectTypes: ['common'],
+ tsType: 'number',
+ valueKind: 'number',
+ enumName: null,
+ },
+ HKMetadataKeyReferenceRangeUpperLimit: {
+ objectTypes: ['common'],
+ tsType: 'number',
+ valueKind: 'number',
+ enumName: null,
+ },
+ HKMetadataKeyWasUserEntered: {
+ objectTypes: ['common'],
+ tsType: 'boolean',
+ valueKind: 'boolean',
+ enumName: null,
+ },
+ HKMetadataKeyWeatherCondition: {
+ objectTypes: ['sample', 'workout'],
+ tsType: 'WeatherCondition',
+ valueKind: 'enum',
+ enumName: 'WeatherCondition',
+ },
+ HKMetadataKeyWeatherTemperature: {
+ objectTypes: ['sample', 'workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyWeatherHumidity: {
+ objectTypes: ['sample', 'workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyActivityType: {
+ objectTypes: ['sample'],
+ tsType: 'WorkoutActivityType',
+ valueKind: 'enum',
+ enumName: 'WorkoutActivityType',
+ },
+ HKMetadataKeyAlgorithmVersion: {
+ objectTypes: ['sample'],
+ tsType: 'number',
+ valueKind: 'number',
+ enumName: null,
+ },
+ HKMetadataKeyUserMotionContext: {
+ objectTypes: ['sample'],
+ tsType: 'UserMotionContext',
+ valueKind: 'enum',
+ enumName: 'UserMotionContext',
+ },
+ HKMetadataKeyAverageMETs: {
+ objectTypes: ['workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyAverageSpeed: {
+ objectTypes: ['workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyMaximumSpeed: {
+ objectTypes: ['workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyAlpineSlopeGrade: {
+ objectTypes: ['workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyElevationAscended: {
+ objectTypes: ['workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyElevationDescended: {
+ objectTypes: ['workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyDateOfEarliestDataUsedForEstimate: {
+ objectTypes: ['sample'],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeyGlassesPrescriptionDescription: {
+ objectTypes: [],
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ },
+ HKMetadataKeyAppleDeviceCalibrated: {
+ objectTypes: ['sample'],
+ tsType: 'boolean',
+ valueKind: 'boolean',
+ enumName: null,
+ },
+ HKMetadataKeySexualActivityProtectionUsed: {
+ objectTypes: ['categorySample'],
+ identifiers: ['HKCategoryTypeIdentifierSexualActivity'],
+ },
+ HKMetadataKeyMenstrualCycleStart: {
+ objectTypes: ['categorySample'],
+ identifiers: ['HKCategoryTypeIdentifierMenstrualFlow'],
+ },
+ HKMetadataKeyBodyTemperatureSensorLocation: {
+ objectTypes: ['quantitySample'],
+ identifiers: [
+ 'HKQuantityTypeIdentifierBodyTemperature',
+ 'HKQuantityTypeIdentifierBasalBodyTemperature',
+ ],
+ tsType: 'BodyTemperatureSensorLocation',
+ valueKind: 'enum',
+ enumName: 'BodyTemperatureSensorLocation',
+ },
+ HKMetadataKeyHeartRateSensorLocation: {
+ objectTypes: ['quantitySample'],
+ tsType: 'HeartRateSensorLocation',
+ valueKind: 'enum',
+ enumName: 'HeartRateSensorLocation',
+ },
+ HKMetadataKeyHeartRateMotionContext: {
+ objectTypes: ['quantitySample'],
+ tsType: 'HeartRateMotionContext',
+ valueKind: 'enum',
+ enumName: 'HeartRateMotionContext',
+ },
+ HKMetadataKeyHeartRateEventThreshold: {
+ objectTypes: ['categorySample'],
+ identifiers: [
+ 'HKCategoryTypeIdentifierHighHeartRateEvent',
+ 'HKCategoryTypeIdentifierLowHeartRateEvent',
+ ],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeySessionEstimate: {
+ objectTypes: ['quantitySample'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyHeartRateRecoveryTestType: {
+ objectTypes: ['quantitySample'],
+ tsType: 'HeartRateRecoveryTestType',
+ valueKind: 'enum',
+ enumName: 'HeartRateRecoveryTestType',
+ },
+ HKMetadataKeyHeartRateRecoveryActivityType: {
+ objectTypes: ['quantitySample'],
+ tsType: 'WorkoutActivityType',
+ valueKind: 'enum',
+ enumName: 'WorkoutActivityType',
+ },
+ HKMetadataKeyHeartRateRecoveryActivityDuration: {
+ objectTypes: ['quantitySample'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyHeartRateRecoveryMaxObservedRecoveryHeartRate: {
+ objectTypes: ['quantitySample'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyBloodGlucoseMealTime: {
+ objectTypes: ['quantitySample'],
+ identifiers: ['HKQuantityTypeIdentifierBloodGlucose'],
+ tsType: 'BloodGlucoseMealTime',
+ valueKind: 'enum',
+ enumName: 'BloodGlucoseMealTime',
+ },
+ HKMetadataKeyVO2MaxTestType: {
+ objectTypes: ['quantitySample'],
+ identifiers: ['HKQuantityTypeIdentifierVO2Max'],
+ tsType: 'VO2MaxTestType',
+ valueKind: 'enum',
+ enumName: 'VO2MaxTestType',
+ },
+ HKMetadataKeyInsulinDeliveryReason: {
+ objectTypes: ['quantitySample'],
+ identifiers: ['HKQuantityTypeIdentifierInsulinDelivery'],
+ tsType: 'InsulinDeliveryReason',
+ valueKind: 'enum',
+ enumName: 'InsulinDeliveryReason',
+ },
+ HKMetadataKeyQuantityClampedToLowerBound: {
+ objectTypes: ['quantitySample'],
+ tsType: 'boolean',
+ valueKind: 'boolean',
+ enumName: null,
+ },
+ HKMetadataKeyQuantityClampedToUpperBound: {
+ objectTypes: ['quantitySample'],
+ tsType: 'boolean',
+ valueKind: 'boolean',
+ enumName: null,
+ },
+ HKMetadataKeyAudioExposureLevel: {
+ objectTypes: ['categorySample'],
+ identifiers: [
+ 'HKCategoryTypeIdentifierAudioExposureEvent',
+ 'HKCategoryTypeIdentifierEnvironmentalAudioExposureEvent',
+ 'HKCategoryTypeIdentifierHeadphoneAudioExposureEvent',
+ ],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyVO2MaxValue: {
+ objectTypes: ['categorySample'],
+ identifiers: ['HKCategoryTypeIdentifierLowCardioFitnessEvent'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyLowCardioFitnessEventThreshold: {
+ objectTypes: ['categorySample'],
+ identifiers: ['HKCategoryTypeIdentifierLowCardioFitnessEvent'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyHeadphoneGain: {
+ objectTypes: ['categorySample'],
+ identifiers: [
+ 'HKCategoryTypeIdentifierAudioExposureEvent',
+ 'HKCategoryTypeIdentifierHeadphoneAudioExposureEvent',
+ ],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyWorkoutBrandName: {
+ objectTypes: ['workout'],
+ },
+ HKMetadataKeyGroupFitness: {
+ objectTypes: ['workout'],
+ },
+ HKMetadataKeyAppleFitnessPlusCatalogIdentifier: {
+ objectTypes: ['workout'],
+ },
+ HKMetadataKeyAppleFitnessPlusSession: {
+ objectTypes: ['workout'],
+ },
+ HKMetadataKeyIndoorWorkout: {
+ objectTypes: ['workout'],
+ },
+ HKMetadataKeyCoachedWorkout: {
+ objectTypes: ['workout'],
+ },
+ HKMetadataKeyLapLength: {
+ objectTypes: ['workout'],
+ },
+ HKMetadataKeySwimmingLocationType: {
+ objectTypes: ['workout'],
+ tsType: 'WorkoutSwimmingLocationType',
+ valueKind: 'enum',
+ enumName: 'WorkoutSwimmingLocationType',
+ },
+ HKMetadataKeySwimmingStrokeStyle: {
+ objectTypes: ['workoutEvent'],
+ tsType: 'SwimmingStrokeStyle',
+ valueKind: 'enum',
+ enumName: 'SwimmingStrokeStyle',
+ },
+ HKMetadataKeySWOLFScore: {
+ objectTypes: ['workout'],
+ tsType: 'number',
+ valueKind: 'number',
+ enumName: null,
+ },
+ HKMetadataKeyCrossTrainerDistance: {
+ objectTypes: ['workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyFitnessMachineDuration: {
+ objectTypes: ['workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyIndoorBikeDistance: {
+ objectTypes: ['workout'],
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ },
+ HKMetadataKeyPhysicalEffortEstimationType: {
+ objectTypes: ['sample'],
+ tsType: 'PhysicalEffortEstimationType',
+ valueKind: 'enum',
+ enumName: 'PhysicalEffortEstimationType',
+ },
+ HKMetadataKeyWaterSalinity: {
+ objectTypes: ['sample'],
+ tsType: 'WaterSalinity',
+ valueKind: 'enum',
+ enumName: 'WaterSalinity',
+ },
+}
+
+const CATEGORY_VALUE_ENUM_OVERRIDES: Record = {
+ HKCategoryTypeIdentifierMenstrualFlow: 'HKCategoryValueMenstrualFlow',
+ HKCategoryTypeIdentifierAudioExposureEvent:
+ 'HKCategoryValueEnvironmentalAudioExposureEvent',
+}
+
+const EXISTING_ENUM_NAME_OVERRIDES: Record = {
+ HKWeatherCondition: 'WeatherCondition',
+ HKHeartRateMotionContext: 'HeartRateMotionContext',
+ HKInsulinDeliveryReason: 'InsulinDeliveryReason',
+}
+
+function lowerFirst(value: string): string {
+ if (!value) {
+ return value
+ }
+
+ return `${value.charAt(0).toLowerCase()}${value.slice(1)}`
+}
+
+function toTsEnumName(swiftName: string): string {
+ return EXISTING_ENUM_NAME_OVERRIDES[swiftName] ?? swiftName.replace(/^HK/, '')
+}
+
+function toRawMetadataKey(keyConstant: string): string {
+ return keyConstant.replace(/^HKMetadataKey/, 'HK')
+}
+
+function splitTopLevelCommas(input: string): string[] {
+ const result: string[] = []
+ let current = ''
+ let depth = 0
+
+ for (const char of input) {
+ if (char === '(') {
+ depth += 1
+ } else if (char === ')') {
+ depth = Math.max(0, depth - 1)
+ }
+
+ if (char === ',' && depth === 0) {
+ if (current.trim()) {
+ result.push(current.trim())
+ }
+ current = ''
+ continue
+ }
+
+ current += char
+ }
+
+ if (current.trim()) {
+ result.push(current.trim())
+ }
+
+ return result
+}
+
+function parseIosAvailability(text: string): string | null {
+ const match = text.match(/ios\(([^),]+)/)
+ return match?.[1]?.trim() ?? null
+}
+
+function parseSymbolGraphIosAvailability(
+ symbol: SymbolGraphSymbol,
+): string | null {
+ const iosAvailability = symbol.availability?.find(
+ (entry) => entry.domain === 'iOS',
+ )
+ const major = iosAvailability?.introduced?.major
+ if (major == null) {
+ return null
+ }
+ const minor = iosAvailability?.introduced?.minor ?? 0
+ return `${major}.${minor}`
+}
+
+function isLegacySymbol(symbol: SymbolGraphSymbol): boolean {
+ return (
+ symbol.availability?.some(
+ (entry) => entry.deprecated != null || entry.renamed != null,
+ ) ?? false
+ )
+}
+
+function parseSwiftNamedConstants(
+ symbolGraph: SymbolGraphDocument,
+ ownerName: string,
+ precisePrefix?: string,
+): SymbolGraphNamedConstant[] {
+ return symbolGraph.symbols
+ .filter((symbol) => symbol.kind?.identifier === 'swift.type.property')
+ .filter((symbol) => symbol.pathComponents?.[0] === ownerName)
+ .filter((symbol) =>
+ precisePrefix == null
+ ? true
+ : (symbol.identifier?.precise?.startsWith(precisePrefix) ?? false),
+ )
+ .map((symbol) => {
+ const precise = symbol.identifier?.precise
+ const name = precise?.replace(/^c:@/, '')
+ if (name == null) {
+ throw new Error(`Missing precise identifier for ${ownerName} constant`)
+ }
+
+ return {
+ name,
+ ios: parseSymbolGraphIosAvailability(symbol),
+ legacy: isLegacySymbol(symbol),
+ }
+ })
+ .sort((left, right) => left.name.localeCompare(right.name))
+}
+
+function sanitizeEnumEntry(entry: string): string {
+ return entry
+ .replace(/\/\/.*$/gm, '')
+ .replace(/\s+/g, ' ')
+ .trim()
+}
+
+function parseNumericValue(
+ rawValue: string | undefined,
+ knownValues: Map,
+ previousValue: number,
+): number {
+ if (rawValue == null || rawValue === '') {
+ return previousValue + 1
+ }
+
+ const cleanValue = rawValue.trim()
+ if (/^-?\d+$/.test(cleanValue)) {
+ return Number.parseInt(cleanValue, 10)
+ }
+
+ if (knownValues.has(cleanValue)) {
+ const knownValue = knownValues.get(cleanValue)
+ if (knownValue != null) {
+ return knownValue
+ }
+ }
+
+ return previousValue + 1
+}
+
+export function parseNsEnums(headerSource: string): EnumSchema[] {
+ const enums: EnumSchema[] = []
+ const enumRegex =
+ /typedef NS_ENUM\((?:NS)?(?:U)?Integer,\s*(\w+)\)\s*\{([\s\S]*?)\}\s*([^;]*);/g
+
+ for (const match of headerSource.matchAll(enumRegex)) {
+ const swiftName = match[1]
+ const rawBody = match[2]
+ if (swiftName == null || rawBody == null) {
+ continue
+ }
+ const body = rawBody.replace(/\/\/.*$/gm, '')
+ const trailer = match[3] ?? ''
+ const ios = parseIosAvailability(trailer)
+ const entries = splitTopLevelCommas(body)
+ const members: EnumMemberSchema[] = []
+ const knownValues = new Map()
+ let previousValue = -1
+
+ for (const rawEntry of entries) {
+ const entry = sanitizeEnumEntry(rawEntry)
+ if (!entry) {
+ continue
+ }
+
+ const nameMatch = entry.match(/^(\w+)/)
+ if (!nameMatch) {
+ continue
+ }
+
+ const fullName = nameMatch[1]
+ if (fullName == null) {
+ continue
+ }
+ const suffix = fullName.startsWith(swiftName)
+ ? fullName.slice(swiftName.length)
+ : fullName.replace(/^HK/, '')
+ const memberName = lowerFirst(
+ /^\d/.test(suffix) ? `value${suffix}` : suffix,
+ )
+ const rawValueMatch = entry.match(/=\s*([^=]+)$/)
+ const rawValue = parseNumericValue(
+ rawValueMatch?.[1],
+ knownValues,
+ previousValue,
+ )
+ previousValue = rawValue
+ knownValues.set(fullName, rawValue)
+
+ members.push({
+ name: memberName,
+ swiftName: fullName,
+ rawValue,
+ })
+ }
+
+ enums.push({
+ name: toTsEnumName(swiftName),
+ ios,
+ members,
+ })
+ }
+
+ return enums.sort((left, right) => left.name.localeCompare(right.name))
+}
+
+function parseQuantityIdentifierComments(headerSource: string): Map<
+ string,
+ {
+ readonly canonicalUnit: string | null
+ readonly aggregationStyle: string | null
+ }
+> {
+ const identifiers = new Map<
+ string,
+ {
+ readonly canonicalUnit: string | null
+ readonly aggregationStyle: string | null
+ }
+ >()
+ const regex =
+ /^HK_EXTERN HKQuantityTypeIdentifier const (\w+)\s+([^;]*);\s*\/\/\s*(.*)$/gm
+
+ for (const match of headerSource.matchAll(regex)) {
+ const name = match[1]
+ if (name == null) {
+ continue
+ }
+ const comment = match[3]?.trim() ?? ''
+ const [canonicalUnitRaw, aggregationRaw] = comment
+ .split(',')
+ .map((value) => value.trim())
+ identifiers.set(name, {
+ canonicalUnit: canonicalUnitRaw || null,
+ aggregationStyle: aggregationRaw || null,
+ })
+ }
+
+ return identifiers
+}
+
+function parseCategoryIdentifierValueEnums(
+ headerSource: string,
+): Map {
+ const identifiers = new Map()
+ const regex =
+ /^HK_EXTERN HKCategoryTypeIdentifier const (\w+)\s+([^;]*);\s*\/\/\s*(.*)$/gm
+
+ for (const match of headerSource.matchAll(regex)) {
+ const name = match[1]
+ if (name == null) {
+ continue
+ }
+ const comment = match[3]?.trim() ?? ''
+ identifiers.set(
+ name,
+ CATEGORY_VALUE_ENUM_OVERRIDES[name] ?? (comment || null),
+ )
+ }
+
+ return identifiers
+}
+
+function parseMetadataDocComments(headerSource: string): Map<
+ string,
+ {
+ readonly expectedType: string | null
+ readonly objectTypes: readonly ObjectType[]
+ readonly identifiers: readonly string[]
+ }
+> {
+ const keys = new Map<
+ string,
+ {
+ readonly expectedType: string | null
+ readonly objectTypes: readonly ObjectType[]
+ readonly identifiers: readonly string[]
+ }
+ >()
+ const regex =
+ /(\/\*![\s\S]*?\*\/)\s*HK_EXTERN NSString \* const (HKMetadataKey\w+)\s*([^;]*);/g
+
+ for (const match of headerSource.matchAll(regex)) {
+ const docComment = match[1]
+ const keyConstant = match[2]
+ if (docComment == null || keyConstant == null) {
+ continue
+ }
+
+ keys.set(keyConstant, {
+ expectedType: extractExpectedType(docComment),
+ objectTypes: inferObjectTypes(docComment).objectTypes,
+ identifiers: inferObjectTypes(docComment).identifiers,
+ })
+ }
+
+ return keys
+}
+
+function parseQuantityIdentifiers(
+ symbolGraph: SymbolGraphDocument,
+ headerSource: string,
+ overrides: IdentifierOverrides,
+): QuantityIdentifierSchema[] {
+ const readOnly = new Set(overrides.quantity.readOnly)
+ const commentMap = parseQuantityIdentifierComments(headerSource)
+ const identifiers = new Map()
+ const discovered = parseSwiftNamedConstants(
+ symbolGraph,
+ 'HKQuantityTypeIdentifier',
+ 'c:@HKQuantityTypeIdentifier',
+ )
+
+ for (const constant of discovered) {
+ const commentInfo = commentMap.get(constant.name)
+ identifiers.set(constant.name, {
+ name: constant.name,
+ ios: constant.ios,
+ canonicalUnit: commentInfo?.canonicalUnit ?? null,
+ aggregationStyle: commentInfo?.aggregationStyle ?? null,
+ writeable: !readOnly.has(constant.name),
+ legacy: constant.legacy,
+ })
+ }
+
+ return [...identifiers.values()].sort((left, right) =>
+ left.name.localeCompare(right.name),
+ )
+}
+
+function parseCategoryIdentifiers(
+ symbolGraph: SymbolGraphDocument,
+ headerSource: string,
+ overrides: IdentifierOverrides,
+): CategoryIdentifierSchema[] {
+ const readOnly = new Set(overrides.category.readOnly)
+ const valueEnumMap = parseCategoryIdentifierValueEnums(headerSource)
+ const identifiers = new Map()
+ const discovered = parseSwiftNamedConstants(
+ symbolGraph,
+ 'HKCategoryTypeIdentifier',
+ 'c:@HKCategoryTypeIdentifier',
+ )
+
+ for (const constant of discovered) {
+ identifiers.set(constant.name, {
+ name: constant.name,
+ ios: constant.ios,
+ valueEnum: valueEnumMap.get(constant.name) ?? null,
+ writeable: !readOnly.has(constant.name),
+ legacy: constant.legacy,
+ })
+ }
+
+ return [...identifiers.values()].sort((left, right) =>
+ left.name.localeCompare(right.name),
+ )
+}
+
+function extractExpectedType(docComment: string): string | null {
+ const normalized = docComment.replace(/\s+/g, ' ')
+ const match = normalized.match(/The expected value type is ([^.]+)\./i)
+ return match?.[1]?.trim() ?? null
+}
+
+function inferObjectTypes(docComment: string): {
+ readonly objectTypes: ObjectType[]
+ readonly identifiers: string[]
+} {
+ const normalized = docComment.replace(/\s+/g, ' ')
+ const identifiers = [
+ ...new Set(
+ [...normalized.matchAll(/HK(?:Category|Quantity)TypeIdentifier\w+/g)].map(
+ (match) => match[0],
+ ),
+ ),
+ ]
+ const objectTypes = new Set()
+
+ if (
+ /HKObject|creating an HKObject|when the HKObject was created|of the HKObject/i.test(
+ normalized,
+ )
+ ) {
+ objectTypes.add('common')
+ }
+
+ if (/sample|reading|HKSample/i.test(normalized)) {
+ objectTypes.add('sample')
+ }
+
+ if (/category sample/i.test(normalized)) {
+ objectTypes.add('categorySample')
+ }
+
+ if (/quantity sample|HKQuantitySample/i.test(normalized)) {
+ objectTypes.add('quantitySample')
+ }
+
+ if (
+ /HKWorkout object|workout segment|distance sample|workout was/i.test(
+ normalized,
+ )
+ ) {
+ objectTypes.add('workout')
+ }
+
+ if (/HKWorkoutEvent object/i.test(normalized)) {
+ objectTypes.add('workoutEvent')
+ }
+
+ return {
+ objectTypes: [...objectTypes],
+ identifiers,
+ }
+}
+
+function inferTsType(expectedType: string | null): {
+ readonly tsType: string
+ readonly valueKind: MetadataValueKind
+ readonly enumName: string | null
+} | null {
+ if (expectedType == null) {
+ return null
+ }
+
+ if (/NSString/i.test(expectedType)) {
+ return {
+ tsType: 'string',
+ valueKind: 'string',
+ enumName: null,
+ }
+ }
+
+ if (/BOOL/i.test(expectedType)) {
+ return {
+ tsType: 'boolean',
+ valueKind: 'boolean',
+ enumName: null,
+ }
+ }
+
+ if (/HKQuantity/i.test(expectedType)) {
+ return {
+ tsType: 'Quantity',
+ valueKind: 'quantity',
+ enumName: null,
+ }
+ }
+
+ const enumMatch = expectedType.match(/\b(HK[A-Za-z0-9]+)\b/)
+
+ if (enumMatch?.[1] && enumMatch[1] !== 'HKQuantity') {
+ return {
+ tsType: toTsEnumName(enumMatch[1]),
+ valueKind: 'enum',
+ enumName: toTsEnumName(enumMatch[1]),
+ }
+ }
+
+ if (/NSNumber/i.test(expectedType)) {
+ return {
+ tsType: 'number',
+ valueKind: 'number',
+ enumName: null,
+ }
+ }
+
+ return null
+}
+
+function parseMetadataKeys(
+ symbolGraph: SymbolGraphDocument,
+ headerSource: string,
+): MetadataKeySchema[] {
+ const keys: MetadataKeySchema[] = []
+ const docCommentMap = parseMetadataDocComments(headerSource)
+ const metadataConstants = symbolGraph.symbols
+ .filter((symbol) => symbol.kind?.identifier === 'swift.var')
+ .filter((symbol) =>
+ symbol.identifier?.precise?.startsWith('c:@HKMetadataKey'),
+ )
+ .map((symbol) => {
+ const precise = symbol.identifier?.precise
+ const name = precise?.replace(/^c:@/, '')
+ if (name == null) {
+ throw new Error('Missing metadata key precise identifier')
+ }
+ return {
+ name,
+ ios: parseSymbolGraphIosAvailability(symbol),
+ }
+ })
+ .sort((left, right) => left.name.localeCompare(right.name))
+
+ for (const constant of metadataConstants) {
+ const docComment = docCommentMap.get(constant.name)
+ const keyConstant = constant.name
+ const ios = constant.ios
+ const expectedType = docComment?.expectedType ?? null
+ const inferred = inferTsType(expectedType)
+ const override = METADATA_OVERRIDES[keyConstant]
+
+ if (override?.skip) {
+ continue
+ }
+
+ if (inferred == null && override?.tsType == null) {
+ continue
+ }
+
+ const tsType = override?.tsType ?? inferred?.tsType
+ const valueKind = override?.valueKind ?? inferred?.valueKind
+ const enumName =
+ override != null && 'enumName' in override
+ ? (override.enumName ?? null)
+ : (inferred?.enumName ?? null)
+
+ if (tsType == null || valueKind == null) {
+ continue
+ }
+
+ keys.push({
+ keyConstant,
+ rawKey: toRawMetadataKey(keyConstant),
+ ios,
+ expectedType,
+ tsType,
+ valueKind,
+ enumName,
+ objectTypes: override?.objectTypes ?? docComment?.objectTypes ?? [],
+ identifiers: override?.identifiers ?? docComment?.identifiers ?? [],
+ })
+ }
+
+ return keys.sort((left, right) => left.rawKey.localeCompare(right.rawKey))
+}
+
+export function buildHealthkitSchemaFromSources(sources: {
+ readonly sdkPath: string
+ readonly symbolGraph: SymbolGraphDocument
+ readonly identifierOverrides: IdentifierOverrides
+ readonly typeIdentifiersHeader: string
+ readonly categoryValuesHeader: string
+ readonly metadataHeader: string
+ readonly metadataEnumsHeader: string
+ readonly workoutHeader: string
+}): HealthkitSchema {
+ const enumsByName = new Map()
+ for (const enumSchema of [
+ ...parseNsEnums(sources.categoryValuesHeader),
+ ...parseNsEnums(sources.metadataEnumsHeader),
+ ...parseNsEnums(sources.workoutHeader).filter(
+ (schema) =>
+ schema.name === 'WorkoutActivityType' ||
+ schema.name === 'WorkoutEventType',
+ ),
+ ]) {
+ enumsByName.set(enumSchema.name, enumSchema)
+ }
+
+ return {
+ quantityIdentifiers: parseQuantityIdentifiers(
+ sources.symbolGraph,
+ sources.typeIdentifiersHeader,
+ sources.identifierOverrides,
+ ),
+ categoryIdentifiers: parseCategoryIdentifiers(
+ sources.symbolGraph,
+ sources.typeIdentifiersHeader,
+ sources.identifierOverrides,
+ ),
+ enums: [...enumsByName.values()].sort((left, right) =>
+ left.name.localeCompare(right.name),
+ ),
+ metadataKeys: parseMetadataKeys(
+ sources.symbolGraph,
+ sources.metadataHeader,
+ ),
+ }
+}
+
+function unique(values: readonly T[]): T[] {
+ return [...new Set(values)]
+}
+
+function tsSource(
+ strings: TemplateStringsArray,
+ ...values: readonly string[]
+): string {
+ let output = ''
+
+ for (const [index, string] of strings.entries()) {
+ output += string
+ output += values[index] ?? ''
+ }
+
+ return output
+}
+
+const FACTORY = ts.factory
+const EXPORT_MODIFIER = FACTORY.createModifier(ts.SyntaxKind.ExportKeyword)
+const READONLY_MODIFIER = FACTORY.createModifier(ts.SyntaxKind.ReadonlyKeyword)
+
+function identifierName(name: string): ts.PropertyName {
+ return FACTORY.createIdentifier(name)
+}
+
+function stringLiteralType(value: string): ts.TypeNode {
+ return FACTORY.createLiteralTypeNode(FACTORY.createStringLiteral(value))
+}
+
+function keywordType(kind: ts.KeywordTypeSyntaxKind): ts.KeywordTypeNode {
+ return FACTORY.createKeywordTypeNode(kind)
+}
+
+function namedType(
+ name: string,
+ typeArguments: ts.TypeNode[] = [],
+): ts.TypeNode {
+ switch (name) {
+ case 'string':
+ return keywordType(ts.SyntaxKind.StringKeyword)
+ case 'number':
+ return keywordType(ts.SyntaxKind.NumberKeyword)
+ case 'boolean':
+ return keywordType(ts.SyntaxKind.BooleanKeyword)
+ case 'never':
+ return keywordType(ts.SyntaxKind.NeverKeyword)
+ default:
+ return FACTORY.createTypeReferenceNode(name, typeArguments)
+ }
+}
+
+function canonicalUnitToTypeNode(unit: string | null): ts.TypeNode {
+ if (unit == null) {
+ return keywordType(ts.SyntaxKind.StringKeyword)
+ }
+
+ if (unit === 'mg/dL' || unit.startsWith('mmol<')) {
+ return namedType('BloodGlucoseUnit')
+ }
+ if (unit === '%') {
+ return stringLiteralType('%')
+ }
+ if (unit === 'count') {
+ return stringLiteralType('count')
+ }
+ if (unit === 'IU') {
+ return stringLiteralType('IU')
+ }
+ if (unit === 'appleEffortScore') {
+ return stringLiteralType('appleEffortScore')
+ }
+ if (unit === 'degC' || unit === 'degF' || unit === 'K') {
+ return namedType('TemperatureUnit')
+ }
+ if (
+ unit === 'm' ||
+ unit === 'cm' ||
+ unit === 'km' ||
+ unit === 'ft' ||
+ unit === 'in' ||
+ unit === 'yd' ||
+ unit === 'mi'
+ ) {
+ return namedType('LengthUnit')
+ }
+ if (
+ unit === 'kg' ||
+ unit === 'g' ||
+ unit === 'mg' ||
+ unit === 'mcg' ||
+ unit === 'oz' ||
+ unit === 'lb' ||
+ unit === 'st'
+ ) {
+ return namedType('MassUnit')
+ }
+ if (unit === 'l' || unit === 'ml') {
+ return namedType('VolumeUnit')
+ }
+ if (unit === 'kcal' || unit === 'Cal' || unit === 'cal' || unit === 'J') {
+ return namedType('EnergyUnit')
+ }
+ if (unit === 'd' || unit === 'hr' || unit === 'min' || unit === 's') {
+ return namedType('TimeUnit')
+ }
+ if (/^count\/(s|min|hr|d)$/.test(unit)) {
+ return namedType('CountPerTime', [namedType('TimeUnit')])
+ }
+ if (
+ /^(m|km|cm|ft|in|yd|mi)\/(s|min|hr|d)$/.test(unit) ||
+ /^(m|km|cm)\/(s|min|hr)$/.test(unit)
+ ) {
+ return namedType('SpeedUnit', [
+ namedType('LengthUnit'),
+ namedType('TimeUnit'),
+ ])
+ }
+ if (
+ unit === 'mmHg' ||
+ unit === 'inHg' ||
+ unit === 'cmAq' ||
+ unit === 'atm' ||
+ unit === 'dBASPL' ||
+ unit === 'Pa'
+ ) {
+ return namedType('PressureUnit')
+ }
+ if (unit === 'Hz') {
+ return namedType('Unit')
+ }
+
+ return keywordType(ts.SyntaxKind.StringKeyword)
+}
+
+function exportedTypeAlias(
+ name: string,
+ type: ts.TypeNode,
+ typeParameters?: ts.TypeParameterDeclaration[],
+): ts.TypeAliasDeclaration {
+ return FACTORY.createTypeAliasDeclaration(
+ [EXPORT_MODIFIER],
+ name,
+ typeParameters,
+ type,
+ )
+}
+
+function exportedInterface(
+ name: string,
+ members: ts.TypeElement[],
+ extendsNames: string[] = [],
+): ts.InterfaceDeclaration {
+ return FACTORY.createInterfaceDeclaration(
+ [EXPORT_MODIFIER],
+ name,
+ undefined,
+ extendsNames.map((extendName) =>
+ FACTORY.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [
+ FACTORY.createExpressionWithTypeArguments(
+ FACTORY.createIdentifier(extendName),
+ undefined,
+ ),
+ ]),
+ ),
+ members,
+ )
+}
+
+function readonlyProperty(
+ name: string,
+ type: ts.TypeNode,
+ optional = true,
+): ts.PropertySignature {
+ return FACTORY.createPropertySignature(
+ [READONLY_MODIFIER],
+ identifierName(name),
+ optional ? FACTORY.createToken(ts.SyntaxKind.QuestionToken) : undefined,
+ type,
+ )
+}
+
+function exportedConstObject(
+ name: string,
+ entries: readonly { readonly key: string; readonly value: string | null }[],
+): ts.VariableStatement {
+ return FACTORY.createVariableStatement(
+ [EXPORT_MODIFIER],
+ FACTORY.createVariableDeclarationList(
+ [
+ FACTORY.createVariableDeclaration(
+ name,
+ undefined,
+ undefined,
+ FACTORY.createAsExpression(
+ FACTORY.createObjectLiteralExpression(
+ entries.map((entry) =>
+ FACTORY.createPropertyAssignment(
+ identifierName(entry.key),
+ entry.value == null
+ ? FACTORY.createNull()
+ : FACTORY.createStringLiteral(entry.value),
+ ),
+ ),
+ true,
+ ),
+ FACTORY.createTypeReferenceNode('const'),
+ ),
+ ),
+ ],
+ ts.NodeFlags.Const,
+ ),
+ )
+}
+
+function createLiteralUnion(values: readonly string[]): ts.TypeNode {
+ if (values.length === 0) {
+ return keywordType(ts.SyntaxKind.NeverKeyword)
+ }
+ if (values.length === 1) {
+ const firstValue = values[0]
+ if (firstValue == null) {
+ return keywordType(ts.SyntaxKind.NeverKeyword)
+ }
+ return stringLiteralType(firstValue)
+ }
+ return FACTORY.createUnionTypeNode(
+ values.map((value) => stringLiteralType(value)),
+ )
+}
+
+function createPickType(
+ baseName: string,
+ fields: readonly string[],
+): ts.TypeNode {
+ return namedType('Pick', [namedType(baseName), createLiteralUnion(fields)])
+}
+
+function createKeyofType(name: string): ts.TypeNode {
+ return FACTORY.createTypeOperatorNode(
+ ts.SyntaxKind.KeyOfKeyword,
+ namedType(name),
+ )
+}
+
+function createConditionalGeneratedType(
+ genericName: string,
+ baseName: string,
+ mapName: string,
+ fallbackType: ts.TypeNode,
+): ts.TypeAliasDeclaration {
+ return exportedTypeAlias(
+ genericName,
+ FACTORY.createIntersectionTypeNode([
+ namedType(baseName),
+ FACTORY.createParenthesizedType(
+ FACTORY.createConditionalTypeNode(
+ namedType('T'),
+ createKeyofType(mapName),
+ FACTORY.createIndexedAccessTypeNode(
+ namedType(mapName),
+ namedType('T'),
+ ),
+ fallbackType,
+ ),
+ ),
+ ]),
+ [
+ FACTORY.createTypeParameterDeclaration(
+ undefined,
+ 'T',
+ FACTORY.createTypeReferenceNode(
+ genericName.includes('Quantity')
+ ? 'QuantityTypeIdentifier'
+ : 'CategoryTypeIdentifier',
+ ),
+ FACTORY.createTypeReferenceNode(
+ genericName.includes('Quantity')
+ ? 'QuantityTypeIdentifier'
+ : 'CategoryTypeIdentifier',
+ ),
+ ),
+ ],
+ )
+}
+
+export function renderGeneratedTypescript(schema: HealthkitSchema): string {
+ const statements: ts.Statement[] = []
+
+ statements.push(
+ FACTORY.createImportDeclaration(
+ undefined,
+ FACTORY.createImportClause(
+ true,
+ undefined,
+ FACTORY.createNamedImports([
+ FACTORY.createImportSpecifier(
+ true,
+ undefined,
+ FACTORY.createIdentifier('Quantity'),
+ ),
+ ]),
+ ),
+ FACTORY.createStringLiteral('../types/QuantityType'),
+ ),
+ )
+ statements.push(
+ FACTORY.createImportDeclaration(
+ undefined,
+ FACTORY.createImportClause(
+ true,
+ undefined,
+ FACTORY.createNamedImports(
+ [
+ 'BloodGlucoseUnit',
+ 'CountPerTime',
+ 'EnergyUnit',
+ 'LengthUnit',
+ 'MassUnit',
+ 'PressureUnit',
+ 'SpeedUnit',
+ 'TemperatureUnit',
+ 'TimeUnit',
+ ].map((name) =>
+ FACTORY.createImportSpecifier(
+ true,
+ undefined,
+ FACTORY.createIdentifier(name),
+ ),
+ ),
+ ),
+ ),
+ FACTORY.createStringLiteral('../types/Units'),
+ ),
+ )
+
+ const quantityReadOnly = schema.quantityIdentifiers
+ .filter((identifier) => !identifier.writeable)
+ .map((identifier) => identifier.name)
+ const quantityWriteable = schema.quantityIdentifiers
+ .filter((identifier) => identifier.writeable)
+ .map((identifier) => identifier.name)
+ const categoryReadOnly = schema.categoryIdentifiers
+ .filter((identifier) => !identifier.writeable)
+ .map((identifier) => identifier.name)
+ const categoryWriteable = schema.categoryIdentifiers
+ .filter((identifier) => identifier.writeable)
+ .map((identifier) => identifier.name)
+
+ statements.push(
+ exportedTypeAlias(
+ 'QuantityTypeIdentifierReadOnly',
+ createLiteralUnion(quantityReadOnly),
+ ),
+ exportedTypeAlias(
+ 'QuantityTypeIdentifierWriteable',
+ createLiteralUnion(quantityWriteable),
+ ),
+ exportedTypeAlias(
+ 'QuantityTypeIdentifier',
+ FACTORY.createUnionTypeNode([
+ namedType('QuantityTypeIdentifierReadOnly'),
+ namedType('QuantityTypeIdentifierWriteable'),
+ ]),
+ ),
+ exportedTypeAlias(
+ 'CategoryTypeIdentifierReadOnly',
+ createLiteralUnion(categoryReadOnly),
+ ),
+ exportedTypeAlias(
+ 'CategoryTypeIdentifierWriteable',
+ createLiteralUnion(categoryWriteable),
+ ),
+ exportedTypeAlias(
+ 'CategoryTypeIdentifier',
+ FACTORY.createUnionTypeNode([
+ namedType('CategoryTypeIdentifierReadOnly'),
+ namedType('CategoryTypeIdentifierWriteable'),
+ ]),
+ ),
+ )
+
+ statements.push(
+ exportedConstObject(
+ 'QUANTITY_IDENTIFIER_IOS_AVAILABILITY',
+ schema.quantityIdentifiers.map((identifier) => ({
+ key: identifier.name,
+ value: identifier.ios,
+ })),
+ ),
+ exportedConstObject(
+ 'CATEGORY_IDENTIFIER_IOS_AVAILABILITY',
+ schema.categoryIdentifiers.map((identifier) => ({
+ key: identifier.name,
+ value: identifier.ios,
+ })),
+ ),
+ exportedConstObject(
+ 'QUANTITY_IDENTIFIER_CANONICAL_UNITS',
+ schema.quantityIdentifiers.map((identifier) => ({
+ key: identifier.name,
+ value: identifier.canonicalUnit,
+ })),
+ ),
+ exportedConstObject(
+ 'CATEGORY_IDENTIFIER_VALUE_ENUMS',
+ schema.categoryIdentifiers.map((identifier) => ({
+ key: identifier.name,
+ value: identifier.valueEnum ? toTsEnumName(identifier.valueEnum) : null,
+ })),
+ ),
+ )
+
+ statements.push(
+ ...schema.enums.map((schemaEnum) =>
+ FACTORY.createEnumDeclaration(
+ [EXPORT_MODIFIER],
+ schemaEnum.name,
+ schemaEnum.members.map((member) =>
+ FACTORY.createEnumMember(
+ member.name,
+ FACTORY.createNumericLiteral(member.rawValue),
+ ),
+ ),
+ ),
+ ),
+ )
+
+ const categoryValueMembers = schema.categoryIdentifiers
+ .filter((identifier) => identifier.valueEnum != null)
+ .map((identifier) => {
+ if (identifier.valueEnum == null) {
+ throw new Error(`Missing category value enum for ${identifier.name}`)
+ }
+ return readonlyProperty(
+ identifier.name,
+ namedType(toTsEnumName(identifier.valueEnum)),
+ false,
+ )
+ })
+ statements.push(
+ exportedInterface('CategoryValueByIdentifierMap', categoryValueMembers),
+ exportedTypeAlias(
+ 'CategoryValueForIdentifierGenerated',
+ FACTORY.createConditionalTypeNode(
+ namedType('T'),
+ createKeyofType('CategoryValueByIdentifierMap'),
+ FACTORY.createIndexedAccessTypeNode(
+ namedType('CategoryValueByIdentifierMap'),
+ namedType('T'),
+ ),
+ keywordType(ts.SyntaxKind.NumberKeyword),
+ ),
+ [
+ FACTORY.createTypeParameterDeclaration(
+ undefined,
+ 'T',
+ namedType('CategoryTypeIdentifier'),
+ namedType('CategoryTypeIdentifier'),
+ ),
+ ],
+ ),
+ )
+
+ const metadataByObjectType = {
+ common: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('common'),
+ ),
+ sample: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('sample'),
+ ),
+ categorySample: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('categorySample'),
+ ),
+ quantitySample: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('quantitySample'),
+ ),
+ workout: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('workout'),
+ ),
+ workoutEvent: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('workoutEvent'),
+ ),
+ }
+
+ const uniqueMetadataMembers = (
+ keys: readonly HealthkitSchema['metadataKeys'][number][],
+ ) =>
+ unique(keys.map((key) => key.rawKey)).map((rawKey) => {
+ const key = keys.find((entry) => entry.rawKey === rawKey)
+ if (key == null) {
+ throw new Error(`Missing metadata key for raw key ${rawKey}`)
+ }
+ return readonlyProperty(key.rawKey, namedType(key.tsType))
+ })
+
+ const quantityGlobalKeys = metadataByObjectType.quantitySample.filter(
+ (key) => key.identifiers.length === 0,
+ )
+ const quantitySpecificKeys = metadataByObjectType.quantitySample.filter(
+ (key) => key.identifiers.length > 0,
+ )
+ const categorySpecificKeys = metadataByObjectType.categorySample.filter(
+ (key) => key.identifiers.length > 0,
+ )
+
+ statements.push(
+ exportedInterface(
+ 'KnownObjectMetadata',
+ uniqueMetadataMembers(metadataByObjectType.common),
+ ),
+ exportedInterface(
+ 'KnownSampleMetadata',
+ uniqueMetadataMembers(metadataByObjectType.sample),
+ ['KnownObjectMetadata'],
+ ),
+ exportedInterface(
+ 'CategoryTypedMetadata',
+ uniqueMetadataMembers(metadataByObjectType.categorySample),
+ ['KnownSampleMetadata'],
+ ),
+ exportedInterface(
+ 'QuantityTypedMetadata',
+ uniqueMetadataMembers([...quantityGlobalKeys, ...quantitySpecificKeys]),
+ ['KnownSampleMetadata'],
+ ),
+ exportedInterface(
+ 'WorkoutTypedMetadata',
+ uniqueMetadataMembers(metadataByObjectType.workout),
+ ['KnownSampleMetadata'],
+ ),
+ exportedInterface(
+ 'WorkoutEventTypedMetadata',
+ uniqueMetadataMembers(metadataByObjectType.workoutEvent),
+ ),
+ )
+
+ const categorySpecificMap = new Map()
+ for (const key of categorySpecificKeys) {
+ for (const identifier of key.identifiers) {
+ const fields = categorySpecificMap.get(identifier) ?? []
+ fields.push(key.rawKey)
+ categorySpecificMap.set(identifier, fields)
+ }
+ }
+ const quantitySpecificMap = new Map()
+ for (const key of quantitySpecificKeys) {
+ for (const identifier of key.identifiers) {
+ const fields = quantitySpecificMap.get(identifier) ?? []
+ fields.push(key.rawKey)
+ quantitySpecificMap.set(identifier, fields)
+ }
+ }
+
+ statements.push(
+ exportedInterface(
+ 'CategorySpecificMetadataByIdentifierMap',
+ [...categorySpecificMap.entries()]
+ .sort(([left], [right]) => left.localeCompare(right))
+ .map(([identifier, fields]) =>
+ readonlyProperty(
+ identifier,
+ createPickType('CategoryTypedMetadata', fields.sort()),
+ false,
+ ),
+ ),
+ ),
+ createConditionalGeneratedType(
+ 'CategoryTypedMetadataForIdentifierGenerated',
+ 'KnownSampleMetadata',
+ 'CategorySpecificMetadataByIdentifierMap',
+ namedType('Record', [
+ keywordType(ts.SyntaxKind.StringKeyword),
+ keywordType(ts.SyntaxKind.NeverKeyword),
+ ]),
+ ),
+ exportedInterface(
+ 'QuantitySpecificMetadataByIdentifierMap',
+ [...quantitySpecificMap.entries()]
+ .sort(([left], [right]) => left.localeCompare(right))
+ .map(([identifier, fields]) =>
+ readonlyProperty(
+ identifier,
+ createPickType('QuantityTypedMetadata', fields.sort()),
+ false,
+ ),
+ ),
+ ),
+ exportedTypeAlias(
+ 'QuantityTypedMetadataForIdentifierGenerated',
+ FACTORY.createIntersectionTypeNode([
+ namedType('KnownSampleMetadata'),
+ namedType('Pick', [
+ namedType('QuantityTypedMetadata'),
+ createLiteralUnion(
+ quantityGlobalKeys.map((key) => key.rawKey).sort(),
+ ),
+ ]),
+ FACTORY.createParenthesizedType(
+ FACTORY.createConditionalTypeNode(
+ namedType('T'),
+ createKeyofType('QuantitySpecificMetadataByIdentifierMap'),
+ FACTORY.createIndexedAccessTypeNode(
+ namedType('QuantitySpecificMetadataByIdentifierMap'),
+ namedType('T'),
+ ),
+ namedType('Record', [
+ keywordType(ts.SyntaxKind.StringKeyword),
+ keywordType(ts.SyntaxKind.NeverKeyword),
+ ]),
+ ),
+ ),
+ ]),
+ [
+ FACTORY.createTypeParameterDeclaration(
+ undefined,
+ 'T',
+ namedType('QuantityTypeIdentifier'),
+ namedType('QuantityTypeIdentifier'),
+ ),
+ ],
+ ),
+ )
+
+ statements.push(
+ exportedInterface(
+ 'QuantityUnitByIdentifierMap',
+ schema.quantityIdentifiers.map((identifier) =>
+ readonlyProperty(
+ identifier.name,
+ canonicalUnitToTypeNode(identifier.canonicalUnit),
+ false,
+ ),
+ ),
+ ),
+ exportedTypeAlias(
+ 'UnitForIdentifierGenerated',
+ FACTORY.createConditionalTypeNode(
+ namedType('T'),
+ createKeyofType('QuantityUnitByIdentifierMap'),
+ FACTORY.createIndexedAccessTypeNode(
+ namedType('QuantityUnitByIdentifierMap'),
+ namedType('T'),
+ ),
+ keywordType(ts.SyntaxKind.StringKeyword),
+ ),
+ [
+ FACTORY.createTypeParameterDeclaration(
+ undefined,
+ 'T',
+ namedType('QuantityTypeIdentifier'),
+ namedType('QuantityTypeIdentifier'),
+ ),
+ ],
+ ),
+ )
+
+ const sourceFile = ts.factory.createSourceFile(
+ statements,
+ ts.factory.createToken(ts.SyntaxKind.EndOfFileToken),
+ ts.NodeFlags.None,
+ )
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed })
+ const printed = printer.printFile(sourceFile)
+ return `/*\n * AUTO-GENERATED FILE. DO NOT EDIT.\n * Source: scripts/generate-healthkit.ts\n */\n\n${printed}`
+}
+
+function renderMetadataValueKindMap(
+ name: string,
+ keys: readonly MetadataKeySchema[],
+): string {
+ const entries = unique(keys.map((key) => key.rawKey))
+ .sort((left, right) => left.localeCompare(right))
+ .map((rawKey) => {
+ const key = keys.find((entry) => entry.rawKey === rawKey)
+ if (key == null) {
+ throw new Error(`Missing metadata key for raw key ${rawKey}`)
+ }
+ return ` ${JSON.stringify(rawKey)}: ${JSON.stringify(key.valueKind)},`
+ })
+ .join('\n')
+
+ return `export const ${name} = {\n${entries}\n} as const`
+}
+
+function renderIdentifierMetadataKindMap(
+ name: string,
+ keys: readonly MetadataKeySchema[],
+): string {
+ const byIdentifier = new Map()
+
+ for (const key of keys) {
+ for (const identifier of key.identifiers) {
+ const existing = byIdentifier.get(identifier) ?? []
+ existing.push(key)
+ byIdentifier.set(identifier, existing)
+ }
+ }
+
+ const entries = [...byIdentifier.entries()]
+ .sort(([left], [right]) => left.localeCompare(right))
+ .map(([identifier, identifierKeys]) => {
+ const lines = unique(identifierKeys.map((key) => key.rawKey))
+ .sort((left, right) => left.localeCompare(right))
+ .map((rawKey) => {
+ const key = identifierKeys.find((entry) => entry.rawKey === rawKey)
+ if (key == null) {
+ throw new Error(`Missing metadata key for raw key ${rawKey}`)
+ }
+ return ` ${JSON.stringify(rawKey)}: ${JSON.stringify(key.valueKind)},`
+ })
+ .join('\n')
+
+ return ` ${JSON.stringify(identifier)}: {\n${lines}\n },`
+ })
+ .join('\n')
+
+ return `export const ${name} = {\n${entries}\n} as const`
+}
+
+export function renderGeneratedContracts(schema: HealthkitSchema): string {
+ const metadataByObjectType = {
+ common: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('common'),
+ ),
+ sample: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('sample'),
+ ),
+ categorySample: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('categorySample'),
+ ),
+ quantitySample: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('quantitySample'),
+ ),
+ workout: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('workout'),
+ ),
+ workoutEvent: schema.metadataKeys.filter((key) =>
+ key.objectTypes.includes('workoutEvent'),
+ ),
+ }
+
+ return tsSource`/*
+ * AUTO-GENERATED FILE. DO NOT EDIT.
+ * Source: scripts/generate-healthkit.ts
+ */
+
+import { z } from 'zod'
+import type { CategoryTypeIdentifier } from '@kingstinct/react-native-healthkit/types/CategoryTypeIdentifier'
+import type { QuantityTypeIdentifier } from '@kingstinct/react-native-healthkit/types/QuantityTypeIdentifier'
+
+export type ContractMetadataValueKind = 'string' | 'boolean' | 'number' | 'quantity' | 'enum'
+
+${renderMetadataValueKindMap(
+ 'KNOWN_OBJECT_METADATA_KIND_BY_KEY',
+ metadataByObjectType.common,
+)}
+
+${renderMetadataValueKindMap(
+ 'KNOWN_SAMPLE_METADATA_KIND_BY_KEY',
+ metadataByObjectType.sample,
+)}
+
+${renderMetadataValueKindMap(
+ 'KNOWN_WORKOUT_METADATA_KIND_BY_KEY',
+ metadataByObjectType.workout,
+)}
+
+${renderMetadataValueKindMap(
+ 'KNOWN_WORKOUT_EVENT_METADATA_KIND_BY_KEY',
+ metadataByObjectType.workoutEvent,
+)}
+
+${renderIdentifierMetadataKindMap(
+ 'KNOWN_CATEGORY_METADATA_KIND_BY_IDENTIFIER',
+ metadataByObjectType.categorySample.filter(
+ (key) => key.identifiers.length > 0,
+ ),
+)}
+
+${renderIdentifierMetadataKindMap(
+ 'KNOWN_QUANTITY_METADATA_KIND_BY_IDENTIFIER',
+ metadataByObjectType.quantitySample.filter(
+ (key) => key.identifiers.length > 0,
+ ),
+)}
+
+const CATEGORY_METADATA_KIND_LOOKUP = KNOWN_CATEGORY_METADATA_KIND_BY_IDENTIFIER as Readonly>>>
+const QUANTITY_METADATA_KIND_LOOKUP = KNOWN_QUANTITY_METADATA_KIND_BY_IDENTIFIER as Readonly>>>
+
+export const contractQuantitySchema = z
+ .object({
+ unit: z.string(),
+ quantity: z.number(),
+ })
+ .passthrough()
+
+export const contractSourceSchema = z
+ .object({
+ name: z.string(),
+ bundleIdentifier: z.string(),
+ })
+ .passthrough()
+
+export const contractSourceRevisionSchema = z
+ .object({
+ source: contractSourceSchema,
+ operatingSystemVersion: z.string(),
+ version: z.string().optional(),
+ productType: z.string().optional(),
+ })
+ .passthrough()
+
+export const contractDeviceSchema = z
+ .object({
+ name: z.string().optional(),
+ firmwareVersion: z.string().optional(),
+ hardwareVersion: z.string().optional(),
+ localIdentifier: z.string().optional(),
+ manufacturer: z.string().optional(),
+ model: z.string().optional(),
+ softwareVersion: z.string().optional(),
+ udiDeviceIdentifier: z.string().optional(),
+ })
+ .passthrough()
+
+export const contractSampleTypeSchema = z
+ .object({
+ identifier: z.string(),
+ allowsRecalibrationForEstimates: z.boolean(),
+ isMinimumDurationRestricted: z.boolean(),
+ isMaximumDurationRestricted: z.boolean(),
+ })
+ .passthrough()
+
+export const contractWorkoutActivitySchema = z
+ .object({
+ startDate: z.date(),
+ endDate: z.date(),
+ uuid: z.string(),
+ duration: z.number(),
+ })
+ .passthrough()
+
+function contractSchemaForMetadataKind(
+ kind: ContractMetadataValueKind,
+): z.ZodTypeAny {
+ switch (kind) {
+ case 'string':
+ return z.string()
+ case 'boolean':
+ return z.boolean()
+ case 'number':
+ case 'enum':
+ return z.number()
+ case 'quantity':
+ return contractQuantitySchema
+ }
+}
+
+function contractMetadataSchemaFromKinds(
+ kinds: Readonly>,
+) {
+ const shape: Record = {}
+
+ for (const [key, kind] of Object.entries(kinds)) {
+ shape[key] = contractSchemaForMetadataKind(kind).optional()
+ }
+
+ return z.object(shape).passthrough()
+}
+
+function mergeContractMetadataKinds(
+ ...sources: ReadonlyArray>>
+): Record {
+ return Object.assign({}, ...sources)
+}
+
+export const contractObjectMetadataSchema = contractMetadataSchemaFromKinds(
+ KNOWN_OBJECT_METADATA_KIND_BY_KEY,
+)
+export const contractSampleMetadataSchema = contractMetadataSchemaFromKinds(
+ mergeContractMetadataKinds(
+ KNOWN_OBJECT_METADATA_KIND_BY_KEY,
+ KNOWN_SAMPLE_METADATA_KIND_BY_KEY,
+ ),
+)
+export const contractWorkoutMetadataSchema = contractMetadataSchemaFromKinds(
+ mergeContractMetadataKinds(
+ KNOWN_OBJECT_METADATA_KIND_BY_KEY,
+ KNOWN_SAMPLE_METADATA_KIND_BY_KEY,
+ KNOWN_WORKOUT_METADATA_KIND_BY_KEY,
+ ),
+)
+export const contractWorkoutEventMetadataSchema =
+ contractMetadataSchemaFromKinds(KNOWN_WORKOUT_EVENT_METADATA_KIND_BY_KEY)
+
+const categoryMetadataSchemaLookup = Object.fromEntries(
+ Object.entries(CATEGORY_METADATA_KIND_LOOKUP).map(([identifier, kinds]) => [
+ identifier,
+ contractMetadataSchemaFromKinds(
+ mergeContractMetadataKinds(
+ KNOWN_OBJECT_METADATA_KIND_BY_KEY,
+ KNOWN_SAMPLE_METADATA_KIND_BY_KEY,
+ kinds,
+ ),
+ ),
+ ]),
+) as Record
+
+const quantityMetadataSchemaLookup = Object.fromEntries(
+ Object.entries(QUANTITY_METADATA_KIND_LOOKUP).map(([identifier, kinds]) => [
+ identifier,
+ contractMetadataSchemaFromKinds(
+ mergeContractMetadataKinds(
+ KNOWN_OBJECT_METADATA_KIND_BY_KEY,
+ KNOWN_SAMPLE_METADATA_KIND_BY_KEY,
+ kinds,
+ ),
+ ),
+ ]),
+) as Record
+
+export function getKnownCategoryMetadataKindMap(
+ identifier: CategoryTypeIdentifier,
+): Readonly> {
+ return CATEGORY_METADATA_KIND_LOOKUP[identifier] ?? {}
+}
+
+export function getKnownQuantityMetadataKindMap(
+ identifier: QuantityTypeIdentifier,
+): Readonly> {
+ return QUANTITY_METADATA_KIND_LOOKUP[identifier] ?? {}
+}
+
+export function getCategoryMetadataContractSchema(
+ identifier: CategoryTypeIdentifier,
+): z.ZodTypeAny {
+ return categoryMetadataSchemaLookup[identifier] ?? contractSampleMetadataSchema
+}
+
+export function getQuantityMetadataContractSchema(
+ identifier: QuantityTypeIdentifier,
+): z.ZodTypeAny {
+ return quantityMetadataSchemaLookup[identifier] ?? contractSampleMetadataSchema
+}
+
+function createBaseSampleContractSchema(metadataSchema: z.ZodTypeAny) {
+ return z
+ .object({
+ uuid: z.string(),
+ sourceRevision: contractSourceRevisionSchema,
+ device: contractDeviceSchema.optional(),
+ metadata: metadataSchema,
+ sampleType: contractSampleTypeSchema,
+ startDate: z.date(),
+ endDate: z.date(),
+ hasUndeterminedDuration: z.boolean(),
+ })
+ .passthrough()
+}
+
+export function getQuantitySampleContractSchema(
+ identifier: QuantityTypeIdentifier,
+) {
+ return createBaseSampleContractSchema(
+ getQuantityMetadataContractSchema(identifier),
+ )
+ .extend({
+ quantityType: z.literal(identifier),
+ quantity: z.number(),
+ unit: z.string(),
+ })
+ .passthrough()
+}
+
+export function getCategorySampleContractSchema(
+ identifier: CategoryTypeIdentifier,
+) {
+ return createBaseSampleContractSchema(
+ getCategoryMetadataContractSchema(identifier),
+ )
+ .extend({
+ categoryType: z.literal(identifier),
+ value: z.number(),
+ })
+ .passthrough()
+}
+
+export const contractWorkoutEventSchema = z
+ .object({
+ type: z.number(),
+ startDate: z.date(),
+ endDate: z.date(),
+ metadata: contractWorkoutEventMetadataSchema.optional(),
+ })
+ .passthrough()
+
+export const contractWorkoutSampleSchema = createBaseSampleContractSchema(
+ contractWorkoutMetadataSchema,
+)
+ .extend({
+ workoutActivityType: z.number(),
+ duration: contractQuantitySchema,
+ totalEnergyBurned: contractQuantitySchema.optional(),
+ totalDistance: contractQuantitySchema.optional(),
+ totalSwimmingStrokeCount: contractQuantitySchema.optional(),
+ totalFlightsClimbed: contractQuantitySchema.optional(),
+ events: z.array(contractWorkoutEventSchema).optional(),
+ activities: z.array(contractWorkoutActivitySchema).optional(),
+ })
+ .passthrough()
+`
+}
+
+export function renderGeneratedSwift(schema: HealthkitSchema): string {
+ const booleanKeys = unique(
+ schema.metadataKeys
+ .filter((key) => key.valueKind === 'boolean')
+ .map((key) => key.rawKey),
+ )
+ const numericKeys = unique(
+ schema.metadataKeys
+ .filter((key) => key.valueKind === 'number' || key.valueKind === 'enum')
+ .map((key) => key.rawKey),
+ )
+
+ const renderSetEntries = (keys: readonly string[]) =>
+ keys.map((key) => ` ${JSON.stringify(key)},`).join('\n')
+
+ return `// AUTO-GENERATED FILE. DO NOT EDIT.\n// Source: scripts/generate-healthkit.ts\n\nimport Foundation\n\nprivate let healthkitBooleanMetadataKeys: Set = [\n${renderSetEntries(booleanKeys)}\n]\n\nprivate let healthkitNumericMetadataKeys: Set = [\n${renderSetEntries(numericKeys)}\n]\n\nfunc isKnownBooleanMetadataKey(_ key: String) -> Bool {\n healthkitBooleanMetadataKeys.contains(key)\n}\n\nfunc isKnownNumericMetadataKey(_ key: String) -> Bool {\n healthkitNumericMetadataKeys.contains(key)\n}\n`
+}
diff --git a/packages/react-native-healthkit/scripts/healthkit-schema/identifier-overrides.json b/packages/react-native-healthkit/scripts/healthkit-schema/identifier-overrides.json
new file mode 100644
index 00000000..d9cb8ef7
--- /dev/null
+++ b/packages/react-native-healthkit/scripts/healthkit-schema/identifier-overrides.json
@@ -0,0 +1,20 @@
+{
+ "quantity": {
+ "readOnly": [
+ "HKQuantityTypeIdentifierWalkingHeartRateAverage",
+ "HKQuantityTypeIdentifierAtrialFibrillationBurden",
+ "HKQuantityTypeIdentifierAppleExerciseTime",
+ "HKQuantityTypeIdentifierAppleStandTime",
+ "HKQuantityTypeIdentifierAppleWalkingSteadiness"
+ ]
+ },
+ "category": {
+ "readOnly": [
+ "HKCategoryTypeIdentifierAppleStandHour",
+ "HKCategoryTypeIdentifierHighHeartRateEvent",
+ "HKCategoryTypeIdentifierLowHeartRateEvent",
+ "HKCategoryTypeIdentifierHeadphoneAudioExposureEvent",
+ "HKCategoryTypeIdentifierHypertensionEvent"
+ ]
+ }
+}
diff --git a/packages/react-native-healthkit/scripts/healthkit-sdk.ts b/packages/react-native-healthkit/scripts/healthkit-sdk.ts
new file mode 100644
index 00000000..0e2140bc
--- /dev/null
+++ b/packages/react-native-healthkit/scripts/healthkit-sdk.ts
@@ -0,0 +1,221 @@
+import { execFileSync } from 'node:child_process'
+import {
+ mkdirSync,
+ mkdtempSync,
+ readFileSync,
+ rmSync,
+ writeFileSync,
+} from 'node:fs'
+import { tmpdir } from 'node:os'
+import { dirname, join, resolve } from 'node:path'
+import type {
+ HealthkitSchema,
+ IdentifierOverrides,
+ SymbolGraphDocument,
+} from './generate-healthkit'
+
+const ROOT = resolve(dirname(new URL(import.meta.url).pathname), '..')
+export const DEFAULT_GENERATED_TS_PATH = join(
+ ROOT,
+ 'src/generated/healthkit.generated.ts',
+)
+export const DEFAULT_GENERATED_SCHEMA_PATH = join(
+ ROOT,
+ 'src/generated/healthkit-schema.json',
+)
+export const DEFAULT_GENERATED_CONTRACT_TS_PATH = join(
+ ROOT,
+ '..',
+ '..',
+ 'apps/example/contracts/generated/healthkit.contract.generated.ts',
+)
+export const DEFAULT_GENERATED_SWIFT_PATH = join(
+ ROOT,
+ 'ios/generated/HealthkitGenerated.swift',
+)
+const IDENTIFIER_OVERRIDES_PATH = join(
+ ROOT,
+ 'scripts/healthkit-schema/identifier-overrides.json',
+)
+
+export interface HealthkitSdkSources {
+ readonly sdkPath: string
+ readonly symbolGraph: SymbolGraphDocument
+ readonly identifierOverrides: IdentifierOverrides
+ readonly typeIdentifiersHeader: string
+ readonly categoryValuesHeader: string
+ readonly metadataHeader: string
+ readonly metadataEnumsHeader: string
+ readonly workoutHeader: string
+}
+
+export interface GeneratedArtifactPaths {
+ readonly typescriptPath: string
+ readonly schemaPath: string
+ readonly contractTypescriptPath: string
+ readonly swiftPath: string
+}
+
+export function getGeneratedArtifactPaths(
+ overrides: Partial = {},
+): GeneratedArtifactPaths {
+ return {
+ typescriptPath:
+ overrides.typescriptPath ??
+ process.env.HEALTHKIT_GENERATED_TS_PATH ??
+ DEFAULT_GENERATED_TS_PATH,
+ schemaPath:
+ overrides.schemaPath ??
+ process.env.HEALTHKIT_GENERATED_SCHEMA_PATH ??
+ DEFAULT_GENERATED_SCHEMA_PATH,
+ contractTypescriptPath:
+ overrides.contractTypescriptPath ??
+ process.env.HEALTHKIT_GENERATED_CONTRACT_TS_PATH ??
+ DEFAULT_GENERATED_CONTRACT_TS_PATH,
+ swiftPath:
+ overrides.swiftPath ??
+ process.env.HEALTHKIT_GENERATED_SWIFT_PATH ??
+ DEFAULT_GENERATED_SWIFT_PATH,
+ }
+}
+
+export function getSdkPath(): string {
+ if (process.env.HEALTHKIT_SDK_PATH) {
+ return process.env.HEALTHKIT_SDK_PATH
+ }
+
+ return execFileSync(
+ 'xcrun',
+ ['--sdk', 'iphonesimulator', '--show-sdk-path'],
+ {
+ encoding: 'utf8',
+ },
+ ).trim()
+}
+
+export function readIdentifierOverrides(): IdentifierOverrides {
+ return JSON.parse(
+ readFileSync(IDENTIFIER_OVERRIDES_PATH, 'utf8'),
+ ) as IdentifierOverrides
+}
+
+function readHealthkitHeader(relativePath: string, sdkPath: string): string {
+ return readFileSync(
+ join(
+ sdkPath,
+ 'System/Library/Frameworks/HealthKit.framework',
+ relativePath,
+ ),
+ 'utf8',
+ )
+}
+
+function readHealthkitSymbolGraph(sdkPath: string): SymbolGraphDocument {
+ const outputDir = mkdtempSync(join(tmpdir(), 'healthkit-symbolgraph-'))
+
+ try {
+ execFileSync(
+ 'swift',
+ [
+ 'symbolgraph-extract',
+ '-module-name',
+ 'HealthKit',
+ '-target',
+ 'arm64-apple-ios17.0-simulator',
+ '-sdk',
+ sdkPath,
+ '-output-dir',
+ outputDir,
+ ],
+ {
+ cwd: ROOT,
+ },
+ )
+
+ return JSON.parse(
+ readFileSync(join(outputDir, 'HealthKit.symbols.json'), 'utf8'),
+ ) as SymbolGraphDocument
+ } finally {
+ rmSync(outputDir, { recursive: true, force: true })
+ }
+}
+
+export function loadHealthkitSdkSources(
+ sdkPath = getSdkPath(),
+): HealthkitSdkSources {
+ return {
+ sdkPath,
+ symbolGraph: readHealthkitSymbolGraph(sdkPath),
+ identifierOverrides: readIdentifierOverrides(),
+ typeIdentifiersHeader: readHealthkitHeader(
+ 'Headers/HKTypeIdentifiers.h',
+ sdkPath,
+ ),
+ categoryValuesHeader: readHealthkitHeader(
+ 'Headers/HKCategoryValues.h',
+ sdkPath,
+ ),
+ metadataHeader: readHealthkitHeader('Headers/HKMetadata.h', sdkPath),
+ metadataEnumsHeader: readHealthkitHeader(
+ 'Headers/HKMetadataEnums.h',
+ sdkPath,
+ ),
+ workoutHeader: readHealthkitHeader('Headers/HKWorkout.h', sdkPath),
+ }
+}
+
+export function writeGeneratedArtifacts(
+ schema: HealthkitSchema,
+ renderedTypescript: string,
+ renderedContracts: string,
+ renderedSwift: string,
+ paths = getGeneratedArtifactPaths(),
+) {
+ mkdirSync(dirname(paths.typescriptPath), { recursive: true })
+ mkdirSync(dirname(paths.swiftPath), { recursive: true })
+ mkdirSync(dirname(paths.contractTypescriptPath), { recursive: true })
+
+ writeFileSync(
+ paths.schemaPath,
+ `${JSON.stringify(schema, null, 2)}\n`,
+ 'utf8',
+ )
+ writeFileSync(paths.typescriptPath, renderedTypescript, 'utf8')
+ writeFileSync(paths.contractTypescriptPath, renderedContracts, 'utf8')
+ writeFileSync(paths.swiftPath, renderedSwift, 'utf8')
+}
+
+export function formatGeneratedArtifacts(paths = getGeneratedArtifactPaths()) {
+ execFileSync(
+ 'bunx',
+ [
+ '@biomejs/biome',
+ 'check',
+ '--write',
+ paths.typescriptPath,
+ paths.contractTypescriptPath,
+ paths.schemaPath,
+ paths.swiftPath,
+ ],
+ {
+ stdio: 'inherit',
+ cwd: ROOT,
+ },
+ )
+ execFileSync(
+ 'bunx',
+ [
+ '@biomejs/biome',
+ 'format',
+ '--write',
+ paths.typescriptPath,
+ paths.contractTypescriptPath,
+ paths.schemaPath,
+ paths.swiftPath,
+ ],
+ {
+ stdio: 'inherit',
+ cwd: ROOT,
+ },
+ )
+}
diff --git a/packages/react-native-healthkit/scripts/verify-healthkit-sdk.ts b/packages/react-native-healthkit/scripts/verify-healthkit-sdk.ts
new file mode 100644
index 00000000..46f3732b
--- /dev/null
+++ b/packages/react-native-healthkit/scripts/verify-healthkit-sdk.ts
@@ -0,0 +1,140 @@
+import { strict as assert } from 'node:assert'
+import { buildHealthkitSchemaFromSources } from './generate-healthkit'
+import { loadHealthkitSdkSources } from './healthkit-sdk'
+
+function findMetadataKey(
+ schema: ReturnType,
+ keyConstant: string,
+) {
+ const metadataKey = schema.metadataKeys.find(
+ (entry) => entry.keyConstant === keyConstant,
+ )
+
+ assert.ok(metadataKey, `Missing metadata key ${keyConstant}`)
+ return metadataKey
+}
+
+function main() {
+ const sources = loadHealthkitSdkSources()
+ const schema = buildHealthkitSchemaFromSources(sources)
+
+ const sleepAnalysis = schema.categoryIdentifiers.find(
+ (identifier) => identifier.name === 'HKCategoryTypeIdentifierSleepAnalysis',
+ )
+ assert.equal(
+ sleepAnalysis?.valueEnum,
+ 'HKCategoryValueSleepAnalysis',
+ 'Sleep analysis should map to HKCategoryValueSleepAnalysis',
+ )
+
+ const hypertensionEvent = schema.categoryIdentifiers.find(
+ (identifier) =>
+ identifier.name === 'HKCategoryTypeIdentifierHypertensionEvent',
+ )
+ assert.ok(
+ hypertensionEvent,
+ 'Pinned SDK should expose HKCategoryTypeIdentifierHypertensionEvent',
+ )
+
+ const bloodGlucose = schema.quantityIdentifiers.find(
+ (identifier) => identifier.name === 'HKQuantityTypeIdentifierBloodGlucose',
+ )
+ assert.equal(
+ bloodGlucose?.canonicalUnit,
+ 'mg/dL',
+ 'Blood glucose should retain canonical unit mapping',
+ )
+
+ const workoutBrandName = findMetadataKey(
+ schema,
+ 'HKMetadataKeyWorkoutBrandName',
+ )
+ assert.equal(workoutBrandName.tsType, 'string')
+ assert.ok(workoutBrandName.objectTypes.includes('workout'))
+
+ const swimmingStrokeStyle = findMetadataKey(
+ schema,
+ 'HKMetadataKeySwimmingStrokeStyle',
+ )
+ assert.equal(swimmingStrokeStyle.enumName, 'SwimmingStrokeStyle')
+ assert.ok(swimmingStrokeStyle.objectTypes.includes('workoutEvent'))
+
+ const heartRateMotionContext = findMetadataKey(
+ schema,
+ 'HKMetadataKeyHeartRateMotionContext',
+ )
+ assert.equal(heartRateMotionContext.enumName, 'HeartRateMotionContext')
+ assert.ok(heartRateMotionContext.objectTypes.includes('quantitySample'))
+
+ const heartRateEventThreshold = findMetadataKey(
+ schema,
+ 'HKMetadataKeyHeartRateEventThreshold',
+ )
+ assert.equal(heartRateEventThreshold.tsType, 'Quantity')
+ assert.ok(
+ heartRateEventThreshold.identifiers.includes(
+ 'HKCategoryTypeIdentifierHighHeartRateEvent',
+ ),
+ )
+
+ const audioExposureLevel = findMetadataKey(
+ schema,
+ 'HKMetadataKeyAudioExposureLevel',
+ )
+ assert.equal(audioExposureLevel.tsType, 'Quantity')
+ assert.ok(audioExposureLevel.objectTypes.includes('categorySample'))
+
+ const appleDeviceCalibrated = findMetadataKey(
+ schema,
+ 'HKMetadataKeyAppleDeviceCalibrated',
+ )
+ assert.equal(appleDeviceCalibrated.tsType, 'boolean')
+ assert.ok(appleDeviceCalibrated.objectTypes.includes('sample'))
+
+ const vo2MaxValue = findMetadataKey(schema, 'HKMetadataKeyVO2MaxValue')
+ assert.equal(vo2MaxValue.tsType, 'Quantity')
+ assert.ok(
+ vo2MaxValue.identifiers.includes(
+ 'HKCategoryTypeIdentifierLowCardioFitnessEvent',
+ ),
+ )
+
+ const lowCardioFitnessEventThreshold = findMetadataKey(
+ schema,
+ 'HKMetadataKeyLowCardioFitnessEventThreshold',
+ )
+ assert.equal(lowCardioFitnessEventThreshold.tsType, 'Quantity')
+
+ const earliestEstimateDate = findMetadataKey(
+ schema,
+ 'HKMetadataKeyDateOfEarliestDataUsedForEstimate',
+ )
+ assert.equal(earliestEstimateDate.tsType, 'string')
+
+ const quantityClampedToLowerBound = findMetadataKey(
+ schema,
+ 'HKMetadataKeyQuantityClampedToLowerBound',
+ )
+ assert.equal(quantityClampedToLowerBound.tsType, 'boolean')
+
+ const quantityClampedToUpperBound = findMetadataKey(
+ schema,
+ 'HKMetadataKeyQuantityClampedToUpperBound',
+ )
+ assert.equal(quantityClampedToUpperBound.tsType, 'boolean')
+
+ const glassesPrescriptionDescription = findMetadataKey(
+ schema,
+ 'HKMetadataKeyGlassesPrescriptionDescription',
+ )
+ assert.equal(glassesPrescriptionDescription.tsType, 'string')
+
+ const headphoneGain = findMetadataKey(schema, 'HKMetadataKeyHeadphoneGain')
+ assert.equal(headphoneGain.tsType, 'Quantity')
+
+ process.stdout.write(
+ `Verified HealthKit SDK-backed schema invariants using ${sources.sdkPath}\n`,
+ )
+}
+
+main()
diff --git a/packages/react-native-healthkit/src/generated/healthkit-schema.json b/packages/react-native-healthkit/src/generated/healthkit-schema.json
new file mode 100644
index 00000000..c858935e
--- /dev/null
+++ b/packages/react-native-healthkit/src/generated/healthkit-schema.json
@@ -0,0 +1,3680 @@
+{
+ "quantityIdentifiers": [
+ {
+ "name": "HKQuantityTypeIdentifierActiveEnergyBurned",
+ "ios": "8.0",
+ "canonicalUnit": "kcal",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierAppleExerciseTime",
+ "ios": "9.3",
+ "canonicalUnit": "min",
+ "aggregationStyle": "Cumulative",
+ "writeable": false,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierAppleMoveTime",
+ "ios": "14.5",
+ "canonicalUnit": "min",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierAppleSleepingBreathingDisturbances",
+ "ios": "18.0",
+ "canonicalUnit": "count",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierAppleSleepingWristTemperature",
+ "ios": "16.0",
+ "canonicalUnit": "degC",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierAppleStandTime",
+ "ios": "13.0",
+ "canonicalUnit": "min",
+ "aggregationStyle": "Cumulative",
+ "writeable": false,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierAppleWalkingSteadiness",
+ "ios": "15.0",
+ "canonicalUnit": "%",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": false,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierAtrialFibrillationBurden",
+ "ios": "16.0",
+ "canonicalUnit": "%",
+ "aggregationStyle": "Discrete (Temporally Weighted)",
+ "writeable": false,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierBasalBodyTemperature",
+ "ios": "9.0",
+ "canonicalUnit": "degC",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierBasalEnergyBurned",
+ "ios": "8.0",
+ "canonicalUnit": "kcal",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierBloodAlcoholContent",
+ "ios": "8.0",
+ "canonicalUnit": "%",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierBloodGlucose",
+ "ios": "8.0",
+ "canonicalUnit": "mg/dL",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierBloodPressureDiastolic",
+ "ios": "8.0",
+ "canonicalUnit": "mmHg",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierBloodPressureSystolic",
+ "ios": "8.0",
+ "canonicalUnit": "mmHg",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierBodyFatPercentage",
+ "ios": "8.0",
+ "canonicalUnit": "%",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierBodyMass",
+ "ios": "8.0",
+ "canonicalUnit": "kg",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierBodyMassIndex",
+ "ios": "8.0",
+ "canonicalUnit": "count",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierBodyTemperature",
+ "ios": "8.0",
+ "canonicalUnit": "degC",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierCrossCountrySkiingSpeed",
+ "ios": "18.0",
+ "canonicalUnit": "m/s",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierCyclingCadence",
+ "ios": "17.0",
+ "canonicalUnit": "count/min",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierCyclingFunctionalThresholdPower",
+ "ios": "17.0",
+ "canonicalUnit": "W",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierCyclingPower",
+ "ios": "17.0",
+ "canonicalUnit": "W",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierCyclingSpeed",
+ "ios": "17.0",
+ "canonicalUnit": "m/s",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryBiotin",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryCaffeine",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryCalcium",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryCarbohydrates",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryChloride",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryCholesterol",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryChromium",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryCopper",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryEnergyConsumed",
+ "ios": "8.0",
+ "canonicalUnit": "kcal",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryFatMonounsaturated",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryFatPolyunsaturated",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryFatSaturated",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryFatTotal",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryFiber",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryFolate",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryIodine",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryIron",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryMagnesium",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryManganese",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryMolybdenum",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryNiacin",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryPantothenicAcid",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryPhosphorus",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryPotassium",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryProtein",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryRiboflavin",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietarySelenium",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietarySodium",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietarySugar",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryThiamin",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryVitaminA",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryVitaminB12",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryVitaminB6",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryVitaminC",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryVitaminD",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryVitaminE",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryVitaminK",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryWater",
+ "ios": "9.0",
+ "canonicalUnit": "mL",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDietaryZinc",
+ "ios": "8.0",
+ "canonicalUnit": "g",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDistanceCrossCountrySkiing",
+ "ios": "18.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDistanceCycling",
+ "ios": "8.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDistanceDownhillSnowSports",
+ "ios": "11.2",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDistancePaddleSports",
+ "ios": "18.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDistanceRowing",
+ "ios": "18.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDistanceSkatingSports",
+ "ios": "18.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDistanceSwimming",
+ "ios": "10.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDistanceWalkingRunning",
+ "ios": "8.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierDistanceWheelchair",
+ "ios": "10.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierElectrodermalActivity",
+ "ios": "8.0",
+ "canonicalUnit": "S",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierEnvironmentalAudioExposure",
+ "ios": "13.0",
+ "canonicalUnit": "dBASPL",
+ "aggregationStyle": "Discrete (Equivalent Continuous Level)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierEnvironmentalSoundReduction",
+ "ios": "16.0",
+ "canonicalUnit": "dBASPL",
+ "aggregationStyle": "Discrete (Equivalent Continuous Level)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierEstimatedWorkoutEffortScore",
+ "ios": "18.0",
+ "canonicalUnit": "appleEffortScore",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierFlightsClimbed",
+ "ios": "8.0",
+ "canonicalUnit": "count",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierForcedExpiratoryVolume1",
+ "ios": "8.0",
+ "canonicalUnit": "L",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierForcedVitalCapacity",
+ "ios": "8.0",
+ "canonicalUnit": "L",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierHeadphoneAudioExposure",
+ "ios": "13.0",
+ "canonicalUnit": "dBASPL",
+ "aggregationStyle": "Discrete (Equivalent Continuous Level)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierHeartRate",
+ "ios": "8.0",
+ "canonicalUnit": "count/s",
+ "aggregationStyle": "Discrete (Temporally Weighted)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierHeartRateRecoveryOneMinute",
+ "ios": "16.0",
+ "canonicalUnit": "count/min",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierHeartRateVariabilitySDNN",
+ "ios": "11.0",
+ "canonicalUnit": "ms",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierHeight",
+ "ios": "8.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierInhalerUsage",
+ "ios": "8.0",
+ "canonicalUnit": "count",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierInsulinDelivery",
+ "ios": "11.0",
+ "canonicalUnit": "IU",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierLeanBodyMass",
+ "ios": "8.0",
+ "canonicalUnit": "kg",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierNikeFuel",
+ "ios": "8.0",
+ "canonicalUnit": "count",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierNumberOfAlcoholicBeverages",
+ "ios": "15.0",
+ "canonicalUnit": "count",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierNumberOfTimesFallen",
+ "ios": "8.0",
+ "canonicalUnit": "count",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierOxygenSaturation",
+ "ios": "8.0",
+ "canonicalUnit": "%",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierPaddleSportsSpeed",
+ "ios": "18.0",
+ "canonicalUnit": "m/s",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierPeakExpiratoryFlowRate",
+ "ios": "8.0",
+ "canonicalUnit": "L/min",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierPeripheralPerfusionIndex",
+ "ios": "8.0",
+ "canonicalUnit": "%",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierPhysicalEffort",
+ "ios": "17.0",
+ "canonicalUnit": "kcal/(kg*hr)",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierPushCount",
+ "ios": "10.0",
+ "canonicalUnit": "count",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierRespiratoryRate",
+ "ios": "8.0",
+ "canonicalUnit": "count/s",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierRestingHeartRate",
+ "ios": "11.0",
+ "canonicalUnit": "count/min",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierRowingSpeed",
+ "ios": "18.0",
+ "canonicalUnit": "m/s",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierRunningGroundContactTime",
+ "ios": "16.0",
+ "canonicalUnit": "ms",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierRunningPower",
+ "ios": "16.0",
+ "canonicalUnit": "W",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierRunningSpeed",
+ "ios": "16.0",
+ "canonicalUnit": "m/s",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierRunningStrideLength",
+ "ios": "16.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierRunningVerticalOscillation",
+ "ios": "16.0",
+ "canonicalUnit": "cm",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierSixMinuteWalkTestDistance",
+ "ios": "14.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierStairAscentSpeed",
+ "ios": "14.0",
+ "canonicalUnit": "m/s",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierStairDescentSpeed",
+ "ios": "14.0",
+ "canonicalUnit": "m/s",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierStepCount",
+ "ios": "8.0",
+ "canonicalUnit": "count",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierSwimmingStrokeCount",
+ "ios": "10.0",
+ "canonicalUnit": "count",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierTimeInDaylight",
+ "ios": "17.0",
+ "canonicalUnit": "min",
+ "aggregationStyle": "Cumulative",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierUnderwaterDepth",
+ "ios": "16.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierUVExposure",
+ "ios": "9.0",
+ "canonicalUnit": null,
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierVO2Max",
+ "ios": "11.0",
+ "canonicalUnit": "ml/(kg*min)",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierWaistCircumference",
+ "ios": "11.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierWalkingAsymmetryPercentage",
+ "ios": "14.0",
+ "canonicalUnit": "%",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierWalkingDoubleSupportPercentage",
+ "ios": "14.0",
+ "canonicalUnit": "%",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierWalkingHeartRateAverage",
+ "ios": "11.0",
+ "canonicalUnit": "count/min",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": false,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierWalkingSpeed",
+ "ios": "14.0",
+ "canonicalUnit": "m/s",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierWalkingStepLength",
+ "ios": "14.0",
+ "canonicalUnit": "m",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierWaterTemperature",
+ "ios": "16.0",
+ "canonicalUnit": "degC",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKQuantityTypeIdentifierWorkoutEffortScore",
+ "ios": "18.0",
+ "canonicalUnit": "appleEffortScore",
+ "aggregationStyle": "Discrete (Arithmetic)",
+ "writeable": true,
+ "legacy": false
+ }
+ ],
+ "categoryIdentifiers": [
+ {
+ "name": "HKCategoryTypeIdentifierAbdominalCramps",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierAcne",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierAppetiteChanges",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueAppetiteChanges",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierAppleStandHour",
+ "ios": "9.0",
+ "valueEnum": "HKCategoryValueAppleStandHour",
+ "writeable": false,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierAppleWalkingSteadinessEvent",
+ "ios": "15.0",
+ "valueEnum": "HKCategoryValueAppleWalkingSteadinessEvent",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierAudioExposureEvent",
+ "ios": "13.0",
+ "valueEnum": null,
+ "writeable": true,
+ "legacy": true
+ },
+ {
+ "name": "HKCategoryTypeIdentifierBladderIncontinence",
+ "ios": "14.0",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierBleedingAfterPregnancy",
+ "ios": "18.0",
+ "valueEnum": "HKCategoryValueVaginalBleeding",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierBleedingDuringPregnancy",
+ "ios": "18.0",
+ "valueEnum": "HKCategoryValueVaginalBleeding",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierBloating",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierBreastPain",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierCervicalMucusQuality",
+ "ios": "9.0",
+ "valueEnum": "HKCategoryValueCervicalMucusQuality",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierChestTightnessOrPain",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierChills",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierConstipation",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierContraceptive",
+ "ios": "14.3",
+ "valueEnum": "HKCategoryValueContraceptive",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierCoughing",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierDiarrhea",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierDizziness",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierDrySkin",
+ "ios": "14.0",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierEnvironmentalAudioExposureEvent",
+ "ios": "14.0",
+ "valueEnum": "HKCategoryValueEnvironmentalAudioExposureEvent",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierFainting",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierFatigue",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierFever",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierGeneralizedBodyAche",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierHairLoss",
+ "ios": "14.0",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierHandwashingEvent",
+ "ios": "14.0",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierHeadache",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierHeadphoneAudioExposureEvent",
+ "ios": "14.2",
+ "valueEnum": "HKCategoryValueHeadphoneAudioExposureEvent",
+ "writeable": false,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierHeartburn",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierHighHeartRateEvent",
+ "ios": "12.2",
+ "valueEnum": "HKCategoryValue",
+ "writeable": false,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierHotFlashes",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierHypertensionEvent",
+ "ios": "26.2",
+ "valueEnum": "HKCategoryValue",
+ "writeable": false,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierInfrequentMenstrualCycles",
+ "ios": "16.0",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierIntermenstrualBleeding",
+ "ios": "9.0",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierIrregularHeartRhythmEvent",
+ "ios": "12.2",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierIrregularMenstrualCycles",
+ "ios": "16.0",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierLactation",
+ "ios": "14.3",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierLossOfSmell",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierLossOfTaste",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierLowCardioFitnessEvent",
+ "ios": "14.3",
+ "valueEnum": "HKCategoryValueLowCardioFitnessEvent",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierLowerBackPain",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierLowHeartRateEvent",
+ "ios": "12.2",
+ "valueEnum": "HKCategoryValue",
+ "writeable": false,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierMemoryLapse",
+ "ios": "14.0",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierMenstrualFlow",
+ "ios": "9.0",
+ "valueEnum": "HKCategoryValueMenstrualFlow",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierMindfulSession",
+ "ios": "10.0",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierMoodChanges",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValuePresence",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierNausea",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierNightSweats",
+ "ios": "14.0",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierOvulationTestResult",
+ "ios": "9.0",
+ "valueEnum": "HKCategoryValueOvulationTestResult",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierPelvicPain",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierPersistentIntermenstrualBleeding",
+ "ios": "16.0",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierPregnancy",
+ "ios": "14.3",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierPregnancyTestResult",
+ "ios": "15.0",
+ "valueEnum": "HKCategoryValuePregnancyTestResult",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierProgesteroneTestResult",
+ "ios": "15.0",
+ "valueEnum": "HKCategoryValueProgesteroneTestResult",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierProlongedMenstrualPeriods",
+ "ios": "16.0",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierRapidPoundingOrFlutteringHeartbeat",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierRunnyNose",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierSexualActivity",
+ "ios": "9.0",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierShortnessOfBreath",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierSinusCongestion",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierSkippedHeartbeat",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierSleepAnalysis",
+ "ios": "8.0",
+ "valueEnum": "HKCategoryValueSleepAnalysis",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierSleepApneaEvent",
+ "ios": "18.0",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierSleepChanges",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValuePresence",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierSoreThroat",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierToothbrushingEvent",
+ "ios": "13.0",
+ "valueEnum": "HKCategoryValue",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierVaginalDryness",
+ "ios": "14.0",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierVomiting",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ },
+ {
+ "name": "HKCategoryTypeIdentifierWheezing",
+ "ios": "13.6",
+ "valueEnum": "HKCategoryValueSeverity",
+ "writeable": true,
+ "legacy": false
+ }
+ ],
+ "enums": [
+ {
+ "name": "AppleECGAlgorithmVersion",
+ "ios": "14.0",
+ "members": [
+ {
+ "name": "value1",
+ "swiftName": "HKAppleECGAlgorithmVersion1",
+ "rawValue": 1
+ },
+ {
+ "name": "value2",
+ "swiftName": "HKAppleECGAlgorithmVersion2",
+ "rawValue": 2
+ }
+ ]
+ },
+ {
+ "name": "BloodGlucoseMealTime",
+ "ios": "11.0",
+ "members": [
+ {
+ "name": "preprandial",
+ "swiftName": "HKBloodGlucoseMealTimePreprandial",
+ "rawValue": 1
+ },
+ {
+ "name": "postprandial",
+ "swiftName": "HKBloodGlucoseMealTimePostprandial",
+ "rawValue": 2
+ }
+ ]
+ },
+ {
+ "name": "BodyTemperatureSensorLocation",
+ "ios": "8.0",
+ "members": [
+ {
+ "name": "other",
+ "swiftName": "HKBodyTemperatureSensorLocationOther",
+ "rawValue": 0
+ },
+ {
+ "name": "armpit",
+ "swiftName": "HKBodyTemperatureSensorLocationArmpit",
+ "rawValue": 1
+ },
+ {
+ "name": "body",
+ "swiftName": "HKBodyTemperatureSensorLocationBody",
+ "rawValue": 2
+ },
+ {
+ "name": "ear",
+ "swiftName": "HKBodyTemperatureSensorLocationEar",
+ "rawValue": 3
+ },
+ {
+ "name": "finger",
+ "swiftName": "HKBodyTemperatureSensorLocationFinger",
+ "rawValue": 4
+ },
+ {
+ "name": "gastroIntestinal",
+ "swiftName": "HKBodyTemperatureSensorLocationGastroIntestinal",
+ "rawValue": 5
+ },
+ {
+ "name": "mouth",
+ "swiftName": "HKBodyTemperatureSensorLocationMouth",
+ "rawValue": 6
+ },
+ {
+ "name": "rectum",
+ "swiftName": "HKBodyTemperatureSensorLocationRectum",
+ "rawValue": 7
+ },
+ {
+ "name": "toe",
+ "swiftName": "HKBodyTemperatureSensorLocationToe",
+ "rawValue": 8
+ },
+ {
+ "name": "earDrum",
+ "swiftName": "HKBodyTemperatureSensorLocationEarDrum",
+ "rawValue": 9
+ },
+ {
+ "name": "temporalArtery",
+ "swiftName": "HKBodyTemperatureSensorLocationTemporalArtery",
+ "rawValue": 10
+ },
+ {
+ "name": "forehead",
+ "swiftName": "HKBodyTemperatureSensorLocationForehead",
+ "rawValue": 11
+ }
+ ]
+ },
+ {
+ "name": "CategoryValue",
+ "ios": "9.0",
+ "members": [
+ {
+ "name": "notApplicable",
+ "swiftName": "HKCategoryValueNotApplicable",
+ "rawValue": 0
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueAppetiteChanges",
+ "ios": "13.6",
+ "members": [
+ {
+ "name": "unspecified",
+ "swiftName": "HKCategoryValueAppetiteChangesUnspecified",
+ "rawValue": 0
+ },
+ {
+ "name": "noChange",
+ "swiftName": "HKCategoryValueAppetiteChangesNoChange",
+ "rawValue": 1
+ },
+ {
+ "name": "decreased",
+ "swiftName": "HKCategoryValueAppetiteChangesDecreased",
+ "rawValue": 2
+ },
+ {
+ "name": "increased",
+ "swiftName": "HKCategoryValueAppetiteChangesIncreased",
+ "rawValue": 3
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueAppleStandHour",
+ "ios": "9.0",
+ "members": [
+ {
+ "name": "stood",
+ "swiftName": "HKCategoryValueAppleStandHourStood",
+ "rawValue": 0
+ },
+ {
+ "name": "idle",
+ "swiftName": "HKCategoryValueAppleStandHourIdle",
+ "rawValue": 1
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueAppleWalkingSteadinessEvent",
+ "ios": "15.0",
+ "members": [
+ {
+ "name": "initialLow",
+ "swiftName": "HKCategoryValueAppleWalkingSteadinessEventInitialLow",
+ "rawValue": 1
+ },
+ {
+ "name": "initialVeryLow",
+ "swiftName": "HKCategoryValueAppleWalkingSteadinessEventInitialVeryLow",
+ "rawValue": 2
+ },
+ {
+ "name": "repeatLow",
+ "swiftName": "HKCategoryValueAppleWalkingSteadinessEventRepeatLow",
+ "rawValue": 3
+ },
+ {
+ "name": "repeatVeryLow",
+ "swiftName": "HKCategoryValueAppleWalkingSteadinessEventRepeatVeryLow",
+ "rawValue": 4
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueAudioExposureEvent",
+ "ios": "13.0",
+ "members": [
+ {
+ "name": "loudEnvironment",
+ "swiftName": "HKCategoryValueAudioExposureEventLoudEnvironment",
+ "rawValue": 1
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueCervicalMucusQuality",
+ "ios": "9.0",
+ "members": [
+ {
+ "name": "dry",
+ "swiftName": "HKCategoryValueCervicalMucusQualityDry",
+ "rawValue": 1
+ },
+ {
+ "name": "sticky",
+ "swiftName": "HKCategoryValueCervicalMucusQualitySticky",
+ "rawValue": 2
+ },
+ {
+ "name": "creamy",
+ "swiftName": "HKCategoryValueCervicalMucusQualityCreamy",
+ "rawValue": 3
+ },
+ {
+ "name": "watery",
+ "swiftName": "HKCategoryValueCervicalMucusQualityWatery",
+ "rawValue": 4
+ },
+ {
+ "name": "eggWhite",
+ "swiftName": "HKCategoryValueCervicalMucusQualityEggWhite",
+ "rawValue": 5
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueContraceptive",
+ "ios": "14.3",
+ "members": [
+ {
+ "name": "unspecified",
+ "swiftName": "HKCategoryValueContraceptiveUnspecified",
+ "rawValue": 1
+ },
+ {
+ "name": "implant",
+ "swiftName": "HKCategoryValueContraceptiveImplant",
+ "rawValue": 2
+ },
+ {
+ "name": "injection",
+ "swiftName": "HKCategoryValueContraceptiveInjection",
+ "rawValue": 3
+ },
+ {
+ "name": "intrauterineDevice",
+ "swiftName": "HKCategoryValueContraceptiveIntrauterineDevice",
+ "rawValue": 4
+ },
+ {
+ "name": "intravaginalRing",
+ "swiftName": "HKCategoryValueContraceptiveIntravaginalRing",
+ "rawValue": 5
+ },
+ {
+ "name": "oral",
+ "swiftName": "HKCategoryValueContraceptiveOral",
+ "rawValue": 6
+ },
+ {
+ "name": "patch",
+ "swiftName": "HKCategoryValueContraceptivePatch",
+ "rawValue": 7
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueEnvironmentalAudioExposureEvent",
+ "ios": "14.0",
+ "members": [
+ {
+ "name": "momentaryLimit",
+ "swiftName": "HKCategoryValueEnvironmentalAudioExposureEventMomentaryLimit",
+ "rawValue": 1
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueHeadphoneAudioExposureEvent",
+ "ios": "14.2",
+ "members": [
+ {
+ "name": "sevenDayLimit",
+ "swiftName": "HKCategoryValueHeadphoneAudioExposureEventSevenDayLimit",
+ "rawValue": 1
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueLowCardioFitnessEvent",
+ "ios": "14.3",
+ "members": [
+ {
+ "name": "lowFitness",
+ "swiftName": "HKCategoryValueLowCardioFitnessEventLowFitness",
+ "rawValue": 1
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueMenstrualFlow",
+ "ios": "9.0",
+ "members": [
+ {
+ "name": "unspecified",
+ "swiftName": "HKCategoryValueMenstrualFlowUnspecified",
+ "rawValue": 1
+ },
+ {
+ "name": "light",
+ "swiftName": "HKCategoryValueMenstrualFlowLight",
+ "rawValue": 2
+ },
+ {
+ "name": "medium",
+ "swiftName": "HKCategoryValueMenstrualFlowMedium",
+ "rawValue": 3
+ },
+ {
+ "name": "heavy",
+ "swiftName": "HKCategoryValueMenstrualFlowHeavy",
+ "rawValue": 4
+ },
+ {
+ "name": "none",
+ "swiftName": "HKCategoryValueMenstrualFlowNone",
+ "rawValue": 5
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueOvulationTestResult",
+ "ios": "9.0",
+ "members": [
+ {
+ "name": "negative",
+ "swiftName": "HKCategoryValueOvulationTestResultNegative",
+ "rawValue": 1
+ },
+ {
+ "name": "luteinizingHormoneSurge",
+ "swiftName": "HKCategoryValueOvulationTestResultLuteinizingHormoneSurge",
+ "rawValue": 2
+ },
+ {
+ "name": "positive",
+ "swiftName": "HKCategoryValueOvulationTestResultPositive",
+ "rawValue": 2
+ },
+ {
+ "name": "indeterminate",
+ "swiftName": "HKCategoryValueOvulationTestResultIndeterminate",
+ "rawValue": 3
+ },
+ {
+ "name": "estrogenSurge",
+ "swiftName": "HKCategoryValueOvulationTestResultEstrogenSurge",
+ "rawValue": 4
+ }
+ ]
+ },
+ {
+ "name": "CategoryValuePregnancyTestResult",
+ "ios": "15.0",
+ "members": [
+ {
+ "name": "negative",
+ "swiftName": "HKCategoryValuePregnancyTestResultNegative",
+ "rawValue": 1
+ },
+ {
+ "name": "positive",
+ "swiftName": "HKCategoryValuePregnancyTestResultPositive",
+ "rawValue": 2
+ },
+ {
+ "name": "indeterminate",
+ "swiftName": "HKCategoryValuePregnancyTestResultIndeterminate",
+ "rawValue": 3
+ }
+ ]
+ },
+ {
+ "name": "CategoryValuePresence",
+ "ios": "13.6",
+ "members": [
+ {
+ "name": "present",
+ "swiftName": "HKCategoryValuePresencePresent",
+ "rawValue": 0
+ },
+ {
+ "name": "notPresent",
+ "swiftName": "HKCategoryValuePresenceNotPresent",
+ "rawValue": 1
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueProgesteroneTestResult",
+ "ios": "15.0",
+ "members": [
+ {
+ "name": "negative",
+ "swiftName": "HKCategoryValueProgesteroneTestResultNegative",
+ "rawValue": 1
+ },
+ {
+ "name": "positive",
+ "swiftName": "HKCategoryValueProgesteroneTestResultPositive",
+ "rawValue": 2
+ },
+ {
+ "name": "indeterminate",
+ "swiftName": "HKCategoryValueProgesteroneTestResultIndeterminate",
+ "rawValue": 3
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueSeverity",
+ "ios": "13.6",
+ "members": [
+ {
+ "name": "unspecified",
+ "swiftName": "HKCategoryValueSeverityUnspecified",
+ "rawValue": 0
+ },
+ {
+ "name": "notPresent",
+ "swiftName": "HKCategoryValueSeverityNotPresent",
+ "rawValue": 1
+ },
+ {
+ "name": "mild",
+ "swiftName": "HKCategoryValueSeverityMild",
+ "rawValue": 2
+ },
+ {
+ "name": "moderate",
+ "swiftName": "HKCategoryValueSeverityModerate",
+ "rawValue": 3
+ },
+ {
+ "name": "severe",
+ "swiftName": "HKCategoryValueSeveritySevere",
+ "rawValue": 4
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueSleepAnalysis",
+ "ios": "8.0",
+ "members": [
+ {
+ "name": "inBed",
+ "swiftName": "HKCategoryValueSleepAnalysisInBed",
+ "rawValue": 0
+ },
+ {
+ "name": "asleepUnspecified",
+ "swiftName": "HKCategoryValueSleepAnalysisAsleepUnspecified",
+ "rawValue": 1
+ },
+ {
+ "name": "asleep",
+ "swiftName": "HKCategoryValueSleepAnalysisAsleep",
+ "rawValue": 1
+ },
+ {
+ "name": "awake",
+ "swiftName": "HKCategoryValueSleepAnalysisAwake",
+ "rawValue": 2
+ },
+ {
+ "name": "asleepCore",
+ "swiftName": "HKCategoryValueSleepAnalysisAsleepCore",
+ "rawValue": 3
+ },
+ {
+ "name": "asleepDeep",
+ "swiftName": "HKCategoryValueSleepAnalysisAsleepDeep",
+ "rawValue": 4
+ },
+ {
+ "name": "asleepREM",
+ "swiftName": "HKCategoryValueSleepAnalysisAsleepREM",
+ "rawValue": 5
+ }
+ ]
+ },
+ {
+ "name": "CategoryValueVaginalBleeding",
+ "ios": "18.0",
+ "members": [
+ {
+ "name": "unspecified",
+ "swiftName": "HKCategoryValueVaginalBleedingUnspecified",
+ "rawValue": 1
+ },
+ {
+ "name": "light",
+ "swiftName": "HKCategoryValueVaginalBleedingLight",
+ "rawValue": 2
+ },
+ {
+ "name": "medium",
+ "swiftName": "HKCategoryValueVaginalBleedingMedium",
+ "rawValue": 3
+ },
+ {
+ "name": "heavy",
+ "swiftName": "HKCategoryValueVaginalBleedingHeavy",
+ "rawValue": 4
+ },
+ {
+ "name": "none",
+ "swiftName": "HKCategoryValueVaginalBleedingNone",
+ "rawValue": 5
+ }
+ ]
+ },
+ {
+ "name": "CyclingFunctionalThresholdPowerTestType",
+ "ios": "17.0",
+ "members": [
+ {
+ "name": "maxExercise60Minute",
+ "swiftName": "HKCyclingFunctionalThresholdPowerTestTypeMaxExercise60Minute",
+ "rawValue": 1
+ },
+ {
+ "name": "maxExercise20Minute",
+ "swiftName": "HKCyclingFunctionalThresholdPowerTestTypeMaxExercise20Minute",
+ "rawValue": 2
+ },
+ {
+ "name": "rampTest",
+ "swiftName": "HKCyclingFunctionalThresholdPowerTestTypeRampTest",
+ "rawValue": 3
+ },
+ {
+ "name": "predictionExercise",
+ "swiftName": "HKCyclingFunctionalThresholdPowerTestTypePredictionExercise",
+ "rawValue": 4
+ }
+ ]
+ },
+ {
+ "name": "DevicePlacementSide",
+ "ios": "14.0",
+ "members": [
+ {
+ "name": "unknown",
+ "swiftName": "HKDevicePlacementSideUnknown",
+ "rawValue": 0
+ },
+ {
+ "name": "left",
+ "swiftName": "HKDevicePlacementSideLeft",
+ "rawValue": 1
+ },
+ {
+ "name": "right",
+ "swiftName": "HKDevicePlacementSideRight",
+ "rawValue": 2
+ },
+ {
+ "name": "central",
+ "swiftName": "HKDevicePlacementSideCentral",
+ "rawValue": 3
+ }
+ ]
+ },
+ {
+ "name": "HeartRateMotionContext",
+ "ios": "11.0",
+ "members": [
+ {
+ "name": "notSet",
+ "swiftName": "HKHeartRateMotionContextNotSet",
+ "rawValue": 0
+ },
+ {
+ "name": "sedentary",
+ "swiftName": "HKHeartRateMotionContextSedentary",
+ "rawValue": 1
+ },
+ {
+ "name": "active",
+ "swiftName": "HKHeartRateMotionContextActive",
+ "rawValue": 2
+ }
+ ]
+ },
+ {
+ "name": "HeartRateRecoveryTestType",
+ "ios": "16.0",
+ "members": [
+ {
+ "name": "maxExercise",
+ "swiftName": "HKHeartRateRecoveryTestTypeMaxExercise",
+ "rawValue": 1
+ },
+ {
+ "name": "predictionSubMaxExercise",
+ "swiftName": "HKHeartRateRecoveryTestTypePredictionSubMaxExercise",
+ "rawValue": 2
+ },
+ {
+ "name": "predictionNonExercise",
+ "swiftName": "HKHeartRateRecoveryTestTypePredictionNonExercise",
+ "rawValue": 3
+ }
+ ]
+ },
+ {
+ "name": "HeartRateSensorLocation",
+ "ios": "8.0",
+ "members": [
+ {
+ "name": "other",
+ "swiftName": "HKHeartRateSensorLocationOther",
+ "rawValue": 0
+ },
+ {
+ "name": "chest",
+ "swiftName": "HKHeartRateSensorLocationChest",
+ "rawValue": 1
+ },
+ {
+ "name": "wrist",
+ "swiftName": "HKHeartRateSensorLocationWrist",
+ "rawValue": 2
+ },
+ {
+ "name": "finger",
+ "swiftName": "HKHeartRateSensorLocationFinger",
+ "rawValue": 3
+ },
+ {
+ "name": "hand",
+ "swiftName": "HKHeartRateSensorLocationHand",
+ "rawValue": 4
+ },
+ {
+ "name": "earLobe",
+ "swiftName": "HKHeartRateSensorLocationEarLobe",
+ "rawValue": 5
+ },
+ {
+ "name": "foot",
+ "swiftName": "HKHeartRateSensorLocationFoot",
+ "rawValue": 6
+ }
+ ]
+ },
+ {
+ "name": "InsulinDeliveryReason",
+ "ios": "11.0",
+ "members": [
+ {
+ "name": "basal",
+ "swiftName": "HKInsulinDeliveryReasonBasal",
+ "rawValue": 1
+ },
+ {
+ "name": "bolus",
+ "swiftName": "HKInsulinDeliveryReasonBolus",
+ "rawValue": 2
+ }
+ ]
+ },
+ {
+ "name": "PhysicalEffortEstimationType",
+ "ios": "17.0",
+ "members": [
+ {
+ "name": "activityLookup",
+ "swiftName": "HKPhysicalEffortEstimationTypeActivityLookup",
+ "rawValue": 1
+ },
+ {
+ "name": "deviceSensed",
+ "swiftName": "HKPhysicalEffortEstimationTypeDeviceSensed",
+ "rawValue": 2
+ }
+ ]
+ },
+ {
+ "name": "SwimmingStrokeStyle",
+ "ios": "10.0",
+ "members": [
+ {
+ "name": "unknown",
+ "swiftName": "HKSwimmingStrokeStyleUnknown",
+ "rawValue": 0
+ },
+ {
+ "name": "mixed",
+ "swiftName": "HKSwimmingStrokeStyleMixed",
+ "rawValue": 1
+ },
+ {
+ "name": "freestyle",
+ "swiftName": "HKSwimmingStrokeStyleFreestyle",
+ "rawValue": 2
+ },
+ {
+ "name": "backstroke",
+ "swiftName": "HKSwimmingStrokeStyleBackstroke",
+ "rawValue": 3
+ },
+ {
+ "name": "breaststroke",
+ "swiftName": "HKSwimmingStrokeStyleBreaststroke",
+ "rawValue": 4
+ },
+ {
+ "name": "butterfly",
+ "swiftName": "HKSwimmingStrokeStyleButterfly",
+ "rawValue": 5
+ },
+ {
+ "name": "kickboard",
+ "swiftName": "HKSwimmingStrokeStyleKickboard",
+ "rawValue": 6
+ }
+ ]
+ },
+ {
+ "name": "UserMotionContext",
+ "ios": "16.0",
+ "members": [
+ {
+ "name": "notSet",
+ "swiftName": "HKUserMotionContextNotSet",
+ "rawValue": 0
+ },
+ {
+ "name": "stationary",
+ "swiftName": "HKUserMotionContextStationary",
+ "rawValue": 1
+ },
+ {
+ "name": "active",
+ "swiftName": "HKUserMotionContextActive",
+ "rawValue": 2
+ }
+ ]
+ },
+ {
+ "name": "VO2MaxTestType",
+ "ios": "11.0",
+ "members": [
+ {
+ "name": "maxExercise",
+ "swiftName": "HKVO2MaxTestTypeMaxExercise",
+ "rawValue": 1
+ },
+ {
+ "name": "predictionSubMaxExercise",
+ "swiftName": "HKVO2MaxTestTypePredictionSubMaxExercise",
+ "rawValue": 2
+ },
+ {
+ "name": "predictionNonExercise",
+ "swiftName": "HKVO2MaxTestTypePredictionNonExercise",
+ "rawValue": 3
+ },
+ {
+ "name": "predictionStepTest",
+ "swiftName": "HKVO2MaxTestTypePredictionStepTest",
+ "rawValue": 4
+ }
+ ]
+ },
+ {
+ "name": "WaterSalinity",
+ "ios": "17.0",
+ "members": [
+ {
+ "name": "freshWater",
+ "swiftName": "HKWaterSalinityFreshWater",
+ "rawValue": 1
+ },
+ {
+ "name": "saltWater",
+ "swiftName": "HKWaterSalinitySaltWater",
+ "rawValue": 2
+ }
+ ]
+ },
+ {
+ "name": "WeatherCondition",
+ "ios": "10.0",
+ "members": [
+ {
+ "name": "none",
+ "swiftName": "HKWeatherConditionNone",
+ "rawValue": 0
+ },
+ {
+ "name": "clear",
+ "swiftName": "HKWeatherConditionClear",
+ "rawValue": 1
+ },
+ {
+ "name": "fair",
+ "swiftName": "HKWeatherConditionFair",
+ "rawValue": 2
+ },
+ {
+ "name": "partlyCloudy",
+ "swiftName": "HKWeatherConditionPartlyCloudy",
+ "rawValue": 3
+ },
+ {
+ "name": "mostlyCloudy",
+ "swiftName": "HKWeatherConditionMostlyCloudy",
+ "rawValue": 4
+ },
+ {
+ "name": "cloudy",
+ "swiftName": "HKWeatherConditionCloudy",
+ "rawValue": 5
+ },
+ {
+ "name": "foggy",
+ "swiftName": "HKWeatherConditionFoggy",
+ "rawValue": 6
+ },
+ {
+ "name": "haze",
+ "swiftName": "HKWeatherConditionHaze",
+ "rawValue": 7
+ },
+ {
+ "name": "windy",
+ "swiftName": "HKWeatherConditionWindy",
+ "rawValue": 8
+ },
+ {
+ "name": "blustery",
+ "swiftName": "HKWeatherConditionBlustery",
+ "rawValue": 9
+ },
+ {
+ "name": "smoky",
+ "swiftName": "HKWeatherConditionSmoky",
+ "rawValue": 10
+ },
+ {
+ "name": "dust",
+ "swiftName": "HKWeatherConditionDust",
+ "rawValue": 11
+ },
+ {
+ "name": "snow",
+ "swiftName": "HKWeatherConditionSnow",
+ "rawValue": 12
+ },
+ {
+ "name": "hail",
+ "swiftName": "HKWeatherConditionHail",
+ "rawValue": 13
+ },
+ {
+ "name": "sleet",
+ "swiftName": "HKWeatherConditionSleet",
+ "rawValue": 14
+ },
+ {
+ "name": "freezingDrizzle",
+ "swiftName": "HKWeatherConditionFreezingDrizzle",
+ "rawValue": 15
+ },
+ {
+ "name": "freezingRain",
+ "swiftName": "HKWeatherConditionFreezingRain",
+ "rawValue": 16
+ },
+ {
+ "name": "mixedRainAndHail",
+ "swiftName": "HKWeatherConditionMixedRainAndHail",
+ "rawValue": 17
+ },
+ {
+ "name": "mixedRainAndSnow",
+ "swiftName": "HKWeatherConditionMixedRainAndSnow",
+ "rawValue": 18
+ },
+ {
+ "name": "mixedRainAndSleet",
+ "swiftName": "HKWeatherConditionMixedRainAndSleet",
+ "rawValue": 19
+ },
+ {
+ "name": "mixedSnowAndSleet",
+ "swiftName": "HKWeatherConditionMixedSnowAndSleet",
+ "rawValue": 20
+ },
+ {
+ "name": "drizzle",
+ "swiftName": "HKWeatherConditionDrizzle",
+ "rawValue": 21
+ },
+ {
+ "name": "scatteredShowers",
+ "swiftName": "HKWeatherConditionScatteredShowers",
+ "rawValue": 22
+ },
+ {
+ "name": "showers",
+ "swiftName": "HKWeatherConditionShowers",
+ "rawValue": 23
+ },
+ {
+ "name": "thunderstorms",
+ "swiftName": "HKWeatherConditionThunderstorms",
+ "rawValue": 24
+ },
+ {
+ "name": "tropicalStorm",
+ "swiftName": "HKWeatherConditionTropicalStorm",
+ "rawValue": 25
+ },
+ {
+ "name": "hurricane",
+ "swiftName": "HKWeatherConditionHurricane",
+ "rawValue": 26
+ },
+ {
+ "name": "tornado",
+ "swiftName": "HKWeatherConditionTornado",
+ "rawValue": 27
+ }
+ ]
+ },
+ {
+ "name": "WorkoutActivityType",
+ "ios": "8.0",
+ "members": [
+ {
+ "name": "americanFootball",
+ "swiftName": "HKWorkoutActivityTypeAmericanFootball",
+ "rawValue": 1
+ },
+ {
+ "name": "archery",
+ "swiftName": "HKWorkoutActivityTypeArchery",
+ "rawValue": 2
+ },
+ {
+ "name": "australianFootball",
+ "swiftName": "HKWorkoutActivityTypeAustralianFootball",
+ "rawValue": 3
+ },
+ {
+ "name": "badminton",
+ "swiftName": "HKWorkoutActivityTypeBadminton",
+ "rawValue": 4
+ },
+ {
+ "name": "baseball",
+ "swiftName": "HKWorkoutActivityTypeBaseball",
+ "rawValue": 5
+ },
+ {
+ "name": "basketball",
+ "swiftName": "HKWorkoutActivityTypeBasketball",
+ "rawValue": 6
+ },
+ {
+ "name": "bowling",
+ "swiftName": "HKWorkoutActivityTypeBowling",
+ "rawValue": 7
+ },
+ {
+ "name": "boxing",
+ "swiftName": "HKWorkoutActivityTypeBoxing",
+ "rawValue": 8
+ },
+ {
+ "name": "climbing",
+ "swiftName": "HKWorkoutActivityTypeClimbing",
+ "rawValue": 9
+ },
+ {
+ "name": "cricket",
+ "swiftName": "HKWorkoutActivityTypeCricket",
+ "rawValue": 10
+ },
+ {
+ "name": "crossTraining",
+ "swiftName": "HKWorkoutActivityTypeCrossTraining",
+ "rawValue": 11
+ },
+ {
+ "name": "curling",
+ "swiftName": "HKWorkoutActivityTypeCurling",
+ "rawValue": 12
+ },
+ {
+ "name": "cycling",
+ "swiftName": "HKWorkoutActivityTypeCycling",
+ "rawValue": 13
+ },
+ {
+ "name": "dance",
+ "swiftName": "HKWorkoutActivityTypeDance",
+ "rawValue": 14
+ },
+ {
+ "name": "danceInspiredTraining",
+ "swiftName": "HKWorkoutActivityTypeDanceInspiredTraining",
+ "rawValue": 15
+ },
+ {
+ "name": "elliptical",
+ "swiftName": "HKWorkoutActivityTypeElliptical",
+ "rawValue": 16
+ },
+ {
+ "name": "equestrianSports",
+ "swiftName": "HKWorkoutActivityTypeEquestrianSports",
+ "rawValue": 17
+ },
+ {
+ "name": "fencing",
+ "swiftName": "HKWorkoutActivityTypeFencing",
+ "rawValue": 18
+ },
+ {
+ "name": "fishing",
+ "swiftName": "HKWorkoutActivityTypeFishing",
+ "rawValue": 19
+ },
+ {
+ "name": "functionalStrengthTraining",
+ "swiftName": "HKWorkoutActivityTypeFunctionalStrengthTraining",
+ "rawValue": 20
+ },
+ {
+ "name": "golf",
+ "swiftName": "HKWorkoutActivityTypeGolf",
+ "rawValue": 21
+ },
+ {
+ "name": "gymnastics",
+ "swiftName": "HKWorkoutActivityTypeGymnastics",
+ "rawValue": 22
+ },
+ {
+ "name": "handball",
+ "swiftName": "HKWorkoutActivityTypeHandball",
+ "rawValue": 23
+ },
+ {
+ "name": "hiking",
+ "swiftName": "HKWorkoutActivityTypeHiking",
+ "rawValue": 24
+ },
+ {
+ "name": "hockey",
+ "swiftName": "HKWorkoutActivityTypeHockey",
+ "rawValue": 25
+ },
+ {
+ "name": "hunting",
+ "swiftName": "HKWorkoutActivityTypeHunting",
+ "rawValue": 26
+ },
+ {
+ "name": "lacrosse",
+ "swiftName": "HKWorkoutActivityTypeLacrosse",
+ "rawValue": 27
+ },
+ {
+ "name": "martialArts",
+ "swiftName": "HKWorkoutActivityTypeMartialArts",
+ "rawValue": 28
+ },
+ {
+ "name": "mindAndBody",
+ "swiftName": "HKWorkoutActivityTypeMindAndBody",
+ "rawValue": 29
+ },
+ {
+ "name": "mixedMetabolicCardioTraining",
+ "swiftName": "HKWorkoutActivityTypeMixedMetabolicCardioTraining",
+ "rawValue": 30
+ },
+ {
+ "name": "paddleSports",
+ "swiftName": "HKWorkoutActivityTypePaddleSports",
+ "rawValue": 31
+ },
+ {
+ "name": "play",
+ "swiftName": "HKWorkoutActivityTypePlay",
+ "rawValue": 32
+ },
+ {
+ "name": "preparationAndRecovery",
+ "swiftName": "HKWorkoutActivityTypePreparationAndRecovery",
+ "rawValue": 33
+ },
+ {
+ "name": "racquetball",
+ "swiftName": "HKWorkoutActivityTypeRacquetball",
+ "rawValue": 34
+ },
+ {
+ "name": "rowing",
+ "swiftName": "HKWorkoutActivityTypeRowing",
+ "rawValue": 35
+ },
+ {
+ "name": "rugby",
+ "swiftName": "HKWorkoutActivityTypeRugby",
+ "rawValue": 36
+ },
+ {
+ "name": "running",
+ "swiftName": "HKWorkoutActivityTypeRunning",
+ "rawValue": 37
+ },
+ {
+ "name": "sailing",
+ "swiftName": "HKWorkoutActivityTypeSailing",
+ "rawValue": 38
+ },
+ {
+ "name": "skatingSports",
+ "swiftName": "HKWorkoutActivityTypeSkatingSports",
+ "rawValue": 39
+ },
+ {
+ "name": "snowSports",
+ "swiftName": "HKWorkoutActivityTypeSnowSports",
+ "rawValue": 40
+ },
+ {
+ "name": "soccer",
+ "swiftName": "HKWorkoutActivityTypeSoccer",
+ "rawValue": 41
+ },
+ {
+ "name": "softball",
+ "swiftName": "HKWorkoutActivityTypeSoftball",
+ "rawValue": 42
+ },
+ {
+ "name": "squash",
+ "swiftName": "HKWorkoutActivityTypeSquash",
+ "rawValue": 43
+ },
+ {
+ "name": "stairClimbing",
+ "swiftName": "HKWorkoutActivityTypeStairClimbing",
+ "rawValue": 44
+ },
+ {
+ "name": "surfingSports",
+ "swiftName": "HKWorkoutActivityTypeSurfingSports",
+ "rawValue": 45
+ },
+ {
+ "name": "swimming",
+ "swiftName": "HKWorkoutActivityTypeSwimming",
+ "rawValue": 46
+ },
+ {
+ "name": "tableTennis",
+ "swiftName": "HKWorkoutActivityTypeTableTennis",
+ "rawValue": 47
+ },
+ {
+ "name": "tennis",
+ "swiftName": "HKWorkoutActivityTypeTennis",
+ "rawValue": 48
+ },
+ {
+ "name": "trackAndField",
+ "swiftName": "HKWorkoutActivityTypeTrackAndField",
+ "rawValue": 49
+ },
+ {
+ "name": "traditionalStrengthTraining",
+ "swiftName": "HKWorkoutActivityTypeTraditionalStrengthTraining",
+ "rawValue": 50
+ },
+ {
+ "name": "volleyball",
+ "swiftName": "HKWorkoutActivityTypeVolleyball",
+ "rawValue": 51
+ },
+ {
+ "name": "walking",
+ "swiftName": "HKWorkoutActivityTypeWalking",
+ "rawValue": 52
+ },
+ {
+ "name": "waterFitness",
+ "swiftName": "HKWorkoutActivityTypeWaterFitness",
+ "rawValue": 53
+ },
+ {
+ "name": "waterPolo",
+ "swiftName": "HKWorkoutActivityTypeWaterPolo",
+ "rawValue": 54
+ },
+ {
+ "name": "waterSports",
+ "swiftName": "HKWorkoutActivityTypeWaterSports",
+ "rawValue": 55
+ },
+ {
+ "name": "wrestling",
+ "swiftName": "HKWorkoutActivityTypeWrestling",
+ "rawValue": 56
+ },
+ {
+ "name": "yoga",
+ "swiftName": "HKWorkoutActivityTypeYoga",
+ "rawValue": 57
+ },
+ {
+ "name": "barre",
+ "swiftName": "HKWorkoutActivityTypeBarre",
+ "rawValue": 58
+ },
+ {
+ "name": "coreTraining",
+ "swiftName": "HKWorkoutActivityTypeCoreTraining",
+ "rawValue": 59
+ },
+ {
+ "name": "crossCountrySkiing",
+ "swiftName": "HKWorkoutActivityTypeCrossCountrySkiing",
+ "rawValue": 60
+ },
+ {
+ "name": "downhillSkiing",
+ "swiftName": "HKWorkoutActivityTypeDownhillSkiing",
+ "rawValue": 61
+ },
+ {
+ "name": "flexibility",
+ "swiftName": "HKWorkoutActivityTypeFlexibility",
+ "rawValue": 62
+ },
+ {
+ "name": "highIntensityIntervalTraining",
+ "swiftName": "HKWorkoutActivityTypeHighIntensityIntervalTraining",
+ "rawValue": 63
+ },
+ {
+ "name": "jumpRope",
+ "swiftName": "HKWorkoutActivityTypeJumpRope",
+ "rawValue": 64
+ },
+ {
+ "name": "kickboxing",
+ "swiftName": "HKWorkoutActivityTypeKickboxing",
+ "rawValue": 65
+ },
+ {
+ "name": "pilates",
+ "swiftName": "HKWorkoutActivityTypePilates",
+ "rawValue": 66
+ },
+ {
+ "name": "snowboarding",
+ "swiftName": "HKWorkoutActivityTypeSnowboarding",
+ "rawValue": 67
+ },
+ {
+ "name": "stairs",
+ "swiftName": "HKWorkoutActivityTypeStairs",
+ "rawValue": 68
+ },
+ {
+ "name": "stepTraining",
+ "swiftName": "HKWorkoutActivityTypeStepTraining",
+ "rawValue": 69
+ },
+ {
+ "name": "wheelchairWalkPace",
+ "swiftName": "HKWorkoutActivityTypeWheelchairWalkPace",
+ "rawValue": 70
+ },
+ {
+ "name": "wheelchairRunPace",
+ "swiftName": "HKWorkoutActivityTypeWheelchairRunPace",
+ "rawValue": 71
+ },
+ {
+ "name": "taiChi",
+ "swiftName": "HKWorkoutActivityTypeTaiChi",
+ "rawValue": 72
+ },
+ {
+ "name": "mixedCardio",
+ "swiftName": "HKWorkoutActivityTypeMixedCardio",
+ "rawValue": 73
+ },
+ {
+ "name": "handCycling",
+ "swiftName": "HKWorkoutActivityTypeHandCycling",
+ "rawValue": 74
+ },
+ {
+ "name": "discSports",
+ "swiftName": "HKWorkoutActivityTypeDiscSports",
+ "rawValue": 75
+ },
+ {
+ "name": "fitnessGaming",
+ "swiftName": "HKWorkoutActivityTypeFitnessGaming",
+ "rawValue": 76
+ },
+ {
+ "name": "cardioDance",
+ "swiftName": "HKWorkoutActivityTypeCardioDance",
+ "rawValue": 77
+ },
+ {
+ "name": "socialDance",
+ "swiftName": "HKWorkoutActivityTypeSocialDance",
+ "rawValue": 78
+ },
+ {
+ "name": "pickleball",
+ "swiftName": "HKWorkoutActivityTypePickleball",
+ "rawValue": 79
+ },
+ {
+ "name": "cooldown",
+ "swiftName": "HKWorkoutActivityTypeCooldown",
+ "rawValue": 80
+ },
+ {
+ "name": "swimBikeRun",
+ "swiftName": "HKWorkoutActivityTypeSwimBikeRun",
+ "rawValue": 82
+ },
+ {
+ "name": "transition",
+ "swiftName": "HKWorkoutActivityTypeTransition",
+ "rawValue": 83
+ },
+ {
+ "name": "underwaterDiving",
+ "swiftName": "HKWorkoutActivityTypeUnderwaterDiving",
+ "rawValue": 84
+ },
+ {
+ "name": "other",
+ "swiftName": "HKWorkoutActivityTypeOther",
+ "rawValue": 3000
+ }
+ ]
+ },
+ {
+ "name": "WorkoutEventType",
+ "ios": "8.0",
+ "members": [
+ {
+ "name": "pause",
+ "swiftName": "HKWorkoutEventTypePause",
+ "rawValue": 1
+ },
+ {
+ "name": "resume",
+ "swiftName": "HKWorkoutEventTypeResume",
+ "rawValue": 2
+ },
+ {
+ "name": "lap",
+ "swiftName": "HKWorkoutEventTypeLap",
+ "rawValue": 3
+ },
+ {
+ "name": "marker",
+ "swiftName": "HKWorkoutEventTypeMarker",
+ "rawValue": 4
+ },
+ {
+ "name": "motionPaused",
+ "swiftName": "HKWorkoutEventTypeMotionPaused",
+ "rawValue": 5
+ },
+ {
+ "name": "motionResumed",
+ "swiftName": "HKWorkoutEventTypeMotionResumed",
+ "rawValue": 6
+ },
+ {
+ "name": "segment",
+ "swiftName": "HKWorkoutEventTypeSegment",
+ "rawValue": 7
+ },
+ {
+ "name": "pauseOrResumeRequest",
+ "swiftName": "HKWorkoutEventTypePauseOrResumeRequest",
+ "rawValue": 8
+ }
+ ]
+ },
+ {
+ "name": "WorkoutSwimmingLocationType",
+ "ios": "10.0",
+ "members": [
+ {
+ "name": "unknown",
+ "swiftName": "HKWorkoutSwimmingLocationTypeUnknown",
+ "rawValue": 0
+ },
+ {
+ "name": "pool",
+ "swiftName": "HKWorkoutSwimmingLocationTypePool",
+ "rawValue": 1
+ },
+ {
+ "name": "openWater",
+ "swiftName": "HKWorkoutSwimmingLocationTypeOpenWater",
+ "rawValue": 2
+ }
+ ]
+ }
+ ],
+ "metadataKeys": [
+ {
+ "keyConstant": "HKMetadataKeyActivityType",
+ "rawKey": "HKActivityType",
+ "ios": "17.0",
+ "expectedType": "an NSNumber containing a HKWorkoutActivityType value",
+ "tsType": "WorkoutActivityType",
+ "valueKind": "enum",
+ "enumName": "WorkoutActivityType",
+ "objectTypes": ["sample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyAlgorithmVersion",
+ "rawKey": "HKAlgorithmVersion",
+ "ios": "15.0",
+ "expectedType": null,
+ "tsType": "number",
+ "valueKind": "number",
+ "enumName": null,
+ "objectTypes": ["sample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyAlpineSlopeGrade",
+ "rawKey": "HKAlpineSlopeGrade",
+ "ios": "11.2",
+ "expectedType": "an HKQuantity object compatible with percent unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": ["HKQuantityTypeIdentifierDistanceDownhillSnowSports"]
+ },
+ {
+ "keyConstant": "HKMetadataKeyAppleDeviceCalibrated",
+ "rawKey": "HKAppleDeviceCalibrated",
+ "ios": "14.0",
+ "expectedType": null,
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["sample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyAppleECGAlgorithmVersion",
+ "rawKey": "HKAppleECGAlgorithmVersion",
+ "ios": "14.0",
+ "expectedType": "an an NSNumber containing a HKAppleECGAlgorithmVersion value",
+ "tsType": "AppleECGAlgorithmVersion",
+ "valueKind": "enum",
+ "enumName": "AppleECGAlgorithmVersion",
+ "objectTypes": [],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyAppleFitnessPlusCatalogIdentifier",
+ "rawKey": "HKAppleFitnessPlusCatalogIdentifier",
+ "ios": "18.2",
+ "expectedType": "an NSString containing the Fitness+ catalog identifier",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyAppleFitnessPlusSession",
+ "rawKey": "HKAppleFitnessPlusSession",
+ "ios": "17.0",
+ "expectedType": "an NSNumber containing a BOOL value",
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyAudioExposureDuration",
+ "rawKey": "HKAudioExposureDuration",
+ "ios": "14.2",
+ "expectedType": "an HKQuantity object compatible with a time unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["sample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyAudioExposureLevel",
+ "rawKey": "HKAudioExposureLevel",
+ "ios": "13.0",
+ "expectedType": null,
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["categorySample"],
+ "identifiers": [
+ "HKCategoryTypeIdentifierAudioExposureEvent",
+ "HKCategoryTypeIdentifierEnvironmentalAudioExposureEvent",
+ "HKCategoryTypeIdentifierHeadphoneAudioExposureEvent"
+ ]
+ },
+ {
+ "keyConstant": "HKMetadataKeyAverageMETs",
+ "rawKey": "HKAverageMETs",
+ "ios": "13.0",
+ "expectedType": "an HKQuantity expressed in a METs (kcal/(kg*hr)) unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyAverageSpeed",
+ "rawKey": "HKAverageSpeed",
+ "ios": "11.2",
+ "expectedType": "an HKQuantity object compatible with a speed unit (e",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": ["HKQuantityTypeIdentifierDistanceDownhillSnowSports"]
+ },
+ {
+ "keyConstant": "HKMetadataKeyBarometricPressure",
+ "rawKey": "HKBarometricPressure",
+ "ios": "14.0",
+ "expectedType": "an HKQuantity representing a value in units of pressure (atmospheres, pascals, millimeters of Mercury)",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["sample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyBloodGlucoseMealTime",
+ "rawKey": "HKBloodGlucoseMealTime",
+ "ios": "11.0",
+ "expectedType": "an NSNumber containing a HKBloodGlucoseMealTime value",
+ "tsType": "BloodGlucoseMealTime",
+ "valueKind": "enum",
+ "enumName": "BloodGlucoseMealTime",
+ "objectTypes": ["quantitySample"],
+ "identifiers": ["HKQuantityTypeIdentifierBloodGlucose"]
+ },
+ {
+ "keyConstant": "HKMetadataKeyBodyTemperatureSensorLocation",
+ "rawKey": "HKBodyTemperatureSensorLocation",
+ "ios": "8.0",
+ "expectedType": "an NSNumber containing a HKBodyTemperatureSensorLocation value",
+ "tsType": "BodyTemperatureSensorLocation",
+ "valueKind": "enum",
+ "enumName": "BodyTemperatureSensorLocation",
+ "objectTypes": ["quantitySample"],
+ "identifiers": [
+ "HKQuantityTypeIdentifierBodyTemperature",
+ "HKQuantityTypeIdentifierBasalBodyTemperature"
+ ]
+ },
+ {
+ "keyConstant": "HKMetadataKeyCoachedWorkout",
+ "rawKey": "HKCoachedWorkout",
+ "ios": "8.0",
+ "expectedType": "an NSNumber containing a BOOL value",
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyCrossTrainerDistance",
+ "rawKey": "HKCrossTrainerDistance",
+ "ios": "12.0",
+ "expectedType": "an HKQuantity object compatible with a length unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyCyclingFunctionalThresholdPowerTestType",
+ "rawKey": "HKCyclingFunctionalThresholdPowerTestType",
+ "ios": "17.0",
+ "expectedType": "an NSNumber containing a HKCyclingFunctionalThresholdPowerTestType value",
+ "tsType": "CyclingFunctionalThresholdPowerTestType",
+ "valueKind": "enum",
+ "enumName": "CyclingFunctionalThresholdPowerTestType",
+ "objectTypes": [],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyDateOfEarliestDataUsedForEstimate",
+ "rawKey": "HKDateOfEarliestDataUsedForEstimate",
+ "ios": "15.0",
+ "expectedType": null,
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["sample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyDeviceManufacturerName",
+ "rawKey": "HKDeviceManufacturerName",
+ "ios": "8.0",
+ "expectedType": "NSString",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyDeviceName",
+ "rawKey": "HKDeviceName",
+ "ios": "8.0",
+ "expectedType": "NSString",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyDevicePlacementSide",
+ "rawKey": "HKDevicePlacementSide",
+ "ios": "14.0",
+ "expectedType": "an NSNumber containing a HKDevicePlacementSide value",
+ "tsType": "DevicePlacementSide",
+ "valueKind": "enum",
+ "enumName": "DevicePlacementSide",
+ "objectTypes": [],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyDeviceSerialNumber",
+ "rawKey": "HKDeviceSerialNumber",
+ "ios": "8.0",
+ "expectedType": "NSString",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyDigitalSignature",
+ "rawKey": "HKDigitalSignature",
+ "ios": "8.0",
+ "expectedType": null,
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyElevationAscended",
+ "rawKey": "HKElevationAscended",
+ "ios": "11.2",
+ "expectedType": "an HKQuantity object compatible with length unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyElevationDescended",
+ "rawKey": "HKElevationDescended",
+ "ios": "11.2",
+ "expectedType": "an HKQuantity object compatible with length unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyExternalUUID",
+ "rawKey": "HKExternalUUID",
+ "ios": "8.0",
+ "expectedType": "NSString",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyFitnessMachineDuration",
+ "rawKey": "HKFitnessMachineDuration",
+ "ios": "12.0",
+ "expectedType": "an HKQuantity object compatible with a time unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyFoodType",
+ "rawKey": "HKFoodType",
+ "ios": "8.0",
+ "expectedType": "NSString",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyGlassesPrescriptionDescription",
+ "rawKey": "HKGlassesPrescriptionDescription",
+ "ios": "16.0",
+ "expectedType": null,
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": [],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyGroupFitness",
+ "rawKey": "HKGroupFitness",
+ "ios": "8.0",
+ "expectedType": "an NSNumber containing a BOOL value",
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyHeadphoneGain",
+ "rawKey": "HKHeadphoneGain",
+ "ios": "16.4",
+ "expectedType": null,
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["categorySample"],
+ "identifiers": [
+ "HKCategoryTypeIdentifierAudioExposureEvent",
+ "HKCategoryTypeIdentifierHeadphoneAudioExposureEvent"
+ ]
+ },
+ {
+ "keyConstant": "HKMetadataKeyHeartRateEventThreshold",
+ "rawKey": "HKHeartRateEventThreshold",
+ "ios": "12.2",
+ "expectedType": null,
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["categorySample"],
+ "identifiers": [
+ "HKCategoryTypeIdentifierHighHeartRateEvent",
+ "HKCategoryTypeIdentifierLowHeartRateEvent"
+ ]
+ },
+ {
+ "keyConstant": "HKMetadataKeyHeartRateMotionContext",
+ "rawKey": "HKHeartRateMotionContext",
+ "ios": "11.0",
+ "expectedType": "an NSNumber containing a HKHeartRateMotionContext value",
+ "tsType": "HeartRateMotionContext",
+ "valueKind": "enum",
+ "enumName": "HeartRateMotionContext",
+ "objectTypes": ["quantitySample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyHeartRateRecoveryActivityDuration",
+ "rawKey": "HKHeartRateRecoveryActivityDuration",
+ "ios": "16.0",
+ "expectedType": "an HKQuantity object compatible with a time unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["quantitySample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyHeartRateRecoveryActivityType",
+ "rawKey": "HKHeartRateRecoveryActivityType",
+ "ios": "16.0",
+ "expectedType": "an NSNumber containing a HKWorkoutActivityType value",
+ "tsType": "WorkoutActivityType",
+ "valueKind": "enum",
+ "enumName": "WorkoutActivityType",
+ "objectTypes": ["quantitySample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyHeartRateRecoveryMaxObservedRecoveryHeartRate",
+ "rawKey": "HKHeartRateRecoveryMaxObservedRecoveryHeartRate",
+ "ios": "16.0",
+ "expectedType": "an HKQuantity object compatible with \"count/min\" unit (eg \"BPM\")",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["quantitySample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyHeartRateRecoveryTestType",
+ "rawKey": "HKHeartRateRecoveryTestType",
+ "ios": "16.0",
+ "expectedType": "an NSNumber containing a HKHeartRateRecoveryTestType value",
+ "tsType": "HeartRateRecoveryTestType",
+ "valueKind": "enum",
+ "enumName": "HeartRateRecoveryTestType",
+ "objectTypes": ["quantitySample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyHeartRateSensorLocation",
+ "rawKey": "HKHeartRateSensorLocation",
+ "ios": "8.0",
+ "expectedType": "an NSNumber containing a HKHeartRateSensorLocation value",
+ "tsType": "HeartRateSensorLocation",
+ "valueKind": "enum",
+ "enumName": "HeartRateSensorLocation",
+ "objectTypes": ["quantitySample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyIndoorBikeDistance",
+ "rawKey": "HKIndoorBikeDistance",
+ "ios": "12.0",
+ "expectedType": "an HKQuantity object compatible with a length unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyIndoorWorkout",
+ "rawKey": "HKIndoorWorkout",
+ "ios": "8.0",
+ "expectedType": "an NSNumber containing a BOOL value",
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyInsulinDeliveryReason",
+ "rawKey": "HKInsulinDeliveryReason",
+ "ios": "11.0",
+ "expectedType": "an NSNumber containing a HKInsulinDeliveryReason value",
+ "tsType": "InsulinDeliveryReason",
+ "valueKind": "enum",
+ "enumName": "InsulinDeliveryReason",
+ "objectTypes": ["quantitySample"],
+ "identifiers": ["HKQuantityTypeIdentifierInsulinDelivery"]
+ },
+ {
+ "keyConstant": "HKMetadataKeyLapLength",
+ "rawKey": "HKLapLength",
+ "ios": "10.0",
+ "expectedType": "an HKQuantity object compatible with a length unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyLowCardioFitnessEventThreshold",
+ "rawKey": "HKLowCardioFitnessEventThreshold",
+ "ios": "14.3",
+ "expectedType": null,
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["categorySample"],
+ "identifiers": ["HKCategoryTypeIdentifierLowCardioFitnessEvent"]
+ },
+ {
+ "keyConstant": "HKMetadataKeyMaximumLightIntensity",
+ "rawKey": "HKMaximumLightIntensity",
+ "ios": "17.0",
+ "expectedType": "an HKQuantity expressed in HKUnit Lux",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["sample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyMaximumSpeed",
+ "rawKey": "HKMaximumSpeed",
+ "ios": "11.2",
+ "expectedType": "an HKQuantity object compatible with a speed unit (e",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": ["HKQuantityTypeIdentifierDistanceDownhillSnowSports"]
+ },
+ {
+ "keyConstant": "HKMetadataKeyMenstrualCycleStart",
+ "rawKey": "HKMenstrualCycleStart",
+ "ios": "9.0",
+ "expectedType": "an NSNumber containing a BOOL value",
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["categorySample"],
+ "identifiers": ["HKCategoryTypeIdentifierMenstrualFlow"]
+ },
+ {
+ "keyConstant": "HKMetadataKeyPhysicalEffortEstimationType",
+ "rawKey": "HKPhysicalEffortEstimationType",
+ "ios": "17.0",
+ "expectedType": "an NSNumber containing a HKPhysicalEffortEstimationType value",
+ "tsType": "PhysicalEffortEstimationType",
+ "valueKind": "enum",
+ "enumName": "PhysicalEffortEstimationType",
+ "objectTypes": ["sample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyQuantityClampedToLowerBound",
+ "rawKey": "HKQuantityClampedToLowerBound",
+ "ios": "16.0",
+ "expectedType": null,
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["quantitySample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyQuantityClampedToUpperBound",
+ "rawKey": "HKQuantityClampedToUpperBound",
+ "ios": "16.0",
+ "expectedType": null,
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["quantitySample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyReferenceRangeLowerLimit",
+ "rawKey": "HKReferenceRangeLowerLimit",
+ "ios": "8.0",
+ "expectedType": "an NSNumber",
+ "tsType": "number",
+ "valueKind": "number",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyReferenceRangeUpperLimit",
+ "rawKey": "HKReferenceRangeUpperLimit",
+ "ios": "8.0",
+ "expectedType": "an NSNumber",
+ "tsType": "number",
+ "valueKind": "number",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeySessionEstimate",
+ "rawKey": "HKSessionEstimate",
+ "ios": "16.0",
+ "expectedType": "an HKQuantity object with a unit compatible with the associated HKQuantitySample",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["quantitySample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeySexualActivityProtectionUsed",
+ "rawKey": "HKSexualActivityProtectionUsed",
+ "ios": "9.0",
+ "expectedType": "an NSNumber containing a BOOL value",
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["categorySample"],
+ "identifiers": ["HKCategoryTypeIdentifierSexualActivity"]
+ },
+ {
+ "keyConstant": "HKMetadataKeySwimmingLocationType",
+ "rawKey": "HKSwimmingLocationType",
+ "ios": "10.0",
+ "expectedType": "an NSNumber containing an HKWorkoutSwimmingLocationType value",
+ "tsType": "WorkoutSwimmingLocationType",
+ "valueKind": "enum",
+ "enumName": "WorkoutSwimmingLocationType",
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeySwimmingStrokeStyle",
+ "rawKey": "HKSwimmingStrokeStyle",
+ "ios": "10.0",
+ "expectedType": "an NSNumber containing an HKSwimmingStrokeStyle value",
+ "tsType": "SwimmingStrokeStyle",
+ "valueKind": "enum",
+ "enumName": "SwimmingStrokeStyle",
+ "objectTypes": ["workoutEvent"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeySWOLFScore",
+ "rawKey": "HKSWOLFScore",
+ "ios": "16.0",
+ "expectedType": "an NSNumber containing a score",
+ "tsType": "number",
+ "valueKind": "number",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeySyncIdentifier",
+ "rawKey": "HKSyncIdentifier",
+ "ios": "11.0",
+ "expectedType": "NSString",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeySyncVersion",
+ "rawKey": "HKSyncVersion",
+ "ios": "11.0",
+ "expectedType": "NSNumber",
+ "tsType": "number",
+ "valueKind": "number",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyTimeZone",
+ "rawKey": "HKTimeZone",
+ "ios": "8.0",
+ "expectedType": "an NSString compatible with NSTimeZone's +timeZoneWithName:",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyUDIDeviceIdentifier",
+ "rawKey": "HKUDIDeviceIdentifier",
+ "ios": "8.0",
+ "expectedType": "NSString",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyUDIProductionIdentifier",
+ "rawKey": "HKUDIProductionIdentifier",
+ "ios": "8.0",
+ "expectedType": "NSString",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyUserMotionContext",
+ "rawKey": "HKUserMotionContext",
+ "ios": "16.0",
+ "expectedType": "an NSNumber containing a HKUserMotionContext value",
+ "tsType": "UserMotionContext",
+ "valueKind": "enum",
+ "enumName": "UserMotionContext",
+ "objectTypes": ["sample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyVO2MaxTestType",
+ "rawKey": "HKVO2MaxTestType",
+ "ios": "11.0",
+ "expectedType": "an NSNumber containing a HKVO2MaxTestType value",
+ "tsType": "VO2MaxTestType",
+ "valueKind": "enum",
+ "enumName": "VO2MaxTestType",
+ "objectTypes": ["quantitySample"],
+ "identifiers": ["HKQuantityTypeIdentifierVO2Max"]
+ },
+ {
+ "keyConstant": "HKMetadataKeyVO2MaxValue",
+ "rawKey": "HKVO2MaxValue",
+ "ios": "14.3",
+ "expectedType": null,
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["categorySample"],
+ "identifiers": ["HKCategoryTypeIdentifierLowCardioFitnessEvent"]
+ },
+ {
+ "keyConstant": "HKMetadataKeyWasTakenInLab",
+ "rawKey": "HKWasTakenInLab",
+ "ios": "8.0",
+ "expectedType": "an NSNumber containing a BOOL value",
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyWasUserEntered",
+ "rawKey": "HKWasUserEntered",
+ "ios": "8.0",
+ "expectedType": "an NSNumber containing a BOOL value",
+ "tsType": "boolean",
+ "valueKind": "boolean",
+ "enumName": null,
+ "objectTypes": ["common"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyWaterSalinity",
+ "rawKey": "HKWaterSalinity",
+ "ios": "17.0",
+ "expectedType": "an NSNumber containing a HKWaterSalinity value",
+ "tsType": "WaterSalinity",
+ "valueKind": "enum",
+ "enumName": "WaterSalinity",
+ "objectTypes": ["sample"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyWeatherCondition",
+ "rawKey": "HKWeatherCondition",
+ "ios": "10.0",
+ "expectedType": "an NSNumber containing an HKWeatherCondition value",
+ "tsType": "WeatherCondition",
+ "valueKind": "enum",
+ "enumName": "WeatherCondition",
+ "objectTypes": ["sample", "workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyWeatherHumidity",
+ "rawKey": "HKWeatherHumidity",
+ "ios": "10.0",
+ "expectedType": "an HKQuantity expressed in percent",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["sample", "workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyWeatherTemperature",
+ "rawKey": "HKWeatherTemperature",
+ "ios": "10.0",
+ "expectedType": "an HKQuantity expressed in a temperature unit",
+ "tsType": "Quantity",
+ "valueKind": "quantity",
+ "enumName": null,
+ "objectTypes": ["sample", "workout"],
+ "identifiers": []
+ },
+ {
+ "keyConstant": "HKMetadataKeyWorkoutBrandName",
+ "rawKey": "HKWorkoutBrandName",
+ "ios": "8.0",
+ "expectedType": "NSString",
+ "tsType": "string",
+ "valueKind": "string",
+ "enumName": null,
+ "objectTypes": ["workout"],
+ "identifiers": []
+ }
+ ]
+}
diff --git a/packages/react-native-healthkit/src/generated/healthkit.generated.ts b/packages/react-native-healthkit/src/generated/healthkit.generated.ts
new file mode 100644
index 00000000..a2935ff8
--- /dev/null
+++ b/packages/react-native-healthkit/src/generated/healthkit.generated.ts
@@ -0,0 +1,1307 @@
+/*
+ * AUTO-GENERATED FILE. DO NOT EDIT.
+ * Source: scripts/generate-healthkit.ts
+ */
+
+import type { Quantity } from '../types/QuantityType'
+import type {
+ BloodGlucoseUnit,
+ CountPerTime,
+ EnergyUnit,
+ LengthUnit,
+ MassUnit,
+ PressureUnit,
+ SpeedUnit,
+ TemperatureUnit,
+ TimeUnit,
+} from '../types/Units'
+export type QuantityTypeIdentifierReadOnly =
+ | 'HKQuantityTypeIdentifierAppleExerciseTime'
+ | 'HKQuantityTypeIdentifierAppleStandTime'
+ | 'HKQuantityTypeIdentifierAppleWalkingSteadiness'
+ | 'HKQuantityTypeIdentifierAtrialFibrillationBurden'
+ | 'HKQuantityTypeIdentifierWalkingHeartRateAverage'
+export type QuantityTypeIdentifierWriteable =
+ | 'HKQuantityTypeIdentifierActiveEnergyBurned'
+ | 'HKQuantityTypeIdentifierAppleMoveTime'
+ | 'HKQuantityTypeIdentifierAppleSleepingBreathingDisturbances'
+ | 'HKQuantityTypeIdentifierAppleSleepingWristTemperature'
+ | 'HKQuantityTypeIdentifierBasalBodyTemperature'
+ | 'HKQuantityTypeIdentifierBasalEnergyBurned'
+ | 'HKQuantityTypeIdentifierBloodAlcoholContent'
+ | 'HKQuantityTypeIdentifierBloodGlucose'
+ | 'HKQuantityTypeIdentifierBloodPressureDiastolic'
+ | 'HKQuantityTypeIdentifierBloodPressureSystolic'
+ | 'HKQuantityTypeIdentifierBodyFatPercentage'
+ | 'HKQuantityTypeIdentifierBodyMass'
+ | 'HKQuantityTypeIdentifierBodyMassIndex'
+ | 'HKQuantityTypeIdentifierBodyTemperature'
+ | 'HKQuantityTypeIdentifierCrossCountrySkiingSpeed'
+ | 'HKQuantityTypeIdentifierCyclingCadence'
+ | 'HKQuantityTypeIdentifierCyclingFunctionalThresholdPower'
+ | 'HKQuantityTypeIdentifierCyclingPower'
+ | 'HKQuantityTypeIdentifierCyclingSpeed'
+ | 'HKQuantityTypeIdentifierDietaryBiotin'
+ | 'HKQuantityTypeIdentifierDietaryCaffeine'
+ | 'HKQuantityTypeIdentifierDietaryCalcium'
+ | 'HKQuantityTypeIdentifierDietaryCarbohydrates'
+ | 'HKQuantityTypeIdentifierDietaryChloride'
+ | 'HKQuantityTypeIdentifierDietaryCholesterol'
+ | 'HKQuantityTypeIdentifierDietaryChromium'
+ | 'HKQuantityTypeIdentifierDietaryCopper'
+ | 'HKQuantityTypeIdentifierDietaryEnergyConsumed'
+ | 'HKQuantityTypeIdentifierDietaryFatMonounsaturated'
+ | 'HKQuantityTypeIdentifierDietaryFatPolyunsaturated'
+ | 'HKQuantityTypeIdentifierDietaryFatSaturated'
+ | 'HKQuantityTypeIdentifierDietaryFatTotal'
+ | 'HKQuantityTypeIdentifierDietaryFiber'
+ | 'HKQuantityTypeIdentifierDietaryFolate'
+ | 'HKQuantityTypeIdentifierDietaryIodine'
+ | 'HKQuantityTypeIdentifierDietaryIron'
+ | 'HKQuantityTypeIdentifierDietaryMagnesium'
+ | 'HKQuantityTypeIdentifierDietaryManganese'
+ | 'HKQuantityTypeIdentifierDietaryMolybdenum'
+ | 'HKQuantityTypeIdentifierDietaryNiacin'
+ | 'HKQuantityTypeIdentifierDietaryPantothenicAcid'
+ | 'HKQuantityTypeIdentifierDietaryPhosphorus'
+ | 'HKQuantityTypeIdentifierDietaryPotassium'
+ | 'HKQuantityTypeIdentifierDietaryProtein'
+ | 'HKQuantityTypeIdentifierDietaryRiboflavin'
+ | 'HKQuantityTypeIdentifierDietarySelenium'
+ | 'HKQuantityTypeIdentifierDietarySodium'
+ | 'HKQuantityTypeIdentifierDietarySugar'
+ | 'HKQuantityTypeIdentifierDietaryThiamin'
+ | 'HKQuantityTypeIdentifierDietaryVitaminA'
+ | 'HKQuantityTypeIdentifierDietaryVitaminB12'
+ | 'HKQuantityTypeIdentifierDietaryVitaminB6'
+ | 'HKQuantityTypeIdentifierDietaryVitaminC'
+ | 'HKQuantityTypeIdentifierDietaryVitaminD'
+ | 'HKQuantityTypeIdentifierDietaryVitaminE'
+ | 'HKQuantityTypeIdentifierDietaryVitaminK'
+ | 'HKQuantityTypeIdentifierDietaryWater'
+ | 'HKQuantityTypeIdentifierDietaryZinc'
+ | 'HKQuantityTypeIdentifierDistanceCrossCountrySkiing'
+ | 'HKQuantityTypeIdentifierDistanceCycling'
+ | 'HKQuantityTypeIdentifierDistanceDownhillSnowSports'
+ | 'HKQuantityTypeIdentifierDistancePaddleSports'
+ | 'HKQuantityTypeIdentifierDistanceRowing'
+ | 'HKQuantityTypeIdentifierDistanceSkatingSports'
+ | 'HKQuantityTypeIdentifierDistanceSwimming'
+ | 'HKQuantityTypeIdentifierDistanceWalkingRunning'
+ | 'HKQuantityTypeIdentifierDistanceWheelchair'
+ | 'HKQuantityTypeIdentifierElectrodermalActivity'
+ | 'HKQuantityTypeIdentifierEnvironmentalAudioExposure'
+ | 'HKQuantityTypeIdentifierEnvironmentalSoundReduction'
+ | 'HKQuantityTypeIdentifierEstimatedWorkoutEffortScore'
+ | 'HKQuantityTypeIdentifierFlightsClimbed'
+ | 'HKQuantityTypeIdentifierForcedExpiratoryVolume1'
+ | 'HKQuantityTypeIdentifierForcedVitalCapacity'
+ | 'HKQuantityTypeIdentifierHeadphoneAudioExposure'
+ | 'HKQuantityTypeIdentifierHeartRate'
+ | 'HKQuantityTypeIdentifierHeartRateRecoveryOneMinute'
+ | 'HKQuantityTypeIdentifierHeartRateVariabilitySDNN'
+ | 'HKQuantityTypeIdentifierHeight'
+ | 'HKQuantityTypeIdentifierInhalerUsage'
+ | 'HKQuantityTypeIdentifierInsulinDelivery'
+ | 'HKQuantityTypeIdentifierLeanBodyMass'
+ | 'HKQuantityTypeIdentifierNikeFuel'
+ | 'HKQuantityTypeIdentifierNumberOfAlcoholicBeverages'
+ | 'HKQuantityTypeIdentifierNumberOfTimesFallen'
+ | 'HKQuantityTypeIdentifierOxygenSaturation'
+ | 'HKQuantityTypeIdentifierPaddleSportsSpeed'
+ | 'HKQuantityTypeIdentifierPeakExpiratoryFlowRate'
+ | 'HKQuantityTypeIdentifierPeripheralPerfusionIndex'
+ | 'HKQuantityTypeIdentifierPhysicalEffort'
+ | 'HKQuantityTypeIdentifierPushCount'
+ | 'HKQuantityTypeIdentifierRespiratoryRate'
+ | 'HKQuantityTypeIdentifierRestingHeartRate'
+ | 'HKQuantityTypeIdentifierRowingSpeed'
+ | 'HKQuantityTypeIdentifierRunningGroundContactTime'
+ | 'HKQuantityTypeIdentifierRunningPower'
+ | 'HKQuantityTypeIdentifierRunningSpeed'
+ | 'HKQuantityTypeIdentifierRunningStrideLength'
+ | 'HKQuantityTypeIdentifierRunningVerticalOscillation'
+ | 'HKQuantityTypeIdentifierSixMinuteWalkTestDistance'
+ | 'HKQuantityTypeIdentifierStairAscentSpeed'
+ | 'HKQuantityTypeIdentifierStairDescentSpeed'
+ | 'HKQuantityTypeIdentifierStepCount'
+ | 'HKQuantityTypeIdentifierSwimmingStrokeCount'
+ | 'HKQuantityTypeIdentifierTimeInDaylight'
+ | 'HKQuantityTypeIdentifierUnderwaterDepth'
+ | 'HKQuantityTypeIdentifierUVExposure'
+ | 'HKQuantityTypeIdentifierVO2Max'
+ | 'HKQuantityTypeIdentifierWaistCircumference'
+ | 'HKQuantityTypeIdentifierWalkingAsymmetryPercentage'
+ | 'HKQuantityTypeIdentifierWalkingDoubleSupportPercentage'
+ | 'HKQuantityTypeIdentifierWalkingSpeed'
+ | 'HKQuantityTypeIdentifierWalkingStepLength'
+ | 'HKQuantityTypeIdentifierWaterTemperature'
+ | 'HKQuantityTypeIdentifierWorkoutEffortScore'
+export type QuantityTypeIdentifier =
+ | QuantityTypeIdentifierReadOnly
+ | QuantityTypeIdentifierWriteable
+export type CategoryTypeIdentifierReadOnly =
+ | 'HKCategoryTypeIdentifierAppleStandHour'
+ | 'HKCategoryTypeIdentifierHeadphoneAudioExposureEvent'
+ | 'HKCategoryTypeIdentifierHighHeartRateEvent'
+ | 'HKCategoryTypeIdentifierHypertensionEvent'
+ | 'HKCategoryTypeIdentifierLowHeartRateEvent'
+export type CategoryTypeIdentifierWriteable =
+ | 'HKCategoryTypeIdentifierAbdominalCramps'
+ | 'HKCategoryTypeIdentifierAcne'
+ | 'HKCategoryTypeIdentifierAppetiteChanges'
+ | 'HKCategoryTypeIdentifierAppleWalkingSteadinessEvent'
+ | 'HKCategoryTypeIdentifierAudioExposureEvent'
+ | 'HKCategoryTypeIdentifierBladderIncontinence'
+ | 'HKCategoryTypeIdentifierBleedingAfterPregnancy'
+ | 'HKCategoryTypeIdentifierBleedingDuringPregnancy'
+ | 'HKCategoryTypeIdentifierBloating'
+ | 'HKCategoryTypeIdentifierBreastPain'
+ | 'HKCategoryTypeIdentifierCervicalMucusQuality'
+ | 'HKCategoryTypeIdentifierChestTightnessOrPain'
+ | 'HKCategoryTypeIdentifierChills'
+ | 'HKCategoryTypeIdentifierConstipation'
+ | 'HKCategoryTypeIdentifierContraceptive'
+ | 'HKCategoryTypeIdentifierCoughing'
+ | 'HKCategoryTypeIdentifierDiarrhea'
+ | 'HKCategoryTypeIdentifierDizziness'
+ | 'HKCategoryTypeIdentifierDrySkin'
+ | 'HKCategoryTypeIdentifierEnvironmentalAudioExposureEvent'
+ | 'HKCategoryTypeIdentifierFainting'
+ | 'HKCategoryTypeIdentifierFatigue'
+ | 'HKCategoryTypeIdentifierFever'
+ | 'HKCategoryTypeIdentifierGeneralizedBodyAche'
+ | 'HKCategoryTypeIdentifierHairLoss'
+ | 'HKCategoryTypeIdentifierHandwashingEvent'
+ | 'HKCategoryTypeIdentifierHeadache'
+ | 'HKCategoryTypeIdentifierHeartburn'
+ | 'HKCategoryTypeIdentifierHotFlashes'
+ | 'HKCategoryTypeIdentifierInfrequentMenstrualCycles'
+ | 'HKCategoryTypeIdentifierIntermenstrualBleeding'
+ | 'HKCategoryTypeIdentifierIrregularHeartRhythmEvent'
+ | 'HKCategoryTypeIdentifierIrregularMenstrualCycles'
+ | 'HKCategoryTypeIdentifierLactation'
+ | 'HKCategoryTypeIdentifierLossOfSmell'
+ | 'HKCategoryTypeIdentifierLossOfTaste'
+ | 'HKCategoryTypeIdentifierLowCardioFitnessEvent'
+ | 'HKCategoryTypeIdentifierLowerBackPain'
+ | 'HKCategoryTypeIdentifierMemoryLapse'
+ | 'HKCategoryTypeIdentifierMenstrualFlow'
+ | 'HKCategoryTypeIdentifierMindfulSession'
+ | 'HKCategoryTypeIdentifierMoodChanges'
+ | 'HKCategoryTypeIdentifierNausea'
+ | 'HKCategoryTypeIdentifierNightSweats'
+ | 'HKCategoryTypeIdentifierOvulationTestResult'
+ | 'HKCategoryTypeIdentifierPelvicPain'
+ | 'HKCategoryTypeIdentifierPersistentIntermenstrualBleeding'
+ | 'HKCategoryTypeIdentifierPregnancy'
+ | 'HKCategoryTypeIdentifierPregnancyTestResult'
+ | 'HKCategoryTypeIdentifierProgesteroneTestResult'
+ | 'HKCategoryTypeIdentifierProlongedMenstrualPeriods'
+ | 'HKCategoryTypeIdentifierRapidPoundingOrFlutteringHeartbeat'
+ | 'HKCategoryTypeIdentifierRunnyNose'
+ | 'HKCategoryTypeIdentifierSexualActivity'
+ | 'HKCategoryTypeIdentifierShortnessOfBreath'
+ | 'HKCategoryTypeIdentifierSinusCongestion'
+ | 'HKCategoryTypeIdentifierSkippedHeartbeat'
+ | 'HKCategoryTypeIdentifierSleepAnalysis'
+ | 'HKCategoryTypeIdentifierSleepApneaEvent'
+ | 'HKCategoryTypeIdentifierSleepChanges'
+ | 'HKCategoryTypeIdentifierSoreThroat'
+ | 'HKCategoryTypeIdentifierToothbrushingEvent'
+ | 'HKCategoryTypeIdentifierVaginalDryness'
+ | 'HKCategoryTypeIdentifierVomiting'
+ | 'HKCategoryTypeIdentifierWheezing'
+export type CategoryTypeIdentifier =
+ | CategoryTypeIdentifierReadOnly
+ | CategoryTypeIdentifierWriteable
+export const QUANTITY_IDENTIFIER_IOS_AVAILABILITY = {
+ HKQuantityTypeIdentifierActiveEnergyBurned: '8.0',
+ HKQuantityTypeIdentifierAppleExerciseTime: '9.3',
+ HKQuantityTypeIdentifierAppleMoveTime: '14.5',
+ HKQuantityTypeIdentifierAppleSleepingBreathingDisturbances: '18.0',
+ HKQuantityTypeIdentifierAppleSleepingWristTemperature: '16.0',
+ HKQuantityTypeIdentifierAppleStandTime: '13.0',
+ HKQuantityTypeIdentifierAppleWalkingSteadiness: '15.0',
+ HKQuantityTypeIdentifierAtrialFibrillationBurden: '16.0',
+ HKQuantityTypeIdentifierBasalBodyTemperature: '9.0',
+ HKQuantityTypeIdentifierBasalEnergyBurned: '8.0',
+ HKQuantityTypeIdentifierBloodAlcoholContent: '8.0',
+ HKQuantityTypeIdentifierBloodGlucose: '8.0',
+ HKQuantityTypeIdentifierBloodPressureDiastolic: '8.0',
+ HKQuantityTypeIdentifierBloodPressureSystolic: '8.0',
+ HKQuantityTypeIdentifierBodyFatPercentage: '8.0',
+ HKQuantityTypeIdentifierBodyMass: '8.0',
+ HKQuantityTypeIdentifierBodyMassIndex: '8.0',
+ HKQuantityTypeIdentifierBodyTemperature: '8.0',
+ HKQuantityTypeIdentifierCrossCountrySkiingSpeed: '18.0',
+ HKQuantityTypeIdentifierCyclingCadence: '17.0',
+ HKQuantityTypeIdentifierCyclingFunctionalThresholdPower: '17.0',
+ HKQuantityTypeIdentifierCyclingPower: '17.0',
+ HKQuantityTypeIdentifierCyclingSpeed: '17.0',
+ HKQuantityTypeIdentifierDietaryBiotin: '8.0',
+ HKQuantityTypeIdentifierDietaryCaffeine: '8.0',
+ HKQuantityTypeIdentifierDietaryCalcium: '8.0',
+ HKQuantityTypeIdentifierDietaryCarbohydrates: '8.0',
+ HKQuantityTypeIdentifierDietaryChloride: '8.0',
+ HKQuantityTypeIdentifierDietaryCholesterol: '8.0',
+ HKQuantityTypeIdentifierDietaryChromium: '8.0',
+ HKQuantityTypeIdentifierDietaryCopper: '8.0',
+ HKQuantityTypeIdentifierDietaryEnergyConsumed: '8.0',
+ HKQuantityTypeIdentifierDietaryFatMonounsaturated: '8.0',
+ HKQuantityTypeIdentifierDietaryFatPolyunsaturated: '8.0',
+ HKQuantityTypeIdentifierDietaryFatSaturated: '8.0',
+ HKQuantityTypeIdentifierDietaryFatTotal: '8.0',
+ HKQuantityTypeIdentifierDietaryFiber: '8.0',
+ HKQuantityTypeIdentifierDietaryFolate: '8.0',
+ HKQuantityTypeIdentifierDietaryIodine: '8.0',
+ HKQuantityTypeIdentifierDietaryIron: '8.0',
+ HKQuantityTypeIdentifierDietaryMagnesium: '8.0',
+ HKQuantityTypeIdentifierDietaryManganese: '8.0',
+ HKQuantityTypeIdentifierDietaryMolybdenum: '8.0',
+ HKQuantityTypeIdentifierDietaryNiacin: '8.0',
+ HKQuantityTypeIdentifierDietaryPantothenicAcid: '8.0',
+ HKQuantityTypeIdentifierDietaryPhosphorus: '8.0',
+ HKQuantityTypeIdentifierDietaryPotassium: '8.0',
+ HKQuantityTypeIdentifierDietaryProtein: '8.0',
+ HKQuantityTypeIdentifierDietaryRiboflavin: '8.0',
+ HKQuantityTypeIdentifierDietarySelenium: '8.0',
+ HKQuantityTypeIdentifierDietarySodium: '8.0',
+ HKQuantityTypeIdentifierDietarySugar: '8.0',
+ HKQuantityTypeIdentifierDietaryThiamin: '8.0',
+ HKQuantityTypeIdentifierDietaryVitaminA: '8.0',
+ HKQuantityTypeIdentifierDietaryVitaminB12: '8.0',
+ HKQuantityTypeIdentifierDietaryVitaminB6: '8.0',
+ HKQuantityTypeIdentifierDietaryVitaminC: '8.0',
+ HKQuantityTypeIdentifierDietaryVitaminD: '8.0',
+ HKQuantityTypeIdentifierDietaryVitaminE: '8.0',
+ HKQuantityTypeIdentifierDietaryVitaminK: '8.0',
+ HKQuantityTypeIdentifierDietaryWater: '9.0',
+ HKQuantityTypeIdentifierDietaryZinc: '8.0',
+ HKQuantityTypeIdentifierDistanceCrossCountrySkiing: '18.0',
+ HKQuantityTypeIdentifierDistanceCycling: '8.0',
+ HKQuantityTypeIdentifierDistanceDownhillSnowSports: '11.2',
+ HKQuantityTypeIdentifierDistancePaddleSports: '18.0',
+ HKQuantityTypeIdentifierDistanceRowing: '18.0',
+ HKQuantityTypeIdentifierDistanceSkatingSports: '18.0',
+ HKQuantityTypeIdentifierDistanceSwimming: '10.0',
+ HKQuantityTypeIdentifierDistanceWalkingRunning: '8.0',
+ HKQuantityTypeIdentifierDistanceWheelchair: '10.0',
+ HKQuantityTypeIdentifierElectrodermalActivity: '8.0',
+ HKQuantityTypeIdentifierEnvironmentalAudioExposure: '13.0',
+ HKQuantityTypeIdentifierEnvironmentalSoundReduction: '16.0',
+ HKQuantityTypeIdentifierEstimatedWorkoutEffortScore: '18.0',
+ HKQuantityTypeIdentifierFlightsClimbed: '8.0',
+ HKQuantityTypeIdentifierForcedExpiratoryVolume1: '8.0',
+ HKQuantityTypeIdentifierForcedVitalCapacity: '8.0',
+ HKQuantityTypeIdentifierHeadphoneAudioExposure: '13.0',
+ HKQuantityTypeIdentifierHeartRate: '8.0',
+ HKQuantityTypeIdentifierHeartRateRecoveryOneMinute: '16.0',
+ HKQuantityTypeIdentifierHeartRateVariabilitySDNN: '11.0',
+ HKQuantityTypeIdentifierHeight: '8.0',
+ HKQuantityTypeIdentifierInhalerUsage: '8.0',
+ HKQuantityTypeIdentifierInsulinDelivery: '11.0',
+ HKQuantityTypeIdentifierLeanBodyMass: '8.0',
+ HKQuantityTypeIdentifierNikeFuel: '8.0',
+ HKQuantityTypeIdentifierNumberOfAlcoholicBeverages: '15.0',
+ HKQuantityTypeIdentifierNumberOfTimesFallen: '8.0',
+ HKQuantityTypeIdentifierOxygenSaturation: '8.0',
+ HKQuantityTypeIdentifierPaddleSportsSpeed: '18.0',
+ HKQuantityTypeIdentifierPeakExpiratoryFlowRate: '8.0',
+ HKQuantityTypeIdentifierPeripheralPerfusionIndex: '8.0',
+ HKQuantityTypeIdentifierPhysicalEffort: '17.0',
+ HKQuantityTypeIdentifierPushCount: '10.0',
+ HKQuantityTypeIdentifierRespiratoryRate: '8.0',
+ HKQuantityTypeIdentifierRestingHeartRate: '11.0',
+ HKQuantityTypeIdentifierRowingSpeed: '18.0',
+ HKQuantityTypeIdentifierRunningGroundContactTime: '16.0',
+ HKQuantityTypeIdentifierRunningPower: '16.0',
+ HKQuantityTypeIdentifierRunningSpeed: '16.0',
+ HKQuantityTypeIdentifierRunningStrideLength: '16.0',
+ HKQuantityTypeIdentifierRunningVerticalOscillation: '16.0',
+ HKQuantityTypeIdentifierSixMinuteWalkTestDistance: '14.0',
+ HKQuantityTypeIdentifierStairAscentSpeed: '14.0',
+ HKQuantityTypeIdentifierStairDescentSpeed: '14.0',
+ HKQuantityTypeIdentifierStepCount: '8.0',
+ HKQuantityTypeIdentifierSwimmingStrokeCount: '10.0',
+ HKQuantityTypeIdentifierTimeInDaylight: '17.0',
+ HKQuantityTypeIdentifierUnderwaterDepth: '16.0',
+ HKQuantityTypeIdentifierUVExposure: '9.0',
+ HKQuantityTypeIdentifierVO2Max: '11.0',
+ HKQuantityTypeIdentifierWaistCircumference: '11.0',
+ HKQuantityTypeIdentifierWalkingAsymmetryPercentage: '14.0',
+ HKQuantityTypeIdentifierWalkingDoubleSupportPercentage: '14.0',
+ HKQuantityTypeIdentifierWalkingHeartRateAverage: '11.0',
+ HKQuantityTypeIdentifierWalkingSpeed: '14.0',
+ HKQuantityTypeIdentifierWalkingStepLength: '14.0',
+ HKQuantityTypeIdentifierWaterTemperature: '16.0',
+ HKQuantityTypeIdentifierWorkoutEffortScore: '18.0',
+} as const
+export const CATEGORY_IDENTIFIER_IOS_AVAILABILITY = {
+ HKCategoryTypeIdentifierAbdominalCramps: '13.6',
+ HKCategoryTypeIdentifierAcne: '13.6',
+ HKCategoryTypeIdentifierAppetiteChanges: '13.6',
+ HKCategoryTypeIdentifierAppleStandHour: '9.0',
+ HKCategoryTypeIdentifierAppleWalkingSteadinessEvent: '15.0',
+ HKCategoryTypeIdentifierAudioExposureEvent: '13.0',
+ HKCategoryTypeIdentifierBladderIncontinence: '14.0',
+ HKCategoryTypeIdentifierBleedingAfterPregnancy: '18.0',
+ HKCategoryTypeIdentifierBleedingDuringPregnancy: '18.0',
+ HKCategoryTypeIdentifierBloating: '13.6',
+ HKCategoryTypeIdentifierBreastPain: '13.6',
+ HKCategoryTypeIdentifierCervicalMucusQuality: '9.0',
+ HKCategoryTypeIdentifierChestTightnessOrPain: '13.6',
+ HKCategoryTypeIdentifierChills: '13.6',
+ HKCategoryTypeIdentifierConstipation: '13.6',
+ HKCategoryTypeIdentifierContraceptive: '14.3',
+ HKCategoryTypeIdentifierCoughing: '13.6',
+ HKCategoryTypeIdentifierDiarrhea: '13.6',
+ HKCategoryTypeIdentifierDizziness: '13.6',
+ HKCategoryTypeIdentifierDrySkin: '14.0',
+ HKCategoryTypeIdentifierEnvironmentalAudioExposureEvent: '14.0',
+ HKCategoryTypeIdentifierFainting: '13.6',
+ HKCategoryTypeIdentifierFatigue: '13.6',
+ HKCategoryTypeIdentifierFever: '13.6',
+ HKCategoryTypeIdentifierGeneralizedBodyAche: '13.6',
+ HKCategoryTypeIdentifierHairLoss: '14.0',
+ HKCategoryTypeIdentifierHandwashingEvent: '14.0',
+ HKCategoryTypeIdentifierHeadache: '13.6',
+ HKCategoryTypeIdentifierHeadphoneAudioExposureEvent: '14.2',
+ HKCategoryTypeIdentifierHeartburn: '13.6',
+ HKCategoryTypeIdentifierHighHeartRateEvent: '12.2',
+ HKCategoryTypeIdentifierHotFlashes: '13.6',
+ HKCategoryTypeIdentifierHypertensionEvent: '26.2',
+ HKCategoryTypeIdentifierInfrequentMenstrualCycles: '16.0',
+ HKCategoryTypeIdentifierIntermenstrualBleeding: '9.0',
+ HKCategoryTypeIdentifierIrregularHeartRhythmEvent: '12.2',
+ HKCategoryTypeIdentifierIrregularMenstrualCycles: '16.0',
+ HKCategoryTypeIdentifierLactation: '14.3',
+ HKCategoryTypeIdentifierLossOfSmell: '13.6',
+ HKCategoryTypeIdentifierLossOfTaste: '13.6',
+ HKCategoryTypeIdentifierLowCardioFitnessEvent: '14.3',
+ HKCategoryTypeIdentifierLowerBackPain: '13.6',
+ HKCategoryTypeIdentifierLowHeartRateEvent: '12.2',
+ HKCategoryTypeIdentifierMemoryLapse: '14.0',
+ HKCategoryTypeIdentifierMenstrualFlow: '9.0',
+ HKCategoryTypeIdentifierMindfulSession: '10.0',
+ HKCategoryTypeIdentifierMoodChanges: '13.6',
+ HKCategoryTypeIdentifierNausea: '13.6',
+ HKCategoryTypeIdentifierNightSweats: '14.0',
+ HKCategoryTypeIdentifierOvulationTestResult: '9.0',
+ HKCategoryTypeIdentifierPelvicPain: '13.6',
+ HKCategoryTypeIdentifierPersistentIntermenstrualBleeding: '16.0',
+ HKCategoryTypeIdentifierPregnancy: '14.3',
+ HKCategoryTypeIdentifierPregnancyTestResult: '15.0',
+ HKCategoryTypeIdentifierProgesteroneTestResult: '15.0',
+ HKCategoryTypeIdentifierProlongedMenstrualPeriods: '16.0',
+ HKCategoryTypeIdentifierRapidPoundingOrFlutteringHeartbeat: '13.6',
+ HKCategoryTypeIdentifierRunnyNose: '13.6',
+ HKCategoryTypeIdentifierSexualActivity: '9.0',
+ HKCategoryTypeIdentifierShortnessOfBreath: '13.6',
+ HKCategoryTypeIdentifierSinusCongestion: '13.6',
+ HKCategoryTypeIdentifierSkippedHeartbeat: '13.6',
+ HKCategoryTypeIdentifierSleepAnalysis: '8.0',
+ HKCategoryTypeIdentifierSleepApneaEvent: '18.0',
+ HKCategoryTypeIdentifierSleepChanges: '13.6',
+ HKCategoryTypeIdentifierSoreThroat: '13.6',
+ HKCategoryTypeIdentifierToothbrushingEvent: '13.0',
+ HKCategoryTypeIdentifierVaginalDryness: '14.0',
+ HKCategoryTypeIdentifierVomiting: '13.6',
+ HKCategoryTypeIdentifierWheezing: '13.6',
+} as const
+export const QUANTITY_IDENTIFIER_CANONICAL_UNITS = {
+ HKQuantityTypeIdentifierActiveEnergyBurned: 'kcal',
+ HKQuantityTypeIdentifierAppleExerciseTime: 'min',
+ HKQuantityTypeIdentifierAppleMoveTime: 'min',
+ HKQuantityTypeIdentifierAppleSleepingBreathingDisturbances: 'count',
+ HKQuantityTypeIdentifierAppleSleepingWristTemperature: 'degC',
+ HKQuantityTypeIdentifierAppleStandTime: 'min',
+ HKQuantityTypeIdentifierAppleWalkingSteadiness: '%',
+ HKQuantityTypeIdentifierAtrialFibrillationBurden: '%',
+ HKQuantityTypeIdentifierBasalBodyTemperature: 'degC',
+ HKQuantityTypeIdentifierBasalEnergyBurned: 'kcal',
+ HKQuantityTypeIdentifierBloodAlcoholContent: '%',
+ HKQuantityTypeIdentifierBloodGlucose: 'mg/dL',
+ HKQuantityTypeIdentifierBloodPressureDiastolic: 'mmHg',
+ HKQuantityTypeIdentifierBloodPressureSystolic: 'mmHg',
+ HKQuantityTypeIdentifierBodyFatPercentage: '%',
+ HKQuantityTypeIdentifierBodyMass: 'kg',
+ HKQuantityTypeIdentifierBodyMassIndex: 'count',
+ HKQuantityTypeIdentifierBodyTemperature: 'degC',
+ HKQuantityTypeIdentifierCrossCountrySkiingSpeed: 'm/s',
+ HKQuantityTypeIdentifierCyclingCadence: 'count/min',
+ HKQuantityTypeIdentifierCyclingFunctionalThresholdPower: 'W',
+ HKQuantityTypeIdentifierCyclingPower: 'W',
+ HKQuantityTypeIdentifierCyclingSpeed: 'm/s',
+ HKQuantityTypeIdentifierDietaryBiotin: 'g',
+ HKQuantityTypeIdentifierDietaryCaffeine: 'g',
+ HKQuantityTypeIdentifierDietaryCalcium: 'g',
+ HKQuantityTypeIdentifierDietaryCarbohydrates: 'g',
+ HKQuantityTypeIdentifierDietaryChloride: 'g',
+ HKQuantityTypeIdentifierDietaryCholesterol: 'g',
+ HKQuantityTypeIdentifierDietaryChromium: 'g',
+ HKQuantityTypeIdentifierDietaryCopper: 'g',
+ HKQuantityTypeIdentifierDietaryEnergyConsumed: 'kcal',
+ HKQuantityTypeIdentifierDietaryFatMonounsaturated: 'g',
+ HKQuantityTypeIdentifierDietaryFatPolyunsaturated: 'g',
+ HKQuantityTypeIdentifierDietaryFatSaturated: 'g',
+ HKQuantityTypeIdentifierDietaryFatTotal: 'g',
+ HKQuantityTypeIdentifierDietaryFiber: 'g',
+ HKQuantityTypeIdentifierDietaryFolate: 'g',
+ HKQuantityTypeIdentifierDietaryIodine: 'g',
+ HKQuantityTypeIdentifierDietaryIron: 'g',
+ HKQuantityTypeIdentifierDietaryMagnesium: 'g',
+ HKQuantityTypeIdentifierDietaryManganese: 'g',
+ HKQuantityTypeIdentifierDietaryMolybdenum: 'g',
+ HKQuantityTypeIdentifierDietaryNiacin: 'g',
+ HKQuantityTypeIdentifierDietaryPantothenicAcid: 'g',
+ HKQuantityTypeIdentifierDietaryPhosphorus: 'g',
+ HKQuantityTypeIdentifierDietaryPotassium: 'g',
+ HKQuantityTypeIdentifierDietaryProtein: 'g',
+ HKQuantityTypeIdentifierDietaryRiboflavin: 'g',
+ HKQuantityTypeIdentifierDietarySelenium: 'g',
+ HKQuantityTypeIdentifierDietarySodium: 'g',
+ HKQuantityTypeIdentifierDietarySugar: 'g',
+ HKQuantityTypeIdentifierDietaryThiamin: 'g',
+ HKQuantityTypeIdentifierDietaryVitaminA: 'g',
+ HKQuantityTypeIdentifierDietaryVitaminB12: 'g',
+ HKQuantityTypeIdentifierDietaryVitaminB6: 'g',
+ HKQuantityTypeIdentifierDietaryVitaminC: 'g',
+ HKQuantityTypeIdentifierDietaryVitaminD: 'g',
+ HKQuantityTypeIdentifierDietaryVitaminE: 'g',
+ HKQuantityTypeIdentifierDietaryVitaminK: 'g',
+ HKQuantityTypeIdentifierDietaryWater: 'mL',
+ HKQuantityTypeIdentifierDietaryZinc: 'g',
+ HKQuantityTypeIdentifierDistanceCrossCountrySkiing: 'm',
+ HKQuantityTypeIdentifierDistanceCycling: 'm',
+ HKQuantityTypeIdentifierDistanceDownhillSnowSports: 'm',
+ HKQuantityTypeIdentifierDistancePaddleSports: 'm',
+ HKQuantityTypeIdentifierDistanceRowing: 'm',
+ HKQuantityTypeIdentifierDistanceSkatingSports: 'm',
+ HKQuantityTypeIdentifierDistanceSwimming: 'm',
+ HKQuantityTypeIdentifierDistanceWalkingRunning: 'm',
+ HKQuantityTypeIdentifierDistanceWheelchair: 'm',
+ HKQuantityTypeIdentifierElectrodermalActivity: 'S',
+ HKQuantityTypeIdentifierEnvironmentalAudioExposure: 'dBASPL',
+ HKQuantityTypeIdentifierEnvironmentalSoundReduction: 'dBASPL',
+ HKQuantityTypeIdentifierEstimatedWorkoutEffortScore: 'appleEffortScore',
+ HKQuantityTypeIdentifierFlightsClimbed: 'count',
+ HKQuantityTypeIdentifierForcedExpiratoryVolume1: 'L',
+ HKQuantityTypeIdentifierForcedVitalCapacity: 'L',
+ HKQuantityTypeIdentifierHeadphoneAudioExposure: 'dBASPL',
+ HKQuantityTypeIdentifierHeartRate: 'count/s',
+ HKQuantityTypeIdentifierHeartRateRecoveryOneMinute: 'count/min',
+ HKQuantityTypeIdentifierHeartRateVariabilitySDNN: 'ms',
+ HKQuantityTypeIdentifierHeight: 'm',
+ HKQuantityTypeIdentifierInhalerUsage: 'count',
+ HKQuantityTypeIdentifierInsulinDelivery: 'IU',
+ HKQuantityTypeIdentifierLeanBodyMass: 'kg',
+ HKQuantityTypeIdentifierNikeFuel: 'count',
+ HKQuantityTypeIdentifierNumberOfAlcoholicBeverages: 'count',
+ HKQuantityTypeIdentifierNumberOfTimesFallen: 'count',
+ HKQuantityTypeIdentifierOxygenSaturation: '%',
+ HKQuantityTypeIdentifierPaddleSportsSpeed: 'm/s',
+ HKQuantityTypeIdentifierPeakExpiratoryFlowRate: 'L/min',
+ HKQuantityTypeIdentifierPeripheralPerfusionIndex: '%',
+ HKQuantityTypeIdentifierPhysicalEffort: 'kcal/(kg*hr)',
+ HKQuantityTypeIdentifierPushCount: 'count',
+ HKQuantityTypeIdentifierRespiratoryRate: 'count/s',
+ HKQuantityTypeIdentifierRestingHeartRate: 'count/min',
+ HKQuantityTypeIdentifierRowingSpeed: 'm/s',
+ HKQuantityTypeIdentifierRunningGroundContactTime: 'ms',
+ HKQuantityTypeIdentifierRunningPower: 'W',
+ HKQuantityTypeIdentifierRunningSpeed: 'm/s',
+ HKQuantityTypeIdentifierRunningStrideLength: 'm',
+ HKQuantityTypeIdentifierRunningVerticalOscillation: 'cm',
+ HKQuantityTypeIdentifierSixMinuteWalkTestDistance: 'm',
+ HKQuantityTypeIdentifierStairAscentSpeed: 'm/s',
+ HKQuantityTypeIdentifierStairDescentSpeed: 'm/s',
+ HKQuantityTypeIdentifierStepCount: 'count',
+ HKQuantityTypeIdentifierSwimmingStrokeCount: 'count',
+ HKQuantityTypeIdentifierTimeInDaylight: 'min',
+ HKQuantityTypeIdentifierUnderwaterDepth: 'm',
+ HKQuantityTypeIdentifierUVExposure: null,
+ HKQuantityTypeIdentifierVO2Max: 'ml/(kg*min)',
+ HKQuantityTypeIdentifierWaistCircumference: 'm',
+ HKQuantityTypeIdentifierWalkingAsymmetryPercentage: '%',
+ HKQuantityTypeIdentifierWalkingDoubleSupportPercentage: '%',
+ HKQuantityTypeIdentifierWalkingHeartRateAverage: 'count/min',
+ HKQuantityTypeIdentifierWalkingSpeed: 'm/s',
+ HKQuantityTypeIdentifierWalkingStepLength: 'm',
+ HKQuantityTypeIdentifierWaterTemperature: 'degC',
+ HKQuantityTypeIdentifierWorkoutEffortScore: 'appleEffortScore',
+} as const
+export const CATEGORY_IDENTIFIER_VALUE_ENUMS = {
+ HKCategoryTypeIdentifierAbdominalCramps: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierAcne: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierAppetiteChanges: 'CategoryValueAppetiteChanges',
+ HKCategoryTypeIdentifierAppleStandHour: 'CategoryValueAppleStandHour',
+ HKCategoryTypeIdentifierAppleWalkingSteadinessEvent:
+ 'CategoryValueAppleWalkingSteadinessEvent',
+ HKCategoryTypeIdentifierAudioExposureEvent: null,
+ HKCategoryTypeIdentifierBladderIncontinence: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierBleedingAfterPregnancy:
+ 'CategoryValueVaginalBleeding',
+ HKCategoryTypeIdentifierBleedingDuringPregnancy:
+ 'CategoryValueVaginalBleeding',
+ HKCategoryTypeIdentifierBloating: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierBreastPain: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierCervicalMucusQuality:
+ 'CategoryValueCervicalMucusQuality',
+ HKCategoryTypeIdentifierChestTightnessOrPain: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierChills: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierConstipation: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierContraceptive: 'CategoryValueContraceptive',
+ HKCategoryTypeIdentifierCoughing: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierDiarrhea: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierDizziness: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierDrySkin: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierEnvironmentalAudioExposureEvent:
+ 'CategoryValueEnvironmentalAudioExposureEvent',
+ HKCategoryTypeIdentifierFainting: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierFatigue: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierFever: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierGeneralizedBodyAche: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierHairLoss: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierHandwashingEvent: 'CategoryValue',
+ HKCategoryTypeIdentifierHeadache: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierHeadphoneAudioExposureEvent:
+ 'CategoryValueHeadphoneAudioExposureEvent',
+ HKCategoryTypeIdentifierHeartburn: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierHighHeartRateEvent: 'CategoryValue',
+ HKCategoryTypeIdentifierHotFlashes: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierHypertensionEvent: 'CategoryValue',
+ HKCategoryTypeIdentifierInfrequentMenstrualCycles: 'CategoryValue',
+ HKCategoryTypeIdentifierIntermenstrualBleeding: 'CategoryValue',
+ HKCategoryTypeIdentifierIrregularHeartRhythmEvent: 'CategoryValue',
+ HKCategoryTypeIdentifierIrregularMenstrualCycles: 'CategoryValue',
+ HKCategoryTypeIdentifierLactation: 'CategoryValue',
+ HKCategoryTypeIdentifierLossOfSmell: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierLossOfTaste: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierLowCardioFitnessEvent:
+ 'CategoryValueLowCardioFitnessEvent',
+ HKCategoryTypeIdentifierLowerBackPain: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierLowHeartRateEvent: 'CategoryValue',
+ HKCategoryTypeIdentifierMemoryLapse: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierMenstrualFlow: 'CategoryValueMenstrualFlow',
+ HKCategoryTypeIdentifierMindfulSession: 'CategoryValue',
+ HKCategoryTypeIdentifierMoodChanges: 'CategoryValuePresence',
+ HKCategoryTypeIdentifierNausea: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierNightSweats: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierOvulationTestResult:
+ 'CategoryValueOvulationTestResult',
+ HKCategoryTypeIdentifierPelvicPain: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierPersistentIntermenstrualBleeding: 'CategoryValue',
+ HKCategoryTypeIdentifierPregnancy: 'CategoryValue',
+ HKCategoryTypeIdentifierPregnancyTestResult:
+ 'CategoryValuePregnancyTestResult',
+ HKCategoryTypeIdentifierProgesteroneTestResult:
+ 'CategoryValueProgesteroneTestResult',
+ HKCategoryTypeIdentifierProlongedMenstrualPeriods: 'CategoryValue',
+ HKCategoryTypeIdentifierRapidPoundingOrFlutteringHeartbeat:
+ 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierRunnyNose: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierSexualActivity: 'CategoryValue',
+ HKCategoryTypeIdentifierShortnessOfBreath: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierSinusCongestion: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierSkippedHeartbeat: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierSleepAnalysis: 'CategoryValueSleepAnalysis',
+ HKCategoryTypeIdentifierSleepApneaEvent: 'CategoryValue',
+ HKCategoryTypeIdentifierSleepChanges: 'CategoryValuePresence',
+ HKCategoryTypeIdentifierSoreThroat: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierToothbrushingEvent: 'CategoryValue',
+ HKCategoryTypeIdentifierVaginalDryness: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierVomiting: 'CategoryValueSeverity',
+ HKCategoryTypeIdentifierWheezing: 'CategoryValueSeverity',
+} as const
+export enum AppleECGAlgorithmVersion {
+ value1 = 1,
+ value2 = 2,
+}
+export enum BloodGlucoseMealTime {
+ preprandial = 1,
+ postprandial = 2,
+}
+export enum BodyTemperatureSensorLocation {
+ other = 0,
+ armpit = 1,
+ body = 2,
+ ear = 3,
+ finger = 4,
+ gastroIntestinal = 5,
+ mouth = 6,
+ rectum = 7,
+ toe = 8,
+ earDrum = 9,
+ temporalArtery = 10,
+ forehead = 11,
+}
+export enum CategoryValue {
+ notApplicable = 0,
+}
+export enum CategoryValueAppetiteChanges {
+ unspecified = 0,
+ noChange = 1,
+ decreased = 2,
+ increased = 3,
+}
+export enum CategoryValueAppleStandHour {
+ stood = 0,
+ idle = 1,
+}
+export enum CategoryValueAppleWalkingSteadinessEvent {
+ initialLow = 1,
+ initialVeryLow = 2,
+ repeatLow = 3,
+ repeatVeryLow = 4,
+}
+export enum CategoryValueAudioExposureEvent {
+ loudEnvironment = 1,
+}
+export enum CategoryValueCervicalMucusQuality {
+ dry = 1,
+ sticky = 2,
+ creamy = 3,
+ watery = 4,
+ eggWhite = 5,
+}
+export enum CategoryValueContraceptive {
+ unspecified = 1,
+ implant = 2,
+ injection = 3,
+ intrauterineDevice = 4,
+ intravaginalRing = 5,
+ oral = 6,
+ patch = 7,
+}
+export enum CategoryValueEnvironmentalAudioExposureEvent {
+ momentaryLimit = 1,
+}
+export enum CategoryValueHeadphoneAudioExposureEvent {
+ sevenDayLimit = 1,
+}
+export enum CategoryValueLowCardioFitnessEvent {
+ lowFitness = 1,
+}
+export enum CategoryValueMenstrualFlow {
+ unspecified = 1,
+ light = 2,
+ medium = 3,
+ heavy = 4,
+ none = 5,
+}
+export enum CategoryValueOvulationTestResult {
+ negative = 1,
+ luteinizingHormoneSurge = 2,
+ positive = 2,
+ indeterminate = 3,
+ estrogenSurge = 4,
+}
+export enum CategoryValuePregnancyTestResult {
+ negative = 1,
+ positive = 2,
+ indeterminate = 3,
+}
+export enum CategoryValuePresence {
+ present = 0,
+ notPresent = 1,
+}
+export enum CategoryValueProgesteroneTestResult {
+ negative = 1,
+ positive = 2,
+ indeterminate = 3,
+}
+export enum CategoryValueSeverity {
+ unspecified = 0,
+ notPresent = 1,
+ mild = 2,
+ moderate = 3,
+ severe = 4,
+}
+export enum CategoryValueSleepAnalysis {
+ inBed = 0,
+ asleepUnspecified = 1,
+ asleep = 1,
+ awake = 2,
+ asleepCore = 3,
+ asleepDeep = 4,
+ asleepREM = 5,
+}
+export enum CategoryValueVaginalBleeding {
+ unspecified = 1,
+ light = 2,
+ medium = 3,
+ heavy = 4,
+ none = 5,
+}
+export enum CyclingFunctionalThresholdPowerTestType {
+ maxExercise60Minute = 1,
+ maxExercise20Minute = 2,
+ rampTest = 3,
+ predictionExercise = 4,
+}
+export enum DevicePlacementSide {
+ unknown = 0,
+ left = 1,
+ right = 2,
+ central = 3,
+}
+export enum HeartRateMotionContext {
+ notSet = 0,
+ sedentary = 1,
+ active = 2,
+}
+export enum HeartRateRecoveryTestType {
+ maxExercise = 1,
+ predictionSubMaxExercise = 2,
+ predictionNonExercise = 3,
+}
+export enum HeartRateSensorLocation {
+ other = 0,
+ chest = 1,
+ wrist = 2,
+ finger = 3,
+ hand = 4,
+ earLobe = 5,
+ foot = 6,
+}
+export enum InsulinDeliveryReason {
+ basal = 1,
+ bolus = 2,
+}
+export enum PhysicalEffortEstimationType {
+ activityLookup = 1,
+ deviceSensed = 2,
+}
+export enum SwimmingStrokeStyle {
+ unknown = 0,
+ mixed = 1,
+ freestyle = 2,
+ backstroke = 3,
+ breaststroke = 4,
+ butterfly = 5,
+ kickboard = 6,
+}
+export enum UserMotionContext {
+ notSet = 0,
+ stationary = 1,
+ active = 2,
+}
+export enum VO2MaxTestType {
+ maxExercise = 1,
+ predictionSubMaxExercise = 2,
+ predictionNonExercise = 3,
+ predictionStepTest = 4,
+}
+export enum WaterSalinity {
+ freshWater = 1,
+ saltWater = 2,
+}
+export enum WeatherCondition {
+ none = 0,
+ clear = 1,
+ fair = 2,
+ partlyCloudy = 3,
+ mostlyCloudy = 4,
+ cloudy = 5,
+ foggy = 6,
+ haze = 7,
+ windy = 8,
+ blustery = 9,
+ smoky = 10,
+ dust = 11,
+ snow = 12,
+ hail = 13,
+ sleet = 14,
+ freezingDrizzle = 15,
+ freezingRain = 16,
+ mixedRainAndHail = 17,
+ mixedRainAndSnow = 18,
+ mixedRainAndSleet = 19,
+ mixedSnowAndSleet = 20,
+ drizzle = 21,
+ scatteredShowers = 22,
+ showers = 23,
+ thunderstorms = 24,
+ tropicalStorm = 25,
+ hurricane = 26,
+ tornado = 27,
+}
+export enum WorkoutActivityType {
+ americanFootball = 1,
+ archery = 2,
+ australianFootball = 3,
+ badminton = 4,
+ baseball = 5,
+ basketball = 6,
+ bowling = 7,
+ boxing = 8,
+ climbing = 9,
+ cricket = 10,
+ crossTraining = 11,
+ curling = 12,
+ cycling = 13,
+ dance = 14,
+ danceInspiredTraining = 15,
+ elliptical = 16,
+ equestrianSports = 17,
+ fencing = 18,
+ fishing = 19,
+ functionalStrengthTraining = 20,
+ golf = 21,
+ gymnastics = 22,
+ handball = 23,
+ hiking = 24,
+ hockey = 25,
+ hunting = 26,
+ lacrosse = 27,
+ martialArts = 28,
+ mindAndBody = 29,
+ mixedMetabolicCardioTraining = 30,
+ paddleSports = 31,
+ play = 32,
+ preparationAndRecovery = 33,
+ racquetball = 34,
+ rowing = 35,
+ rugby = 36,
+ running = 37,
+ sailing = 38,
+ skatingSports = 39,
+ snowSports = 40,
+ soccer = 41,
+ softball = 42,
+ squash = 43,
+ stairClimbing = 44,
+ surfingSports = 45,
+ swimming = 46,
+ tableTennis = 47,
+ tennis = 48,
+ trackAndField = 49,
+ traditionalStrengthTraining = 50,
+ volleyball = 51,
+ walking = 52,
+ waterFitness = 53,
+ waterPolo = 54,
+ waterSports = 55,
+ wrestling = 56,
+ yoga = 57,
+ barre = 58,
+ coreTraining = 59,
+ crossCountrySkiing = 60,
+ downhillSkiing = 61,
+ flexibility = 62,
+ highIntensityIntervalTraining = 63,
+ jumpRope = 64,
+ kickboxing = 65,
+ pilates = 66,
+ snowboarding = 67,
+ stairs = 68,
+ stepTraining = 69,
+ wheelchairWalkPace = 70,
+ wheelchairRunPace = 71,
+ taiChi = 72,
+ mixedCardio = 73,
+ handCycling = 74,
+ discSports = 75,
+ fitnessGaming = 76,
+ cardioDance = 77,
+ socialDance = 78,
+ pickleball = 79,
+ cooldown = 80,
+ swimBikeRun = 82,
+ transition = 83,
+ underwaterDiving = 84,
+ other = 3000,
+}
+export enum WorkoutEventType {
+ pause = 1,
+ resume = 2,
+ lap = 3,
+ marker = 4,
+ motionPaused = 5,
+ motionResumed = 6,
+ segment = 7,
+ pauseOrResumeRequest = 8,
+}
+export enum WorkoutSwimmingLocationType {
+ unknown = 0,
+ pool = 1,
+ openWater = 2,
+}
+export interface CategoryValueByIdentifierMap {
+ readonly HKCategoryTypeIdentifierAbdominalCramps: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierAcne: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierAppetiteChanges: CategoryValueAppetiteChanges
+ readonly HKCategoryTypeIdentifierAppleStandHour: CategoryValueAppleStandHour
+ readonly HKCategoryTypeIdentifierAppleWalkingSteadinessEvent: CategoryValueAppleWalkingSteadinessEvent
+ readonly HKCategoryTypeIdentifierBladderIncontinence: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierBleedingAfterPregnancy: CategoryValueVaginalBleeding
+ readonly HKCategoryTypeIdentifierBleedingDuringPregnancy: CategoryValueVaginalBleeding
+ readonly HKCategoryTypeIdentifierBloating: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierBreastPain: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierCervicalMucusQuality: CategoryValueCervicalMucusQuality
+ readonly HKCategoryTypeIdentifierChestTightnessOrPain: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierChills: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierConstipation: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierContraceptive: CategoryValueContraceptive
+ readonly HKCategoryTypeIdentifierCoughing: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierDiarrhea: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierDizziness: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierDrySkin: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierEnvironmentalAudioExposureEvent: CategoryValueEnvironmentalAudioExposureEvent
+ readonly HKCategoryTypeIdentifierFainting: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierFatigue: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierFever: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierGeneralizedBodyAche: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierHairLoss: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierHandwashingEvent: CategoryValue
+ readonly HKCategoryTypeIdentifierHeadache: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierHeadphoneAudioExposureEvent: CategoryValueHeadphoneAudioExposureEvent
+ readonly HKCategoryTypeIdentifierHeartburn: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierHighHeartRateEvent: CategoryValue
+ readonly HKCategoryTypeIdentifierHotFlashes: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierHypertensionEvent: CategoryValue
+ readonly HKCategoryTypeIdentifierInfrequentMenstrualCycles: CategoryValue
+ readonly HKCategoryTypeIdentifierIntermenstrualBleeding: CategoryValue
+ readonly HKCategoryTypeIdentifierIrregularHeartRhythmEvent: CategoryValue
+ readonly HKCategoryTypeIdentifierIrregularMenstrualCycles: CategoryValue
+ readonly HKCategoryTypeIdentifierLactation: CategoryValue
+ readonly HKCategoryTypeIdentifierLossOfSmell: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierLossOfTaste: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierLowCardioFitnessEvent: CategoryValueLowCardioFitnessEvent
+ readonly HKCategoryTypeIdentifierLowerBackPain: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierLowHeartRateEvent: CategoryValue
+ readonly HKCategoryTypeIdentifierMemoryLapse: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierMenstrualFlow: CategoryValueMenstrualFlow
+ readonly HKCategoryTypeIdentifierMindfulSession: CategoryValue
+ readonly HKCategoryTypeIdentifierMoodChanges: CategoryValuePresence
+ readonly HKCategoryTypeIdentifierNausea: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierNightSweats: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierOvulationTestResult: CategoryValueOvulationTestResult
+ readonly HKCategoryTypeIdentifierPelvicPain: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierPersistentIntermenstrualBleeding: CategoryValue
+ readonly HKCategoryTypeIdentifierPregnancy: CategoryValue
+ readonly HKCategoryTypeIdentifierPregnancyTestResult: CategoryValuePregnancyTestResult
+ readonly HKCategoryTypeIdentifierProgesteroneTestResult: CategoryValueProgesteroneTestResult
+ readonly HKCategoryTypeIdentifierProlongedMenstrualPeriods: CategoryValue
+ readonly HKCategoryTypeIdentifierRapidPoundingOrFlutteringHeartbeat: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierRunnyNose: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierSexualActivity: CategoryValue
+ readonly HKCategoryTypeIdentifierShortnessOfBreath: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierSinusCongestion: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierSkippedHeartbeat: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierSleepAnalysis: CategoryValueSleepAnalysis
+ readonly HKCategoryTypeIdentifierSleepApneaEvent: CategoryValue
+ readonly HKCategoryTypeIdentifierSleepChanges: CategoryValuePresence
+ readonly HKCategoryTypeIdentifierSoreThroat: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierToothbrushingEvent: CategoryValue
+ readonly HKCategoryTypeIdentifierVaginalDryness: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierVomiting: CategoryValueSeverity
+ readonly HKCategoryTypeIdentifierWheezing: CategoryValueSeverity
+}
+export type CategoryValueForIdentifierGenerated<
+ T extends CategoryTypeIdentifier = CategoryTypeIdentifier,
+> = T extends keyof CategoryValueByIdentifierMap
+ ? CategoryValueByIdentifierMap[T]
+ : number
+export interface KnownObjectMetadata {
+ readonly HKDeviceManufacturerName?: string
+ readonly HKDeviceName?: string
+ readonly HKDeviceSerialNumber?: string
+ readonly HKDigitalSignature?: string
+ readonly HKExternalUUID?: string
+ readonly HKFoodType?: string
+ readonly HKReferenceRangeLowerLimit?: number
+ readonly HKReferenceRangeUpperLimit?: number
+ readonly HKSyncIdentifier?: string
+ readonly HKSyncVersion?: number
+ readonly HKTimeZone?: string
+ readonly HKUDIDeviceIdentifier?: string
+ readonly HKUDIProductionIdentifier?: string
+ readonly HKWasTakenInLab?: boolean
+ readonly HKWasUserEntered?: boolean
+}
+export interface KnownSampleMetadata extends KnownObjectMetadata {
+ readonly HKActivityType?: WorkoutActivityType
+ readonly HKAlgorithmVersion?: number
+ readonly HKAppleDeviceCalibrated?: boolean
+ readonly HKAudioExposureDuration?: Quantity
+ readonly HKBarometricPressure?: Quantity
+ readonly HKDateOfEarliestDataUsedForEstimate?: string
+ readonly HKMaximumLightIntensity?: Quantity
+ readonly HKPhysicalEffortEstimationType?: PhysicalEffortEstimationType
+ readonly HKUserMotionContext?: UserMotionContext
+ readonly HKWaterSalinity?: WaterSalinity
+ readonly HKWeatherCondition?: WeatherCondition
+ readonly HKWeatherHumidity?: Quantity
+ readonly HKWeatherTemperature?: Quantity
+}
+export interface CategoryTypedMetadata extends KnownSampleMetadata {
+ readonly HKAudioExposureLevel?: Quantity
+ readonly HKHeadphoneGain?: Quantity
+ readonly HKHeartRateEventThreshold?: Quantity
+ readonly HKLowCardioFitnessEventThreshold?: Quantity
+ readonly HKMenstrualCycleStart?: boolean
+ readonly HKSexualActivityProtectionUsed?: boolean
+ readonly HKVO2MaxValue?: Quantity
+}
+export interface QuantityTypedMetadata extends KnownSampleMetadata {
+ readonly HKHeartRateMotionContext?: HeartRateMotionContext
+ readonly HKHeartRateRecoveryActivityDuration?: Quantity
+ readonly HKHeartRateRecoveryActivityType?: WorkoutActivityType
+ readonly HKHeartRateRecoveryMaxObservedRecoveryHeartRate?: Quantity
+ readonly HKHeartRateRecoveryTestType?: HeartRateRecoveryTestType
+ readonly HKHeartRateSensorLocation?: HeartRateSensorLocation
+ readonly HKQuantityClampedToLowerBound?: boolean
+ readonly HKQuantityClampedToUpperBound?: boolean
+ readonly HKSessionEstimate?: Quantity
+ readonly HKBloodGlucoseMealTime?: BloodGlucoseMealTime
+ readonly HKBodyTemperatureSensorLocation?: BodyTemperatureSensorLocation
+ readonly HKInsulinDeliveryReason?: InsulinDeliveryReason
+ readonly HKVO2MaxTestType?: VO2MaxTestType
+}
+export interface WorkoutTypedMetadata extends KnownSampleMetadata {
+ readonly HKAlpineSlopeGrade?: Quantity
+ readonly HKAppleFitnessPlusCatalogIdentifier?: string
+ readonly HKAppleFitnessPlusSession?: boolean
+ readonly HKAverageMETs?: Quantity
+ readonly HKAverageSpeed?: Quantity
+ readonly HKCoachedWorkout?: boolean
+ readonly HKCrossTrainerDistance?: Quantity
+ readonly HKElevationAscended?: Quantity
+ readonly HKElevationDescended?: Quantity
+ readonly HKFitnessMachineDuration?: Quantity
+ readonly HKGroupFitness?: boolean
+ readonly HKIndoorBikeDistance?: Quantity
+ readonly HKIndoorWorkout?: boolean
+ readonly HKLapLength?: Quantity
+ readonly HKMaximumSpeed?: Quantity
+ readonly HKSwimmingLocationType?: WorkoutSwimmingLocationType
+ readonly HKSWOLFScore?: number
+ readonly HKWeatherCondition?: WeatherCondition
+ readonly HKWeatherHumidity?: Quantity
+ readonly HKWeatherTemperature?: Quantity
+ readonly HKWorkoutBrandName?: string
+}
+export interface WorkoutEventTypedMetadata {
+ readonly HKSwimmingStrokeStyle?: SwimmingStrokeStyle
+}
+export interface CategorySpecificMetadataByIdentifierMap {
+ readonly HKCategoryTypeIdentifierAudioExposureEvent: Pick<
+ CategoryTypedMetadata,
+ 'HKAudioExposureLevel' | 'HKHeadphoneGain'
+ >
+ readonly HKCategoryTypeIdentifierEnvironmentalAudioExposureEvent: Pick<
+ CategoryTypedMetadata,
+ 'HKAudioExposureLevel'
+ >
+ readonly HKCategoryTypeIdentifierHeadphoneAudioExposureEvent: Pick<
+ CategoryTypedMetadata,
+ 'HKAudioExposureLevel' | 'HKHeadphoneGain'
+ >
+ readonly HKCategoryTypeIdentifierHighHeartRateEvent: Pick<
+ CategoryTypedMetadata,
+ 'HKHeartRateEventThreshold'
+ >
+ readonly HKCategoryTypeIdentifierLowCardioFitnessEvent: Pick<
+ CategoryTypedMetadata,
+ 'HKLowCardioFitnessEventThreshold' | 'HKVO2MaxValue'
+ >
+ readonly HKCategoryTypeIdentifierLowHeartRateEvent: Pick<
+ CategoryTypedMetadata,
+ 'HKHeartRateEventThreshold'
+ >
+ readonly HKCategoryTypeIdentifierMenstrualFlow: Pick<
+ CategoryTypedMetadata,
+ 'HKMenstrualCycleStart'
+ >
+ readonly HKCategoryTypeIdentifierSexualActivity: Pick<
+ CategoryTypedMetadata,
+ 'HKSexualActivityProtectionUsed'
+ >
+}
+export type CategoryTypedMetadataForIdentifierGenerated<
+ T extends CategoryTypeIdentifier = CategoryTypeIdentifier,
+> = KnownSampleMetadata &
+ (T extends keyof CategorySpecificMetadataByIdentifierMap
+ ? CategorySpecificMetadataByIdentifierMap[T]
+ : Record)
+export interface QuantitySpecificMetadataByIdentifierMap {
+ readonly HKQuantityTypeIdentifierBasalBodyTemperature: Pick<
+ QuantityTypedMetadata,
+ 'HKBodyTemperatureSensorLocation'
+ >
+ readonly HKQuantityTypeIdentifierBloodGlucose: Pick<
+ QuantityTypedMetadata,
+ 'HKBloodGlucoseMealTime'
+ >
+ readonly HKQuantityTypeIdentifierBodyTemperature: Pick<
+ QuantityTypedMetadata,
+ 'HKBodyTemperatureSensorLocation'
+ >
+ readonly HKQuantityTypeIdentifierInsulinDelivery: Pick<
+ QuantityTypedMetadata,
+ 'HKInsulinDeliveryReason'
+ >
+ readonly HKQuantityTypeIdentifierVO2Max: Pick<
+ QuantityTypedMetadata,
+ 'HKVO2MaxTestType'
+ >
+}
+export type QuantityTypedMetadataForIdentifierGenerated<
+ T extends QuantityTypeIdentifier = QuantityTypeIdentifier,
+> = KnownSampleMetadata &
+ Pick<
+ QuantityTypedMetadata,
+ | 'HKHeartRateMotionContext'
+ | 'HKHeartRateRecoveryActivityDuration'
+ | 'HKHeartRateRecoveryActivityType'
+ | 'HKHeartRateRecoveryMaxObservedRecoveryHeartRate'
+ | 'HKHeartRateRecoveryTestType'
+ | 'HKHeartRateSensorLocation'
+ | 'HKQuantityClampedToLowerBound'
+ | 'HKQuantityClampedToUpperBound'
+ | 'HKSessionEstimate'
+ > &
+ (T extends keyof QuantitySpecificMetadataByIdentifierMap
+ ? QuantitySpecificMetadataByIdentifierMap[T]
+ : Record)
+export interface QuantityUnitByIdentifierMap {
+ readonly HKQuantityTypeIdentifierActiveEnergyBurned: EnergyUnit
+ readonly HKQuantityTypeIdentifierAppleExerciseTime: TimeUnit
+ readonly HKQuantityTypeIdentifierAppleMoveTime: TimeUnit
+ readonly HKQuantityTypeIdentifierAppleSleepingBreathingDisturbances: 'count'
+ readonly HKQuantityTypeIdentifierAppleSleepingWristTemperature: TemperatureUnit
+ readonly HKQuantityTypeIdentifierAppleStandTime: TimeUnit
+ readonly HKQuantityTypeIdentifierAppleWalkingSteadiness: '%'
+ readonly HKQuantityTypeIdentifierAtrialFibrillationBurden: '%'
+ readonly HKQuantityTypeIdentifierBasalBodyTemperature: TemperatureUnit
+ readonly HKQuantityTypeIdentifierBasalEnergyBurned: EnergyUnit
+ readonly HKQuantityTypeIdentifierBloodAlcoholContent: '%'
+ readonly HKQuantityTypeIdentifierBloodGlucose: BloodGlucoseUnit
+ readonly HKQuantityTypeIdentifierBloodPressureDiastolic: PressureUnit
+ readonly HKQuantityTypeIdentifierBloodPressureSystolic: PressureUnit
+ readonly HKQuantityTypeIdentifierBodyFatPercentage: '%'
+ readonly HKQuantityTypeIdentifierBodyMass: MassUnit
+ readonly HKQuantityTypeIdentifierBodyMassIndex: 'count'
+ readonly HKQuantityTypeIdentifierBodyTemperature: TemperatureUnit
+ readonly HKQuantityTypeIdentifierCrossCountrySkiingSpeed: SpeedUnit<
+ LengthUnit,
+ TimeUnit
+ >
+ readonly HKQuantityTypeIdentifierCyclingCadence: CountPerTime
+ readonly HKQuantityTypeIdentifierCyclingFunctionalThresholdPower: string
+ readonly HKQuantityTypeIdentifierCyclingPower: string
+ readonly HKQuantityTypeIdentifierCyclingSpeed: SpeedUnit
+ readonly HKQuantityTypeIdentifierDietaryBiotin: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryCaffeine: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryCalcium: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryCarbohydrates: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryChloride: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryCholesterol: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryChromium: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryCopper: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryEnergyConsumed: EnergyUnit
+ readonly HKQuantityTypeIdentifierDietaryFatMonounsaturated: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryFatPolyunsaturated: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryFatSaturated: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryFatTotal: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryFiber: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryFolate: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryIodine: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryIron: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryMagnesium: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryManganese: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryMolybdenum: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryNiacin: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryPantothenicAcid: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryPhosphorus: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryPotassium: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryProtein: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryRiboflavin: MassUnit
+ readonly HKQuantityTypeIdentifierDietarySelenium: MassUnit
+ readonly HKQuantityTypeIdentifierDietarySodium: MassUnit
+ readonly HKQuantityTypeIdentifierDietarySugar: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryThiamin: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryVitaminA: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryVitaminB12: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryVitaminB6: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryVitaminC: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryVitaminD: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryVitaminE: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryVitaminK: MassUnit
+ readonly HKQuantityTypeIdentifierDietaryWater: string
+ readonly HKQuantityTypeIdentifierDietaryZinc: MassUnit
+ readonly HKQuantityTypeIdentifierDistanceCrossCountrySkiing: LengthUnit
+ readonly HKQuantityTypeIdentifierDistanceCycling: LengthUnit
+ readonly HKQuantityTypeIdentifierDistanceDownhillSnowSports: LengthUnit
+ readonly HKQuantityTypeIdentifierDistancePaddleSports: LengthUnit
+ readonly HKQuantityTypeIdentifierDistanceRowing: LengthUnit
+ readonly HKQuantityTypeIdentifierDistanceSkatingSports: LengthUnit
+ readonly HKQuantityTypeIdentifierDistanceSwimming: LengthUnit
+ readonly HKQuantityTypeIdentifierDistanceWalkingRunning: LengthUnit
+ readonly HKQuantityTypeIdentifierDistanceWheelchair: LengthUnit
+ readonly HKQuantityTypeIdentifierElectrodermalActivity: string
+ readonly HKQuantityTypeIdentifierEnvironmentalAudioExposure: PressureUnit
+ readonly HKQuantityTypeIdentifierEnvironmentalSoundReduction: PressureUnit
+ readonly HKQuantityTypeIdentifierEstimatedWorkoutEffortScore: 'appleEffortScore'
+ readonly HKQuantityTypeIdentifierFlightsClimbed: 'count'
+ readonly HKQuantityTypeIdentifierForcedExpiratoryVolume1: string
+ readonly HKQuantityTypeIdentifierForcedVitalCapacity: string
+ readonly HKQuantityTypeIdentifierHeadphoneAudioExposure: PressureUnit
+ readonly HKQuantityTypeIdentifierHeartRate: CountPerTime
+ readonly HKQuantityTypeIdentifierHeartRateRecoveryOneMinute: CountPerTime
+ readonly HKQuantityTypeIdentifierHeartRateVariabilitySDNN: string
+ readonly HKQuantityTypeIdentifierHeight: LengthUnit
+ readonly HKQuantityTypeIdentifierInhalerUsage: 'count'
+ readonly HKQuantityTypeIdentifierInsulinDelivery: 'IU'
+ readonly HKQuantityTypeIdentifierLeanBodyMass: MassUnit
+ readonly HKQuantityTypeIdentifierNikeFuel: 'count'
+ readonly HKQuantityTypeIdentifierNumberOfAlcoholicBeverages: 'count'
+ readonly HKQuantityTypeIdentifierNumberOfTimesFallen: 'count'
+ readonly HKQuantityTypeIdentifierOxygenSaturation: '%'
+ readonly HKQuantityTypeIdentifierPaddleSportsSpeed: SpeedUnit<
+ LengthUnit,
+ TimeUnit
+ >
+ readonly HKQuantityTypeIdentifierPeakExpiratoryFlowRate: string
+ readonly HKQuantityTypeIdentifierPeripheralPerfusionIndex: '%'
+ readonly HKQuantityTypeIdentifierPhysicalEffort: string
+ readonly HKQuantityTypeIdentifierPushCount: 'count'
+ readonly HKQuantityTypeIdentifierRespiratoryRate: CountPerTime
+ readonly HKQuantityTypeIdentifierRestingHeartRate: CountPerTime
+ readonly HKQuantityTypeIdentifierRowingSpeed: SpeedUnit
+ readonly HKQuantityTypeIdentifierRunningGroundContactTime: string
+ readonly HKQuantityTypeIdentifierRunningPower: string
+ readonly HKQuantityTypeIdentifierRunningSpeed: SpeedUnit
+ readonly HKQuantityTypeIdentifierRunningStrideLength: LengthUnit
+ readonly HKQuantityTypeIdentifierRunningVerticalOscillation: LengthUnit
+ readonly HKQuantityTypeIdentifierSixMinuteWalkTestDistance: LengthUnit
+ readonly HKQuantityTypeIdentifierStairAscentSpeed: SpeedUnit<
+ LengthUnit,
+ TimeUnit
+ >
+ readonly HKQuantityTypeIdentifierStairDescentSpeed: SpeedUnit<
+ LengthUnit,
+ TimeUnit
+ >
+ readonly HKQuantityTypeIdentifierStepCount: 'count'
+ readonly HKQuantityTypeIdentifierSwimmingStrokeCount: 'count'
+ readonly HKQuantityTypeIdentifierTimeInDaylight: TimeUnit
+ readonly HKQuantityTypeIdentifierUnderwaterDepth: LengthUnit
+ readonly HKQuantityTypeIdentifierUVExposure: string
+ readonly HKQuantityTypeIdentifierVO2Max: string
+ readonly HKQuantityTypeIdentifierWaistCircumference: LengthUnit
+ readonly HKQuantityTypeIdentifierWalkingAsymmetryPercentage: '%'
+ readonly HKQuantityTypeIdentifierWalkingDoubleSupportPercentage: '%'
+ readonly HKQuantityTypeIdentifierWalkingHeartRateAverage: CountPerTime
+ readonly HKQuantityTypeIdentifierWalkingSpeed: SpeedUnit
+ readonly HKQuantityTypeIdentifierWalkingStepLength: LengthUnit
+ readonly HKQuantityTypeIdentifierWaterTemperature: TemperatureUnit
+ readonly HKQuantityTypeIdentifierWorkoutEffortScore: 'appleEffortScore'
+}
+export type UnitForIdentifierGenerated<
+ T extends QuantityTypeIdentifier = QuantityTypeIdentifier,
+> = T extends keyof QuantityUnitByIdentifierMap
+ ? QuantityUnitByIdentifierMap[T]
+ : string
diff --git a/packages/react-native-healthkit/src/healthkit.ios.ts b/packages/react-native-healthkit/src/healthkit.ios.ts
index 41ec90e4..5e1d92d2 100644
--- a/packages/react-native-healthkit/src/healthkit.ios.ts
+++ b/packages/react-native-healthkit/src/healthkit.ios.ts
@@ -21,7 +21,31 @@ import {
StateOfMind,
Workouts,
} from './modules'
+import type {
+ CorrelationSampleTyped,
+ QueryCorrelationSamplesWithAnchorResponseTyped,
+} from './types/CorrelationType'
+import type {
+ ElectrocardiogramSamplesWithAnchorResponseTyped,
+ ElectrocardiogramSampleTyped,
+} from './types/ElectrocardiogramSample'
+import type {
+ HeartbeatSeriesSamplesWithAnchorResponseTyped,
+ HeartbeatSeriesSampleTyped,
+} from './types/HeartbeatSeries'
+import type {
+ MedicationDoseEventsWithAnchorResponseTyped,
+ MedicationDoseEventTyped,
+} from './types/Medication'
import type { QuantityTypeIdentifier } from './types/QuantityTypeIdentifier'
+import type {
+ StateOfMindSamplesWithAnchorResponseTyped,
+ StateOfMindSampleTyped,
+} from './types/StateOfMind'
+import type {
+ QueryWorkoutSamplesWithAnchorResponseTyped,
+ WorkoutProxyTyped,
+} from './types/Workouts'
import getMostRecentCategorySample from './utils/getMostRecentCategorySample'
import getMostRecentQuantitySample from './utils/getMostRecentQuantitySample'
import getMostRecentWorkout from './utils/getMostRecentWorkout'
@@ -90,6 +114,188 @@ export type AvailableQuantityTypes<
? AvailableQuantityTypesIOS17Plus
: AvailableQuantityTypesBeforeIOS17
+type BoundMethod<
+ TMethod,
+ TReturn = TMethod extends (...args: infer _Args) => infer TResult
+ ? TResult
+ : never,
+> = (
+ ...args: TMethod extends (...args: infer TArgs) => unknown ? TArgs : never
+) => TReturn
+
+function bindRetypedMethod(
+ module: TModule,
+ method: TMethod,
+): BoundMethod {
+ return (
+ method as TMethod extends (...args: infer TArgs) => infer TResult
+ ? (...args: TArgs) => TResult
+ : never
+ ).bind(module) as BoundMethod
+}
+
+const CorrelationTypeBindings = {
+ queryCorrelationSamples: bindRetypedMethod<
+ typeof CorrelationTypes,
+ typeof CorrelationTypes.queryCorrelationSamples,
+ Promise
+ >(CorrelationTypes, CorrelationTypes.queryCorrelationSamples),
+ queryCorrelationSamplesWithAnchor: bindRetypedMethod<
+ typeof CorrelationTypes,
+ typeof CorrelationTypes.queryCorrelationSamplesWithAnchor,
+ Promise
+ >(CorrelationTypes, CorrelationTypes.queryCorrelationSamplesWithAnchor),
+ saveCorrelationSample: bindRetypedMethod<
+ typeof CorrelationTypes,
+ typeof CorrelationTypes.saveCorrelationSample,
+ Promise
+ >(CorrelationTypes, CorrelationTypes.saveCorrelationSample),
+} satisfies {
+ queryCorrelationSamples: BoundMethod<
+ typeof CorrelationTypes.queryCorrelationSamples,
+ Promise
+ >
+ queryCorrelationSamplesWithAnchor: BoundMethod<
+ typeof CorrelationTypes.queryCorrelationSamplesWithAnchor,
+ Promise
+ >
+ saveCorrelationSample: BoundMethod<
+ typeof CorrelationTypes.saveCorrelationSample,
+ Promise
+ >
+}
+
+const HeartbeatSeriesBindings = {
+ queryHeartbeatSeriesSamples: bindRetypedMethod<
+ typeof HeartbeatSeries,
+ typeof HeartbeatSeries.queryHeartbeatSeriesSamples,
+ Promise
+ >(HeartbeatSeries, HeartbeatSeries.queryHeartbeatSeriesSamples),
+ queryHeartbeatSeriesSamplesWithAnchor: bindRetypedMethod<
+ typeof HeartbeatSeries,
+ typeof HeartbeatSeries.queryHeartbeatSeriesSamplesWithAnchor,
+ Promise
+ >(HeartbeatSeries, HeartbeatSeries.queryHeartbeatSeriesSamplesWithAnchor),
+} satisfies {
+ queryHeartbeatSeriesSamples: BoundMethod<
+ typeof HeartbeatSeries.queryHeartbeatSeriesSamples,
+ Promise
+ >
+ queryHeartbeatSeriesSamplesWithAnchor: BoundMethod<
+ typeof HeartbeatSeries.queryHeartbeatSeriesSamplesWithAnchor,
+ Promise
+ >
+}
+
+const ElectrocardiogramBindings = {
+ queryElectrocardiogramSamples: bindRetypedMethod<
+ typeof Electrocardiograms,
+ typeof Electrocardiograms.queryElectrocardiogramSamples,
+ Promise
+ >(Electrocardiograms, Electrocardiograms.queryElectrocardiogramSamples),
+ queryElectrocardiogramSamplesWithAnchor: bindRetypedMethod<
+ typeof Electrocardiograms,
+ typeof Electrocardiograms.queryElectrocardiogramSamplesWithAnchor,
+ Promise
+ >(
+ Electrocardiograms,
+ Electrocardiograms.queryElectrocardiogramSamplesWithAnchor,
+ ),
+} satisfies {
+ queryElectrocardiogramSamples: BoundMethod<
+ typeof Electrocardiograms.queryElectrocardiogramSamples,
+ Promise
+ >
+ queryElectrocardiogramSamplesWithAnchor: BoundMethod<
+ typeof Electrocardiograms.queryElectrocardiogramSamplesWithAnchor,
+ Promise
+ >
+}
+
+const WorkoutBindings = {
+ queryWorkoutSamples: bindRetypedMethod<
+ typeof Workouts,
+ typeof Workouts.queryWorkoutSamples,
+ Promise
+ >(Workouts, Workouts.queryWorkoutSamples),
+ queryWorkoutSamplesWithAnchor: bindRetypedMethod<
+ typeof Workouts,
+ typeof Workouts.queryWorkoutSamplesWithAnchor,
+ Promise
+ >(Workouts, Workouts.queryWorkoutSamplesWithAnchor),
+ saveWorkoutSample: bindRetypedMethod<
+ typeof Workouts,
+ typeof Workouts.saveWorkoutSample,
+ Promise
+ >(Workouts, Workouts.saveWorkoutSample),
+} satisfies {
+ queryWorkoutSamples: BoundMethod<
+ typeof Workouts.queryWorkoutSamples,
+ Promise
+ >
+ queryWorkoutSamplesWithAnchor: BoundMethod<
+ typeof Workouts.queryWorkoutSamplesWithAnchor,
+ Promise
+ >
+ saveWorkoutSample: BoundMethod<
+ typeof Workouts.saveWorkoutSample,
+ Promise
+ >
+}
+
+const StateOfMindBindings = {
+ queryStateOfMindSamples: bindRetypedMethod<
+ typeof StateOfMind,
+ typeof StateOfMind.queryStateOfMindSamples,
+ Promise
+ >(StateOfMind, StateOfMind.queryStateOfMindSamples),
+ queryStateOfMindSamplesWithAnchor: bindRetypedMethod<
+ typeof StateOfMind,
+ typeof StateOfMind.queryStateOfMindSamplesWithAnchor,
+ Promise
+ >(StateOfMind, StateOfMind.queryStateOfMindSamplesWithAnchor),
+ saveStateOfMindSample: bindRetypedMethod<
+ typeof StateOfMind,
+ typeof StateOfMind.saveStateOfMindSample,
+ Promise
+ >(StateOfMind, StateOfMind.saveStateOfMindSample),
+} satisfies {
+ queryStateOfMindSamples: BoundMethod<
+ typeof StateOfMind.queryStateOfMindSamples,
+ Promise
+ >
+ queryStateOfMindSamplesWithAnchor: BoundMethod<
+ typeof StateOfMind.queryStateOfMindSamplesWithAnchor,
+ Promise
+ >
+ saveStateOfMindSample: BoundMethod<
+ typeof StateOfMind.saveStateOfMindSample,
+ Promise
+ >
+}
+
+const MedicationBindings = {
+ queryMedicationEvents: bindRetypedMethod<
+ typeof Medication,
+ typeof Medication.queryMedicationEvents,
+ Promise
+ >(Medication, Medication.queryMedicationEvents),
+ queryMedicationEventsWithAnchor: bindRetypedMethod<
+ typeof Medication,
+ typeof Medication.queryMedicationEventsWithAnchor,
+ Promise
+ >(Medication, Medication.queryMedicationEventsWithAnchor),
+} satisfies {
+ queryMedicationEvents: BoundMethod<
+ typeof Medication.queryMedicationEvents,
+ Promise
+ >
+ queryMedicationEventsWithAnchor: BoundMethod<
+ typeof Medication.queryMedicationEventsWithAnchor,
+ Promise
+ >
+}
+
// Named exports - all functions bound to their respective modules
export const authorizationStatusFor = Core.authorizationStatusFor.bind(Core)
export const requestPerObjectReadAuthorization =
@@ -119,19 +325,17 @@ export const queryCategorySamples =
export const queryCategorySamplesWithAnchor =
CategoryTypes.queryCategorySamplesWithAnchor.bind(CategoryTypes)
export const queryCorrelationSamples =
- CorrelationTypes.queryCorrelationSamples.bind(CorrelationTypes)
+ CorrelationTypeBindings.queryCorrelationSamples
export const queryCorrelationSamplesWithAnchor =
- CorrelationTypes.queryCorrelationSamplesWithAnchor.bind(CorrelationTypes)
+ CorrelationTypeBindings.queryCorrelationSamplesWithAnchor
export const queryHeartbeatSeriesSamples =
- HeartbeatSeries.queryHeartbeatSeriesSamples.bind(HeartbeatSeries)
+ HeartbeatSeriesBindings.queryHeartbeatSeriesSamples
export const queryHeartbeatSeriesSamplesWithAnchor =
- HeartbeatSeries.queryHeartbeatSeriesSamplesWithAnchor.bind(HeartbeatSeries)
+ HeartbeatSeriesBindings.queryHeartbeatSeriesSamplesWithAnchor
export const queryElectrocardiogramSamples =
- Electrocardiograms.queryElectrocardiogramSamples.bind(Electrocardiograms)
+ ElectrocardiogramBindings.queryElectrocardiogramSamples
export const queryElectrocardiogramSamplesWithAnchor =
- Electrocardiograms.queryElectrocardiogramSamplesWithAnchor.bind(
- Electrocardiograms,
- )
+ ElectrocardiogramBindings.queryElectrocardiogramSamplesWithAnchor
export const queryQuantitySamples =
QuantityTypes.queryQuantitySamples.bind(QuantityTypes)
export const queryQuantitySamplesWithAnchor =
@@ -146,28 +350,27 @@ export const queryStatisticsCollectionForQuantitySeparateBySource =
QuantityTypes.queryStatisticsCollectionForQuantitySeparateBySource.bind(
QuantityTypes,
)
-export const queryWorkoutSamples = Workouts.queryWorkoutSamples.bind(Workouts)
+export const queryWorkoutSamples = WorkoutBindings.queryWorkoutSamples
export const queryWorkoutSamplesWithAnchor =
- Workouts.queryWorkoutSamplesWithAnchor.bind(Workouts)
+ WorkoutBindings.queryWorkoutSamplesWithAnchor
export const querySources = Core.querySources.bind(Core)
export const requestAuthorization = Core.requestAuthorization.bind(Core)
export const deleteObjects = Core.deleteObjects.bind(Core)
export const saveCategorySample =
CategoryTypes.saveCategorySample.bind(CategoryTypes)
export const saveCorrelationSample =
- CorrelationTypes.saveCorrelationSample.bind(CorrelationTypes)
+ CorrelationTypeBindings.saveCorrelationSample
export const saveQuantitySample =
QuantityTypes.saveQuantitySample.bind(QuantityTypes)
-export const saveWorkoutSample = Workouts.saveWorkoutSample.bind(Workouts)
+export const saveWorkoutSample = WorkoutBindings.saveWorkoutSample
export const startWatchApp =
Workouts.startWatchAppWithWorkoutConfiguration.bind(Workouts)
export const isProtectedDataAvailable = Core.isProtectedDataAvailable.bind(Core)
export const queryStateOfMindSamples =
- StateOfMind.queryStateOfMindSamples.bind(StateOfMind)
+ StateOfMindBindings.queryStateOfMindSamples
export const queryStateOfMindSamplesWithAnchor =
- StateOfMind.queryStateOfMindSamplesWithAnchor.bind(StateOfMind)
-export const saveStateOfMindSample =
- StateOfMind.saveStateOfMindSample.bind(StateOfMind)
+ StateOfMindBindings.queryStateOfMindSamplesWithAnchor
+export const saveStateOfMindSample = StateOfMindBindings.saveStateOfMindSample
export const isQuantityCompatibleWithUnit =
QuantityTypes.isQuantityCompatibleWithUnit.bind(QuantityTypes)
@@ -181,10 +384,9 @@ export const requestMedicationsAuthorization =
Medication.requestMedicationsAuthorization.bind(Medication)
export const queryMedications = Medication.queryMedications.bind(Medication)
-export const queryMedicationEvents =
- Medication.queryMedicationEvents.bind(Medication)
+export const queryMedicationEvents = MedicationBindings.queryMedicationEvents
export const queryMedicationEventsWithAnchor =
- Medication.queryMedicationEventsWithAnchor.bind(Medication)
+ MedicationBindings.queryMedicationEventsWithAnchor
export const currentAppSource = Core.currentAppSource.bind(Core)
diff --git a/packages/react-native-healthkit/src/healthkit.ts b/packages/react-native-healthkit/src/healthkit.ts
index df2dbc06..73eab32e 100644
--- a/packages/react-native-healthkit/src/healthkit.ts
+++ b/packages/react-native-healthkit/src/healthkit.ts
@@ -13,18 +13,41 @@ import { AuthorizationRequestStatus, AuthorizationStatus } from './types/Auth'
import type {
CategorySamplesWithAnchorResponseTyped,
CategorySampleTyped,
+ CategoryValueForIdentifier,
+ MetadataForCategoryIdentifier,
} from './types/CategoryType'
-import type { CategoryTypeIdentifier } from './types/CategoryTypeIdentifier'
+import type {
+ CategoryTypeIdentifier,
+ CategoryTypeIdentifierWriteable,
+} from './types/CategoryTypeIdentifier'
import {
BiologicalSex,
BloodType,
FitzpatrickSkinType,
WheelchairUse,
} from './types/Characteristics'
-import type { QuantitySample } from './types/QuantitySample'
+import type {
+ MetadataForQuantityIdentifier,
+ QuantitySampleTyped,
+} from './types/QuantitySample'
+import type {
+ IntervalComponents,
+ QuantitySamplesWithAnchorResponseTyped,
+ QueryStatisticsResponse,
+ QueryStatisticsResponseFromSingleSource,
+ StatisticsOptions,
+ StatisticsQueryOptions,
+ UnitForIdentifier,
+} from './types/QuantityType'
+import type {
+ QuantityTypeIdentifier,
+ QuantityTypeIdentifierWriteable,
+} from './types/QuantityTypeIdentifier'
import type {
QueryOptionsWithAnchor,
+ QueryOptionsWithAnchorAndUnit,
QueryOptionsWithSortOrder,
+ QueryOptionsWithSortOrderAndUnit,
} from './types/QueryOptions'
export * from './types'
@@ -146,47 +169,114 @@ export const getWheelchairUse = UnavailableFnFromModule(
)
// QuantityTypeModule functions
-export const queryQuantitySamples = UnavailableFnFromModule(
- 'queryQuantitySamples',
- Promise.resolve([]),
-)
-export const queryQuantitySamplesWithAnchor = UnavailableFnFromModule(
- 'queryQuantitySamplesWithAnchor',
- Promise.resolve({
+export function queryQuantitySamples(
+ _identifier: T,
+ _options: QueryOptionsWithSortOrderAndUnit>,
+): Promise[]> {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return Promise.resolve([])
+}
+
+export function queryQuantitySamplesWithAnchor<
+ T extends QuantityTypeIdentifier,
+>(
+ _identifier: T,
+ _options: QueryOptionsWithAnchorAndUnit>,
+): Promise> {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return Promise.resolve({
samples: [],
deletedSamples: [],
newAnchor: '',
- }),
-)
-export const queryStatisticsForQuantity = UnavailableFnFromModule(
- 'queryStatisticsForQuantity',
- Promise.resolve({
+ })
+}
+export function queryStatisticsForQuantity(
+ _identifier: T,
+ _statistics: readonly StatisticsOptions[],
+ _options?: StatisticsQueryOptions>,
+): Promise {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return Promise.resolve({
sources: [],
- }),
-)
-export const queryStatisticsCollectionForQuantity = UnavailableFnFromModule(
- 'queryStatisticsCollectionForQuantity',
- Promise.resolve([]),
-)
+ })
+}
+export function queryStatisticsCollectionForQuantity<
+ T extends QuantityTypeIdentifier,
+>(
+ _identifier: T,
+ _statistics: readonly StatisticsOptions[],
+ _anchorDate: Date,
+ _intervalComponents: IntervalComponents,
+ _options?: StatisticsQueryOptions>,
+): Promise {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return Promise.resolve([])
+}
-export const queryStatisticsForQuantitySeparateBySource =
- UnavailableFnFromModule(
- 'queryStatisticsForQuantitySeparateBySource',
- Promise.resolve([]),
- )
-export const queryStatisticsCollectionForQuantitySeparateBySource =
- UnavailableFnFromModule(
- 'queryStatisticsCollectionForQuantitySeparateBySource',
- Promise.resolve([]),
- )
-export const saveQuantitySample = UnavailableFnFromModule(
- 'saveQuantitySample',
- Promise.resolve(undefined),
-)
-export const isQuantityCompatibleWithUnit = UnavailableFnFromModule(
- 'isQuantityCompatibleWithUnit',
- false,
-)
+export function queryStatisticsForQuantitySeparateBySource<
+ T extends QuantityTypeIdentifier,
+>(
+ _identifier: T,
+ _statistics: readonly StatisticsOptions[],
+ _options?: StatisticsQueryOptions>,
+): Promise {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return Promise.resolve([])
+}
+export function queryStatisticsCollectionForQuantitySeparateBySource<
+ T extends QuantityTypeIdentifier,
+>(
+ _identifier: T,
+ _statistics: readonly StatisticsOptions[],
+ _anchorDate: Date,
+ _intervalComponents: IntervalComponents,
+ _options?: StatisticsQueryOptions>,
+): Promise {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return Promise.resolve([])
+}
+export function saveQuantitySample(
+ _identifier: T,
+ _unit: UnitForIdentifier,
+ _value: number,
+ _start: Date,
+ _end: Date,
+ _metadata?: MetadataForQuantityIdentifier,
+): Promise | undefined> {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return Promise.resolve(undefined)
+}
+export function isQuantityCompatibleWithUnit(
+ _identifier: T,
+ _unit: UnitForIdentifier,
+): boolean {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return false
+}
// CategoryTypeModule functions
export function queryCategorySamples(
@@ -216,10 +306,20 @@ export function queryCategorySamplesWithAnchor<
newAnchor: '',
})
}
-export const saveCategorySample = UnavailableFnFromModule(
- 'saveCategorySample',
- Promise.resolve(undefined),
-)
+
+export function saveCategorySample(
+ _identifier: T,
+ _value: CategoryValueForIdentifier,
+ _startDate: Date,
+ _endDate: Date,
+ _metadata?: MetadataForCategoryIdentifier,
+): Promise | undefined> {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return Promise.resolve(undefined)
+}
// CorrelationTypeModule functions
export const queryCorrelationSamples = UnavailableFnFromModule(
@@ -321,20 +421,30 @@ export function getMostRecentCategorySample(
return Promise.resolve(undefined)
}
-export const getMostRecentQuantitySample = UnavailableFnFromModule(
- 'getMostRecentQuantitySample',
- // biome-ignore lint/suspicious/noExplicitAny: it works
- Promise.resolve(undefined as any as QuantitySample),
-)
+export function getMostRecentQuantitySample(
+ _identifier: T,
+ _unit?: UnitForIdentifier,
+): Promise | undefined> {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return Promise.resolve(undefined)
+}
export const getMostRecentWorkout = UnavailableFnFromModule(
'getMostRecentWorkout',
// biome-ignore lint/suspicious/noExplicitAny: it works
Promise.resolve(undefined as any as WorkoutProxy),
)
-export const getPreferredUnit = UnavailableFnFromModule(
- 'getPreferredUnit',
- Promise.resolve('count'),
-) // Defaulting to 'count'
+export function getPreferredUnit(
+ _quantityType: T,
+): Promise> {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return Promise.resolve('count' as UnitForIdentifier)
+}
// Hooks (from original export list)
export function useMostRecentCategorySample(
@@ -347,10 +457,16 @@ export function useMostRecentCategorySample(
return undefined
}
-export const useMostRecentQuantitySample = UnavailableFnFromModule(
- 'useMostRecentQuantitySample',
- undefined,
-)
+export function useMostRecentQuantitySample(
+ _identifier: T,
+ _unit?: UnitForIdentifier,
+): QuantitySampleTyped | undefined {
+ if (Platform.OS !== 'ios' && !hasWarned) {
+ console.warn(notAvailableError)
+ hasWarned = true
+ }
+ return undefined
+}
export const useMostRecentWorkout = UnavailableFnFromModule(
'useMostRecentWorkout',
undefined,
diff --git a/packages/react-native-healthkit/src/hooks/useMostRecentQuantitySample.ts b/packages/react-native-healthkit/src/hooks/useMostRecentQuantitySample.ts
index 66a366b2..4717dbcb 100644
--- a/packages/react-native-healthkit/src/hooks/useMostRecentQuantitySample.ts
+++ b/packages/react-native-healthkit/src/hooks/useMostRecentQuantitySample.ts
@@ -1,5 +1,6 @@
import { useCallback, useState } from 'react'
-import type { QuantitySample } from '../types/QuantitySample'
+import type { QuantitySampleTyped } from '../types/QuantitySample'
+import type { UnitForIdentifier } from '../types/QuantityType'
import type { QuantityTypeIdentifier } from '../types/QuantityTypeIdentifier'
import getMostRecentQuantitySample from '../utils/getMostRecentQuantitySample'
import useSubscribeToChanges from './useSubscribeToChanges'
@@ -7,11 +8,11 @@ import useSubscribeToChanges from './useSubscribeToChanges'
/**
* @returns the most recent sample for the given quantity type.
*/
-export function useMostRecentQuantitySample(
- identifier: QuantityTypeIdentifier,
- unit?: string,
+export function useMostRecentQuantitySample(
+ identifier: T,
+ unit?: UnitForIdentifier,
) {
- const [lastSample, setLastSample] = useState()
+ const [lastSample, setLastSample] = useState>()
const fetchMostRecentSample = useCallback(async () => {
const value = await getMostRecentQuantitySample(identifier, unit)
diff --git a/packages/react-native-healthkit/src/hooks/useMostRecentWorkout.ts b/packages/react-native-healthkit/src/hooks/useMostRecentWorkout.ts
index 66686790..78253531 100644
--- a/packages/react-native-healthkit/src/hooks/useMostRecentWorkout.ts
+++ b/packages/react-native-healthkit/src/hooks/useMostRecentWorkout.ts
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from 'react'
-import type { WorkoutProxy } from '../specs/WorkoutProxy.nitro'
+import type { WorkoutProxyTyped } from '../types/Workouts'
import getMostRecentWorkout from '../utils/getMostRecentWorkout'
import useSubscribeToChanges from './useSubscribeToChanges'
@@ -7,7 +7,7 @@ import useSubscribeToChanges from './useSubscribeToChanges'
* @returns the most recent workout sample.
*/
export function useMostRecentWorkout() {
- const [workout, setWorkout] = useState()
+ const [workout, setWorkout] = useState()
const update = useCallback(async () => {
setWorkout(await getMostRecentWorkout())
diff --git a/packages/react-native-healthkit/src/hooks/useQuantitySampleById.ts b/packages/react-native-healthkit/src/hooks/useQuantitySampleById.ts
index da0b998b..b460edd3 100644
--- a/packages/react-native-healthkit/src/hooks/useQuantitySampleById.ts
+++ b/packages/react-native-healthkit/src/hooks/useQuantitySampleById.ts
@@ -1,5 +1,6 @@
import { useCallback, useState } from 'react'
-import type { QuantitySample } from '../types/QuantitySample'
+import type { QuantitySampleTyped } from '../types/QuantitySample'
+import type { UnitForIdentifier } from '../types/QuantityType'
import type { QuantityTypeIdentifier } from '../types/QuantityTypeIdentifier'
import getQuantitySampleById from '../utils/getQuantitySampleById'
import useSubscribeToChanges from './useSubscribeToChanges'
@@ -7,15 +8,15 @@ import useSubscribeToChanges from './useSubscribeToChanges'
/**
* @returns the most recent sample for the given quantity type.
*/
-export function useQuantitySampleById(
- identifier: QuantityTypeIdentifier,
+export function useQuantitySampleById(
+ identifier: T,
uuid: string,
options: {
/** The unit to use for the sample. */
- unit?: string
+ unit?: UnitForIdentifier
} = {},
) {
- const [sample, setSample] = useState()
+ const [sample, setSample] = useState>()
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(null)
diff --git a/packages/react-native-healthkit/src/hooks/useStatisticsForQuantity.ts b/packages/react-native-healthkit/src/hooks/useStatisticsForQuantity.ts
index 0ad26976..101580ff 100644
--- a/packages/react-native-healthkit/src/hooks/useStatisticsForQuantity.ts
+++ b/packages/react-native-healthkit/src/hooks/useStatisticsForQuantity.ts
@@ -3,6 +3,7 @@ import { QuantityTypes } from '../modules'
import type {
QueryStatisticsResponse,
StatisticsOptions,
+ UnitForIdentifier,
} from '../types/QuantityType'
import type { QuantityTypeIdentifier } from '../types/QuantityTypeIdentifier'
import useSubscribeToChanges from './useSubscribeToChanges'
@@ -14,7 +15,7 @@ export function useStatisticsForQuantity<
options: readonly StatisticsOptions[],
from: Date,
to?: Date,
- unit?: string,
+ unit?: UnitForIdentifier,
) {
const [result, setResult] = useState(null)
diff --git a/packages/react-native-healthkit/src/hooks/useSubscribeToQuantitySamples.ts b/packages/react-native-healthkit/src/hooks/useSubscribeToQuantitySamples.ts
index c35fb4fb..591287fa 100644
--- a/packages/react-native-healthkit/src/hooks/useSubscribeToQuantitySamples.ts
+++ b/packages/react-native-healthkit/src/hooks/useSubscribeToQuantitySamples.ts
@@ -9,7 +9,7 @@ export function useSubscribeToQuantitySamples<
TIdentifier extends QuantityTypeIdentifier,
>(
identifier: TIdentifier,
- onChange: (args: OnQuantitySamplesCallback) => void,
+ onChange: (args: OnQuantitySamplesCallback) => void,
): void {
const onChangeRef = useRef(onChange)
diff --git a/packages/react-native-healthkit/src/modules.ts b/packages/react-native-healthkit/src/modules.ts
index bee2a746..e695ffe0 100644
--- a/packages/react-native-healthkit/src/modules.ts
+++ b/packages/react-native-healthkit/src/modules.ts
@@ -9,7 +9,10 @@ import type { CorrelationTypeModule } from './specs/CorrelationTypeModule.nitro'
import type { ElectrocardiogramModule } from './specs/ElectrocardiogramModule.nitro'
import type { HeartbeatSeriesModule } from './specs/HeartbeatSeriesModule.nitro'
import type { MedicationModule } from './specs/MedicationModule.nitro'
-import type { QuantityTypeModule } from './specs/QuantityTypeModule.nitro'
+import type {
+ QuantityTypeModule,
+ QuantityTypeModuleTyped,
+} from './specs/QuantityTypeModule.nitro'
import type { StateOfMindModule } from './specs/StateOfMindModule.nitro'
import type { WorkoutsModule } from './specs/WorkoutsModule.nitro'
@@ -27,7 +30,9 @@ export const Characteristics =
)
export const QuantityTypes =
- NitroModules.createHybridObject('QuantityTypeModule')
+ NitroModules.createHybridObject(
+ 'QuantityTypeModule',
+ ) as QuantityTypeModuleTyped
export const CategoryTypes =
NitroModules.createHybridObject(
diff --git a/packages/react-native-healthkit/src/specs/CategoryTypeModule.nitro.ts b/packages/react-native-healthkit/src/specs/CategoryTypeModule.nitro.ts
index ac06bbad..74a9d67f 100644
--- a/packages/react-native-healthkit/src/specs/CategoryTypeModule.nitro.ts
+++ b/packages/react-native-healthkit/src/specs/CategoryTypeModule.nitro.ts
@@ -8,7 +8,10 @@ import type {
CategoryValueForIdentifier,
MetadataForCategoryIdentifier,
} from '../types/CategoryType'
-import type { CategoryTypeIdentifier } from '../types/CategoryTypeIdentifier'
+import type {
+ CategoryTypeIdentifier,
+ CategoryTypeIdentifierWriteable,
+} from '../types/CategoryTypeIdentifier'
import type { InterfaceAssertion } from '../types/InterfaceVerification'
import type {
QueryOptionsWithAnchor,
@@ -17,7 +20,7 @@ import type {
export interface CategoryTypeModule extends HybridObject<{ ios: 'swift' }> {
saveCategorySample(
- identifier: CategoryTypeIdentifier,
+ identifier: CategoryTypeIdentifierWriteable,
value: CategoryValueForIdentifier,
startDate: Date,
endDate: Date,
@@ -44,13 +47,13 @@ const _interfaceVerification: InterfaceAssertion<
> = true
export interface CategoryTypeModuleTyped {
- saveCategorySample(
+ saveCategorySample(
identifier: T,
- value: CategoryValueForIdentifier,
+ value: CategoryValueForIdentifier,
startDate: Date,
endDate: Date,
metadata?: MetadataForCategoryIdentifier,
- ): Promise
+ ): Promise | undefined>
queryCategorySamples(
identifier: T,
diff --git a/packages/react-native-healthkit/src/specs/QuantityTypeModule.nitro.ts b/packages/react-native-healthkit/src/specs/QuantityTypeModule.nitro.ts
index 8b6ed792..889aa5d3 100644
--- a/packages/react-native-healthkit/src/specs/QuantityTypeModule.nitro.ts
+++ b/packages/react-native-healthkit/src/specs/QuantityTypeModule.nitro.ts
@@ -1,20 +1,30 @@
import type { AnyMap, HybridObject } from 'react-native-nitro-modules'
-import type { QuantitySample } from '../types/QuantitySample'
+import type { InterfaceAssertion } from '../types/InterfaceVerification'
+import type {
+ MetadataForQuantityIdentifier,
+ QuantitySample,
+ QuantitySampleTyped,
+} from '../types/QuantitySample'
import type {
AggregationStyle,
IntervalComponents,
QuantitySamplesWithAnchorResponse,
+ QuantitySamplesWithAnchorResponseTyped,
QueryStatisticsResponse,
QueryStatisticsResponseFromSingleSource,
StatisticsOptions,
StatisticsQueryOptions,
+ StatisticsQueryOptionsWithStringUnit,
+ UnitForIdentifier,
} from '../types/QuantityType'
import type {
QuantityTypeIdentifier,
QuantityTypeIdentifierWriteable,
} from '../types/QuantityTypeIdentifier'
import type {
+ QueryOptionsWithAnchorAndStringUnit,
QueryOptionsWithAnchorAndUnit,
+ QueryOptionsWithSortOrderAndStringUnit,
QueryOptionsWithSortOrderAndUnit,
} from '../types/QueryOptions'
@@ -37,13 +47,13 @@ export interface QuantityTypeModule extends HybridObject<{ ios: 'swift' }> {
queryQuantitySamples(
identifier: QuantityTypeIdentifier,
- options: QueryOptionsWithSortOrderAndUnit,
+ options: QueryOptionsWithSortOrderAndStringUnit,
): Promise
queryStatisticsForQuantity(
identifier: QuantityTypeIdentifier,
statistics: readonly StatisticsOptions[],
- options?: StatisticsQueryOptions,
+ options?: StatisticsQueryOptionsWithStringUnit,
): Promise
queryStatisticsCollectionForQuantity(
@@ -51,13 +61,13 @@ export interface QuantityTypeModule extends HybridObject<{ ios: 'swift' }> {
statistics: readonly StatisticsOptions[],
anchorDate: Date,
intervalComponents: IntervalComponents,
- options?: StatisticsQueryOptions,
+ options?: StatisticsQueryOptionsWithStringUnit,
): Promise
queryStatisticsForQuantitySeparateBySource(
identifier: QuantityTypeIdentifier,
statistics: readonly StatisticsOptions[],
- options?: StatisticsQueryOptions,
+ options?: StatisticsQueryOptionsWithStringUnit,
): Promise
queryStatisticsCollectionForQuantitySeparateBySource(
@@ -65,11 +75,75 @@ export interface QuantityTypeModule extends HybridObject<{ ios: 'swift' }> {
statistics: readonly StatisticsOptions[],
anchorDate: Date,
intervalComponents: IntervalComponents,
- options?: StatisticsQueryOptions,
+ options?: StatisticsQueryOptionsWithStringUnit,
): Promise
queryQuantitySamplesWithAnchor(
identifier: QuantityTypeIdentifier,
- options: QueryOptionsWithAnchorAndUnit,
+ options: QueryOptionsWithAnchorAndStringUnit,
): Promise
}
+
+const _interfaceVerification: InterfaceAssertion<
+ QuantityTypeModule,
+ QuantityTypeModuleTyped,
+ keyof HybridObject<{ ios: 'swift' }>
+> = true
+
+export interface QuantityTypeModuleTyped {
+ isQuantityCompatibleWithUnit(
+ identifier: T,
+ unit: UnitForIdentifier,
+ ): boolean
+
+ aggregationStyle(identifier: QuantityTypeIdentifier): AggregationStyle
+
+ saveQuantitySample(
+ identifier: T,
+ unit: UnitForIdentifier,
+ value: number,
+ start: Date,
+ end: Date,
+ metadata?: MetadataForQuantityIdentifier,
+ ): Promise | undefined>
+
+ queryQuantitySamples(
+ identifier: T,
+ options: QueryOptionsWithSortOrderAndUnit>,
+ ): Promise[]>
+
+ queryStatisticsForQuantity(
+ identifier: T,
+ statistics: readonly StatisticsOptions[],
+ options?: StatisticsQueryOptions>,
+ ): Promise
+
+ queryStatisticsCollectionForQuantity(
+ identifier: T,
+ statistics: readonly StatisticsOptions[],
+ anchorDate: Date,
+ intervalComponents: IntervalComponents,
+ options?: StatisticsQueryOptions>,
+ ): Promise
+
+ queryStatisticsForQuantitySeparateBySource(
+ identifier: T,
+ statistics: readonly StatisticsOptions[],
+ options?: StatisticsQueryOptions>,
+ ): Promise
+
+ queryStatisticsCollectionForQuantitySeparateBySource<
+ T extends QuantityTypeIdentifier,
+ >(
+ identifier: T,
+ statistics: readonly StatisticsOptions[],
+ anchorDate: Date,
+ intervalComponents: IntervalComponents,
+ options?: StatisticsQueryOptions>,
+ ): Promise
+
+ queryQuantitySamplesWithAnchor(
+ identifier: T,
+ options: QueryOptionsWithAnchorAndUnit>,
+ ): Promise>
+}
diff --git a/packages/react-native-healthkit/src/type-tests/generated-typing.ts b/packages/react-native-healthkit/src/type-tests/generated-typing.ts
new file mode 100644
index 00000000..b26b4b6d
--- /dev/null
+++ b/packages/react-native-healthkit/src/type-tests/generated-typing.ts
@@ -0,0 +1,133 @@
+import type {
+ BloodGlucoseUnit,
+ CategorySampleTyped,
+ CategoryTypeIdentifier,
+ CategoryTypeIdentifierWriteable,
+ CategoryValueForIdentifier,
+ CategoryValueSleepAnalysis,
+ CorrelationSampleTyped,
+ HeartRateMotionContext,
+ Quantity,
+ QuantitySampleTyped,
+ QueryOptionsWithAnchorAndUnit,
+ QueryOptionsWithSortOrderAndUnit,
+ StateOfMindSampleTyped,
+ StatisticsQueryOptions,
+ SwimmingStrokeStyle,
+ WorkoutEventTyped,
+ WorkoutSampleTyped,
+} from '../types'
+
+type Equal =
+ (() => T extends A ? 1 : 2) extends () => T extends B ? 1 : 2
+ ? true
+ : false
+
+type Assert = T
+
+type _sleepAnalysisValuesAreTyped = Assert<
+ Equal<
+ CategoryValueForIdentifier<'HKCategoryTypeIdentifierSleepAnalysis'>,
+ CategoryValueSleepAnalysis
+ >
+>
+
+type _newCategoryIdentifierFromSdkIsPresent = Assert<
+ 'HKCategoryTypeIdentifierHypertensionEvent' extends CategoryTypeIdentifier
+ ? true
+ : false
+>
+
+type _heartRateMetadataNarrowsToEnum = Assert<
+ Equal<
+ QuantitySampleTyped<'HKQuantityTypeIdentifierHeartRate'>['metadata']['HKHeartRateMotionContext'],
+ HeartRateMotionContext | undefined
+ >
+>
+
+type _categoryMetadataIncludesKnownSampleFields = Assert<
+ Equal<
+ CategorySampleTyped<'HKCategoryTypeIdentifierSleepAnalysis'>['metadata']['HKWasUserEntered'],
+ boolean | undefined
+ >
+>
+
+type _heartRateEventMetadataIsTypedOnMetadata = Assert<
+ Equal<
+ QuantitySampleTyped<'HKQuantityTypeIdentifierHeartRate'>['metadata']['HKAppleDeviceCalibrated'],
+ boolean | undefined
+ >
+>
+
+type _quantityMetadataIncludesEstimateDate = Assert<
+ Equal<
+ QuantitySampleTyped<'HKQuantityTypeIdentifierVO2Max'>['metadata']['HKDateOfEarliestDataUsedForEstimate'],
+ string | undefined
+ >
+>
+
+type _heartRateEventThresholdMetadataIsTyped = Assert<
+ Equal<
+ CategorySampleTyped<'HKCategoryTypeIdentifierHighHeartRateEvent'>['metadata']['HKHeartRateEventThreshold'],
+ Quantity | undefined
+ >
+>
+
+type _bloodGlucoseUnitNarrows = Assert<
+ Equal<
+ QuantitySampleTyped<'HKQuantityTypeIdentifierBloodGlucose'>['unit'],
+ BloodGlucoseUnit
+ >
+>
+
+type _quantityQueryOptionsUnitNarrows = Assert<
+ Equal<
+ QueryOptionsWithSortOrderAndUnit['unit'],
+ BloodGlucoseUnit | undefined
+ >
+>
+
+type _quantityAnchorQueryOptionsUnitNarrows = Assert<
+ Equal<
+ QueryOptionsWithAnchorAndUnit['unit'],
+ BloodGlucoseUnit | undefined
+ >
+>
+
+type _quantityStatisticsOptionsUnitNarrows = Assert<
+ Equal<
+ StatisticsQueryOptions['unit'],
+ BloodGlucoseUnit | undefined
+ >
+>
+
+type _categorySaveIdentifierIsWriteableOnly = Assert<
+ 'HKCategoryTypeIdentifierHighHeartRateEvent' extends CategoryTypeIdentifierWriteable
+ ? false
+ : true
+>
+
+type _stateOfMindMetadataIsTyped = Assert<
+ Equal<
+ StateOfMindSampleTyped['metadata']['HKWasUserEntered'],
+ boolean | undefined
+ >
+>
+
+type _correlationMetadataIsTyped = Assert<
+ Equal
+>
+
+type _workoutMetadataIsTypedOnMetadata = Assert<
+ Equal<
+ WorkoutSampleTyped['metadata']['HKWorkoutBrandName'],
+ string | undefined
+ >
+>
+
+type _workoutEventMetadataIsTypedOnMetadata = Assert<
+ Equal<
+ NonNullable['HKSwimmingStrokeStyle'],
+ SwimmingStrokeStyle | undefined
+ >
+>
diff --git a/packages/react-native-healthkit/src/types/CategoryType.ts b/packages/react-native-healthkit/src/types/CategoryType.ts
index 613192b3..7fb426a0 100644
--- a/packages/react-native-healthkit/src/types/CategoryType.ts
+++ b/packages/react-native-healthkit/src/types/CategoryType.ts
@@ -1,26 +1,62 @@
import type { AnyMap } from 'react-native-nitro-modules'
+import type {
+ CategoryTypedMetadataForIdentifierGenerated,
+ CategoryValueForIdentifierGenerated,
+} from '../generated/healthkit.generated'
+import {
+ CategoryValueAppetiteChanges,
+ CategoryValueAppleStandHour,
+ CategoryValueAppleWalkingSteadinessEvent,
+ CategoryValueCervicalMucusQuality,
+ CategoryValueContraceptive,
+ CategoryValueEnvironmentalAudioExposureEvent,
+ CategoryValueHeadphoneAudioExposureEvent,
+ CategoryValueLowCardioFitnessEvent,
+ CategoryValueMenstrualFlow,
+ CategoryValue as CategoryValueNotApplicable,
+ CategoryValueOvulationTestResult,
+ CategoryValuePregnancyTestResult,
+ CategoryValuePresence,
+ CategoryValueProgesteroneTestResult,
+ CategoryValueSeverity,
+ CategoryValueSleepAnalysis,
+ CategoryValueVaginalBleeding,
+} from '../generated/healthkit.generated'
import type { CategoryTypeIdentifier } from './CategoryTypeIdentifier'
import type { BaseSample, DeletedSample, GenericMetadata } from './Shared'
import type { SourceRevision } from './Source'
+export {
+ CategoryValueAppetiteChanges,
+ CategoryValueAppleStandHour,
+ CategoryValueAppleWalkingSteadinessEvent,
+ CategoryValueCervicalMucusQuality,
+ CategoryValueContraceptive,
+ CategoryValueEnvironmentalAudioExposureEvent,
+ CategoryValueHeadphoneAudioExposureEvent,
+ CategoryValueLowCardioFitnessEvent,
+ CategoryValueMenstrualFlow,
+ CategoryValueNotApplicable,
+ CategoryValueOvulationTestResult,
+ CategoryValuePregnancyTestResult,
+ CategoryValuePresence,
+ CategoryValueProgesteroneTestResult,
+ CategoryValueSeverity,
+ CategoryValueSleepAnalysis,
+ CategoryValueVaginalBleeding,
+}
+
export type CategoryTypePresenceIdentifier =
| 'HKCategoryTypeIdentifierAppetiteChanges'
| 'HKCategoryTypeIdentifierSleepChanges'
export type CategoryTypeValueNotApplicableIdentifier =
| 'HKCategoryTypeIdentifierHighHeartRateEvent'
+ | 'HKCategoryTypeIdentifierHypertensionEvent'
| 'HKCategoryTypeIdentifierIntermenstrualBleeding'
| 'HKCategoryTypeIdentifierMindfulSession'
| 'HKCategoryTypeIdentifierSexualActivity'
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategoryvaluepregnancytestresult Apple Docs }
- */
-enum CategoryValuePregnancyTestResult {
- positive = 2,
- negative = 1,
- indeterminate = 3,
-}
+ | 'HKCategoryTypeIdentifierSleepApneaEvent'
export type CategoryTypeSeverityIdentifier =
| 'HKCategoryTypeIdentifierAbdominalCramps'
@@ -47,7 +83,6 @@ export type CategoryTypeSeverityIdentifier =
| 'HKCategoryTypeIdentifierLossOfTaste'
| 'HKCategoryTypeIdentifierLowerBackPain'
| 'HKCategoryTypeIdentifierMemoryLapse'
- | 'HKCategoryTypeIdentifierMoodChanges'
| 'HKCategoryTypeIdentifierNausea'
| 'HKCategoryTypeIdentifierNightSweats'
| 'HKCategoryTypeIdentifierPelvicPain'
@@ -61,109 +96,10 @@ export type CategoryTypeSeverityIdentifier =
| 'HKCategoryTypeIdentifierVomiting'
| 'HKCategoryTypeIdentifierWheezing'
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategoryvaluecervicalmucusquality Apple Docs }
- */
-export enum CategoryValueCervicalMucusQuality {
- dry = 1,
- sticky = 2,
- creamy = 3,
- watery = 4,
- eggWhite = 5,
-}
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategoryvaluemenstrualflow Apple Docs }
- */
-export enum CategoryValueMenstrualFlow {
- unspecified = 1,
- none = 5,
- light = 2,
- medium = 3,
- heavy = 4,
-}
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategoryvalueovulationtestresult Apple Docs }
- */
-export enum CategoryValueOvulationTestResult {
- negative = 1,
- luteinizingHormoneSurge = 2,
- indeterminate = 3,
- estrogenSurge = 4,
-}
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategoryvaluesleepanalysis Apple Docs }
- */
-export enum CategoryValueSleepAnalysis {
- inBed = 0,
- asleepUnspecified = 1,
- awake = 2,
- asleepCore = 3,
- asleepDeep = 4,
- asleepREM = 5,
-}
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategoryvalueappetitechanges Apple Docs}
- */
-export enum CategoryValueAppetiteChanges {
- decreased = 2,
- increased = 3,
- noChange = 1,
- unspecified = 0,
-}
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategoryvaluepresence Apple Docs}
- */
-export enum CategoryValuePresence {
- notPresent = 1,
- present = 0,
-}
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategoryvalueseverity Apple Docs }
- */
-export enum CategoryValueSeverity {
- notPresent = 1,
- mild = 2,
- moderate = 3,
- severe = 4,
- unspecified = 0,
-}
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategoryvalue/notapplicable Apple Docs }
- */
-export enum CategoryValueNotApplicable {
- notApplicable = 0,
-}
-
-export enum CategoryValueLowCardioFitnessEvent {
- lowFitness = 1,
-}
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategoryvalue Apple Docs }
- */
export type CategoryValue =
- | CategoryValueAppetiteChanges
- | CategoryValueCervicalMucusQuality
- | CategoryValueLowCardioFitnessEvent
- | CategoryValueMenstrualFlow
- | CategoryValueOvulationTestResult
- | CategoryValuePresence
- | CategoryValueSeverity
- | CategoryValueSleepAnalysis
+ | CategoryValueForIdentifierGenerated
| number
-export enum CategoryValueAppleStandHour {
- stood = 0,
- idle = 1,
-}
-
export interface CategorySampleForSaving {
readonly start: Date
readonly end: Date
@@ -194,49 +130,17 @@ export interface CategorySamplesWithAnchorResponseTyped<
export type MetadataForCategoryIdentifier<
T extends CategoryTypeIdentifier = CategoryTypeIdentifier,
-> = T extends 'HKCategoryTypeIdentifierSexualActivity'
- ? GenericMetadata & {
- readonly HKSexualActivityProtectionUsed?: boolean
- }
- : T extends 'HKCategoryTypeIdentifierMenstrualFlow'
- ? GenericMetadata & {
- readonly HKMenstrualCycleStart?: boolean
- }
- : GenericMetadata
+> = GenericMetadata & CategoryTypedMetadataForIdentifierGenerated
-export interface CategorySampleTyped
- extends Omit {
+export type CategorySampleTyped = Omit<
+ CategorySample,
+ 'categoryType' | 'value' | 'metadata'
+> & {
readonly categoryType: T
readonly value: CategoryValueForIdentifier
-
readonly metadata: MetadataForCategoryIdentifier
-
- readonly metadataSexualActivityProtectionUsed?: boolean
- readonly metadataMenstrualCycleStart?: boolean
}
export type CategoryValueForIdentifier<
T extends CategoryTypeIdentifier = CategoryTypeIdentifier,
-> = T extends 'HKCategoryTypeIdentifierCervicalMucusQuality'
- ? CategoryValueCervicalMucusQuality
- : T extends 'CategoryTypeIdentifierMenstrualFlow'
- ? CategoryValueMenstrualFlow
- : T extends 'HKCategoryTypeIdentifierOvulationTestResult'
- ? CategoryValueOvulationTestResult
- : T extends 'HKCategoryTypeIdentifierSleepAnalysis'
- ? CategoryValueSleepAnalysis
- : T extends CategoryTypeValueNotApplicableIdentifier
- ? CategoryValueNotApplicable
- : T extends CategoryTypeSeverityIdentifier
- ? CategoryValueSeverity
- : T extends CategoryTypePresenceIdentifier
- ? CategoryValuePresence
- : T extends 'HKCategoryTypeIdentifierLowCardioFitnessEvent'
- ? CategoryValueLowCardioFitnessEvent
- : T extends 'HKCategoryTypeIdentifierPregnancyTestResult'
- ? CategoryValuePregnancyTestResult
- : T extends 'HKCategoryTypeIdentifierPregnancyTestResult'
- ? CategoryValuePregnancyTestResult
- : T extends 'HKCategoryTypeIdentifierAppleStandHour'
- ? CategoryValueAppleStandHour
- : number
+> = CategoryValueForIdentifierGenerated
diff --git a/packages/react-native-healthkit/src/types/CategoryTypeIdentifier.ts b/packages/react-native-healthkit/src/types/CategoryTypeIdentifier.ts
index 5d9e8353..c825ecef 100644
--- a/packages/react-native-healthkit/src/types/CategoryTypeIdentifier.ts
+++ b/packages/react-native-healthkit/src/types/CategoryTypeIdentifier.ts
@@ -1,134 +1,5 @@
-export type CategoryTypeIdentifierReadOnly =
- | 'HKCategoryTypeIdentifierAppleStandHour'
- | 'HKCategoryTypeIdentifierHighHeartRateEvent'
- | 'HKCategoryTypeIdentifierLowHeartRateEvent'
- | 'HKCategoryTypeIdentifierHeadphoneAudioExposureEvent'
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifier Apple Docs }
- */
-export type CategoryTypeIdentifierWriteable =
- | 'HKCategoryTypeIdentifierSleepAnalysis'
- | 'HKCategoryTypeIdentifierCervicalMucusQuality'
- | 'HKCategoryTypeIdentifierOvulationTestResult'
- /**
- * @deprecated In iOS 18 beta
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifiermenstrualflow Apple Docs }
- */
- | 'HKCategoryTypeIdentifierMenstrualFlow'
- | 'HKCategoryTypeIdentifierIntermenstrualBleeding'
- | 'HKCategoryTypeIdentifierSexualActivity'
- | 'HKCategoryTypeIdentifierMindfulSession'
- | 'HKCategoryTypeIdentifierIrregularHeartRhythmEvent'
- /**
- * @deprecated Use environmentalAudioExposureEvent instead.
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifier/audioexposureevent Apple Docs }
- */
- | 'HKCategoryTypeIdentifierAudioExposureEvent'
- | 'HKCategoryTypeIdentifierToothbrushingEvent'
- | 'HKCategoryTypeIdentifierLowCardioFitnessEvent'
- | 'HKCategoryTypeIdentifierContraceptive'
- | 'HKCategoryTypeIdentifierLactation'
- | 'HKCategoryTypeIdentifierPregnancy'
- | 'HKCategoryTypeIdentifierPregnancyTestResult'
- | 'HKCategoryTypeIdentifierProgesteroneTestResult'
- | 'HKCategoryTypeIdentifierEnvironmentalAudioExposureEvent'
- | 'HKCategoryTypeIdentifierAppleWalkingSteadinessEvent'
- | 'HKCategoryTypeIdentifierHandwashingEvent'
-
- // Symptoms
- | 'HKCategoryTypeIdentifierAbdominalCramps'
- | 'HKCategoryTypeIdentifierAcne'
- | 'HKCategoryTypeIdentifierAppetiteChanges'
- | 'HKCategoryTypeIdentifierBladderIncontinence'
- | 'HKCategoryTypeIdentifierBloating'
- | 'HKCategoryTypeIdentifierBreastPain'
- | 'HKCategoryTypeIdentifierChestTightnessOrPain'
- | 'HKCategoryTypeIdentifierChills'
- | 'HKCategoryTypeIdentifierConstipation'
- | 'HKCategoryTypeIdentifierCoughing'
- | 'HKCategoryTypeIdentifierDiarrhea'
- | 'HKCategoryTypeIdentifierDizziness'
- | 'HKCategoryTypeIdentifierDrySkin'
- | 'HKCategoryTypeIdentifierFainting'
- | 'HKCategoryTypeIdentifierFatigue'
- | 'HKCategoryTypeIdentifierFever'
- | 'HKCategoryTypeIdentifierGeneralizedBodyAche'
- | 'HKCategoryTypeIdentifierHairLoss'
- | 'HKCategoryTypeIdentifierHeadache'
- | 'HKCategoryTypeIdentifierHeartburn'
- | 'HKCategoryTypeIdentifierHotFlashes'
- | 'HKCategoryTypeIdentifierLossOfSmell'
- | 'HKCategoryTypeIdentifierLossOfTaste'
- | 'HKCategoryTypeIdentifierLowerBackPain'
- | 'HKCategoryTypeIdentifierMemoryLapse'
- | 'HKCategoryTypeIdentifierMoodChanges'
- | 'HKCategoryTypeIdentifierNausea'
- | 'HKCategoryTypeIdentifierNightSweats'
- | 'HKCategoryTypeIdentifierPelvicPain'
- | 'HKCategoryTypeIdentifierRapidPoundingOrFlutteringHeartbeat'
- | 'HKCategoryTypeIdentifierRunnyNose'
- | 'HKCategoryTypeIdentifierShortnessOfBreath'
- | 'HKCategoryTypeIdentifierSinusCongestion'
- | 'HKCategoryTypeIdentifierSkippedHeartbeat'
- | 'HKCategoryTypeIdentifierSleepChanges'
- | 'HKCategoryTypeIdentifierSoreThroat'
- | 'HKCategoryTypeIdentifierVaginalDryness'
- | 'HKCategoryTypeIdentifierVomiting'
- | 'HKCategoryTypeIdentifierWheezing'
-
- /**
- * Bleeding After Pregnancy
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifierbleedingafterpregnancy Apple Docs }
- * @since iOS 18
- */
- | 'HKCategoryTypeIdentifierBleedingAfterPregnancy'
-
- /**
- * Bleeding During Pregnancy
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifierbleedingduringpregnancy Apple Docs }
- * @since iOS 18
- */
- | 'HKCategoryTypeIdentifierBleedingDuringPregnancy'
-
- /**
- * Infrequent Menstrual Cycles
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifier/infrequentmenstrualcycles Apple Docs }
- * @since iOS 16
- */
- | 'HKCategoryTypeIdentifierInfrequentMenstrualCycles'
-
- /**
- * Irregular Menstrual Cycles
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifier/irregularmenstrualcycles Apple Docs }
- * @since iOS 16
- */
- | 'HKCategoryTypeIdentifierIrregularMenstrualCycles'
-
- /**
- * Persistent Intermenstrual Bleeding
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifier/persistentintermenstrualbleeding Apple Docs }
- * @since iOS 16
- */
- | 'HKCategoryTypeIdentifierPersistentIntermenstrualBleeding'
-
- /**
- * Prolonged Menstrual Periods
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifier/prolongedmenstrualperiods Apple Docs }
- * @since iOS 16
- */
- | 'HKCategoryTypeIdentifierProlongedMenstrualPeriods'
-
- /**
- * Sleep Apnea Event
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifier/sleepapneaevent Apple Docs }
- * @since iOS 18
- */
- | 'HKCategoryTypeIdentifierSleepApneaEvent'
-
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifier Apple Docs }
- */
-export type CategoryTypeIdentifier =
- | CategoryTypeIdentifierReadOnly
- | CategoryTypeIdentifierWriteable
+export type {
+ CategoryTypeIdentifier,
+ CategoryTypeIdentifierReadOnly,
+ CategoryTypeIdentifierWriteable,
+} from '../generated/healthkit.generated'
diff --git a/packages/react-native-healthkit/src/types/CorrelationType.ts b/packages/react-native-healthkit/src/types/CorrelationType.ts
index 188b3693..83207135 100644
--- a/packages/react-native-healthkit/src/types/CorrelationType.ts
+++ b/packages/react-native-healthkit/src/types/CorrelationType.ts
@@ -1,6 +1,7 @@
+import type { KnownSampleMetadata } from '../generated/healthkit.generated'
import type { CategorySample, CategorySampleForSaving } from './CategoryType'
import type { QuantitySample, QuantitySampleForSaving } from './QuantitySample'
-import type { BaseSample, DeletedSample } from './Shared'
+import type { BaseSample, DeletedSample, WithTypedMetadata } from './Shared'
/**
* @see {@link https://developer.apple.com/documentation/healthkit/hkcorrelationtypeidentifier Apple Docs }
@@ -14,10 +15,13 @@ type CorrelationObject = CategorySample | QuantitySample
export interface CorrelationSample extends BaseSample {
readonly correlationType: CorrelationTypeIdentifier
readonly objects: readonly CorrelationObject[]
-
- readonly metadataFoodType?: string
}
+export type CorrelationSampleTyped = WithTypedMetadata<
+ CorrelationSample,
+ KnownSampleMetadata
+>
+
export type SampleForSaving = CategorySampleForSaving | QuantitySampleForSaving
export interface QueryCorrelationSamplesWithAnchorResponse {
@@ -25,3 +29,9 @@ export interface QueryCorrelationSamplesWithAnchorResponse {
readonly deletedSamples: readonly DeletedSample[]
readonly newAnchor: string
}
+
+export interface QueryCorrelationSamplesWithAnchorResponseTyped {
+ readonly correlations: readonly CorrelationSampleTyped[]
+ readonly deletedSamples: readonly DeletedSample[]
+ readonly newAnchor: string
+}
diff --git a/packages/react-native-healthkit/src/types/ElectrocardiogramSample.ts b/packages/react-native-healthkit/src/types/ElectrocardiogramSample.ts
index 84e9a319..894800cf 100644
--- a/packages/react-native-healthkit/src/types/ElectrocardiogramSample.ts
+++ b/packages/react-native-healthkit/src/types/ElectrocardiogramSample.ts
@@ -1,8 +1,9 @@
+import type { KnownSampleMetadata } from '../generated/healthkit.generated'
import type {
QueryOptionsWithAnchor,
QueryOptionsWithSortOrder,
} from '../types/QueryOptions'
-import type { BaseSample, DeletedSample } from './Shared'
+import type { BaseSample, DeletedSample, WithTypedMetadata } from './Shared'
// Enums mirror HealthKit; keep the union literal names stable.
export type ElectrocardiogramClassification =
@@ -33,12 +34,23 @@ export interface ElectrocardiogramSample extends BaseSample {
readonly voltages?: readonly ElectrocardiogramVoltage[]
}
+export type ElectrocardiogramSampleTyped = WithTypedMetadata<
+ ElectrocardiogramSample,
+ KnownSampleMetadata
+>
+
export interface ElectrocardiogramSamplesWithAnchorResponse {
readonly samples: readonly ElectrocardiogramSample[]
readonly deletedSamples: readonly DeletedSample[]
readonly newAnchor: string
}
+export interface ElectrocardiogramSamplesWithAnchorResponseTyped {
+ readonly samples: readonly ElectrocardiogramSampleTyped[]
+ readonly deletedSamples: readonly DeletedSample[]
+ readonly newAnchor: string
+}
+
export interface ECGQueryOptionsWithSortOrder
extends QueryOptionsWithSortOrder {
readonly includeVoltages?: boolean // default false
diff --git a/packages/react-native-healthkit/src/types/HeartbeatSeries.ts b/packages/react-native-healthkit/src/types/HeartbeatSeries.ts
index a6abca3f..01903a8e 100644
--- a/packages/react-native-healthkit/src/types/HeartbeatSeries.ts
+++ b/packages/react-native-healthkit/src/types/HeartbeatSeries.ts
@@ -1,4 +1,5 @@
-import type { BaseSample, DeletedSample } from './Shared'
+import type { KnownSampleMetadata } from '../generated/healthkit.generated'
+import type { BaseSample, DeletedSample, WithTypedMetadata } from './Shared'
export interface Heartbeat {
readonly timeSinceSeriesStart: number
@@ -9,8 +10,19 @@ export interface HeartbeatSeriesSample extends BaseSample {
readonly heartbeats: readonly Heartbeat[]
}
+export type HeartbeatSeriesSampleTyped = WithTypedMetadata<
+ HeartbeatSeriesSample,
+ KnownSampleMetadata
+>
+
export interface HeartbeatSeriesSamplesWithAnchorResponse {
readonly samples: readonly HeartbeatSeriesSample[]
readonly deletedSamples: readonly DeletedSample[]
readonly newAnchor: string
}
+
+export interface HeartbeatSeriesSamplesWithAnchorResponseTyped {
+ readonly samples: readonly HeartbeatSeriesSampleTyped[]
+ readonly deletedSamples: readonly DeletedSample[]
+ readonly newAnchor: string
+}
diff --git a/packages/react-native-healthkit/src/types/InterfaceVerification.ts b/packages/react-native-healthkit/src/types/InterfaceVerification.ts
index a71b3bb8..c925b2ec 100644
--- a/packages/react-native-healthkit/src/types/InterfaceVerification.ts
+++ b/packages/react-native-healthkit/src/types/InterfaceVerification.ts
@@ -90,6 +90,25 @@ export type CheckParameterCounts<
ExtractMethodNames]
}
+/**
+ * Checks that typed methods remain assignable to their base counterparts.
+ */
+export type CheckMethodCompatibility<
+ BaseInterface,
+ TypedInterface,
+ ExcludeFromBase extends keyof BaseInterface = never,
+> = {
+ SharedMethodNames: ExtractMethodNames &
+ ExtractMethodNames
+ IncompatibleMethods: {
+ [K in ExtractMethodNames &
+ ExtractMethodNames]: TypedInterface[K] extends BaseInterface[K]
+ ? never
+ : K
+ }[ExtractMethodNames &
+ ExtractMethodNames]
+}
+
/**
* Complete interface verification that checks both method names and parameter counts
*/
@@ -104,6 +123,11 @@ export type VerifyInterfaceSync<
TypedInterface,
ExcludeFromBase
>
+ CompatibilityCheck: CheckMethodCompatibility<
+ BaseInterface,
+ TypedInterface,
+ ExcludeFromBase
+ >
// Final verification result
Result: CheckMethodNames<
@@ -116,7 +140,20 @@ export type VerifyInterfaceSync<
TypedInterface,
ExcludeFromBase
>['ParameterCountMismatches'] extends never
- ? true
+ ? CheckMethodCompatibility<
+ BaseInterface,
+ TypedInterface,
+ ExcludeFromBase
+ >['IncompatibleMethods'] extends never
+ ? true
+ : {
+ ERROR: 'Method signature compatibility mismatch detected'
+ IncompatibleMethods: CheckMethodCompatibility<
+ BaseInterface,
+ TypedInterface,
+ ExcludeFromBase
+ >['IncompatibleMethods']
+ }
: {
ERROR: 'Parameter count mismatch detected'
MethodsWithParameterCountMismatch: CheckParameterCounts<
diff --git a/packages/react-native-healthkit/src/types/Medication.ts b/packages/react-native-healthkit/src/types/Medication.ts
new file mode 100644
index 00000000..7deedc51
--- /dev/null
+++ b/packages/react-native-healthkit/src/types/Medication.ts
@@ -0,0 +1,19 @@
+import type { KnownSampleMetadata } from '../generated/healthkit.generated'
+import type {
+ MedicationDoseEvent,
+ UserAnnotatedMedication,
+} from '../specs/MedicationModule.nitro'
+import type { DeletedSample, WithTypedMetadata } from './Shared'
+
+export type { UserAnnotatedMedication }
+
+export type MedicationDoseEventTyped = WithTypedMetadata<
+ MedicationDoseEvent,
+ KnownSampleMetadata
+>
+
+export interface MedicationDoseEventsWithAnchorResponseTyped {
+ readonly samples: readonly MedicationDoseEventTyped[]
+ readonly deletedSamples: readonly DeletedSample[]
+ readonly newAnchor: string
+}
diff --git a/packages/react-native-healthkit/src/types/MetadataEnums.ts b/packages/react-native-healthkit/src/types/MetadataEnums.ts
new file mode 100644
index 00000000..5f377670
--- /dev/null
+++ b/packages/react-native-healthkit/src/types/MetadataEnums.ts
@@ -0,0 +1,15 @@
+export {
+ AppleECGAlgorithmVersion,
+ BloodGlucoseMealTime,
+ BodyTemperatureSensorLocation,
+ CyclingFunctionalThresholdPowerTestType,
+ DevicePlacementSide,
+ HeartRateRecoveryTestType,
+ HeartRateSensorLocation,
+ PhysicalEffortEstimationType,
+ SwimmingStrokeStyle,
+ UserMotionContext,
+ VO2MaxTestType,
+ WaterSalinity,
+ WorkoutSwimmingLocationType,
+} from '../generated/healthkit.generated'
diff --git a/packages/react-native-healthkit/src/types/QuantitySample.ts b/packages/react-native-healthkit/src/types/QuantitySample.ts
index 5bcc5d07..964a4cbe 100644
--- a/packages/react-native-healthkit/src/types/QuantitySample.ts
+++ b/packages/react-native-healthkit/src/types/QuantitySample.ts
@@ -1,7 +1,8 @@
import type { AnyMap } from 'react-native-nitro-modules'
-
+import type { QuantityTypedMetadataForIdentifierGenerated } from '../generated/healthkit.generated'
+import type { UnitForIdentifier } from './QuantityType'
import type { QuantityTypeIdentifier } from './QuantityTypeIdentifier'
-import type { BaseSample } from './Shared'
+import type { BaseSample, MetadataWithUnknown } from './Shared'
import type { SourceRevision } from './Source'
/**
@@ -13,6 +14,17 @@ export interface QuantitySample extends BaseSample {
readonly unit: string
}
+export type MetadataForQuantityIdentifier<
+ T extends QuantityTypeIdentifier = QuantityTypeIdentifier,
+> = MetadataWithUnknown>
+
+export interface QuantitySampleTyped
+ extends Omit {
+ readonly quantityType: T
+ readonly unit: UnitForIdentifier
+ readonly metadata: MetadataForQuantityIdentifier
+}
+
export interface QuantitySampleForSaving {
readonly startDate: Date
readonly endDate: Date
diff --git a/packages/react-native-healthkit/src/types/QuantityType.ts b/packages/react-native-healthkit/src/types/QuantityType.ts
index b3c2ef3e..73f6a244 100644
--- a/packages/react-native-healthkit/src/types/QuantityType.ts
+++ b/packages/react-native-healthkit/src/types/QuantityType.ts
@@ -1,20 +1,15 @@
+import type { UnitForIdentifierGenerated } from '../generated/healthkit.generated'
+import {
+ HeartRateMotionContext,
+ InsulinDeliveryReason,
+} from '../generated/healthkit.generated'
import type { SourceProxy } from '../specs/SourceProxy.nitro'
-import type { QuantitySample } from './QuantitySample'
+import type { QuantitySample, QuantitySampleTyped } from './QuantitySample'
import type { QuantityTypeIdentifier } from './QuantityTypeIdentifier'
import type { FilterForSamples } from './QueryOptions'
import type { DeletedSample } from './Shared'
-import type {
- BloodGlucoseUnit,
- CountPerTime,
- EnergyUnit,
- LengthUnit,
- MassUnit,
- SpeedUnit,
- TemperatureUnit,
- TimeUnit,
- Unit,
- VolumeUnit,
-} from './Units'
+
+export { HeartRateMotionContext, InsulinDeliveryReason }
interface QuantityDateInterval {
readonly from: Date
@@ -52,25 +47,19 @@ export interface QuantitySamplesWithAnchorResponse {
readonly newAnchor: string
}
+export interface QuantitySamplesWithAnchorResponseTyped<
+ T extends QuantityTypeIdentifier,
+> {
+ readonly samples: readonly QuantitySampleTyped[]
+ readonly deletedSamples: readonly DeletedSample[]
+ readonly newAnchor: string
+}
+
export interface Quantity {
readonly unit: string
readonly quantity: number
}
-/**
- * @see {@link https://developer.apple.com/documentation/healthkit/hkinsulindeliveryreason Apple Docs }
- */
-export enum InsulinDeliveryReason {
- basal = 1,
- bolus = 2,
-}
-
-export enum HeartRateMotionContext {
- active = 2,
- notSet = 0,
- sedentary = 1,
-}
-
export interface IntervalComponents {
readonly minute?: number
readonly hour?: number
@@ -79,11 +68,14 @@ export interface IntervalComponents {
readonly year?: number
}
-export interface StatisticsQueryOptions {
+export interface StatisticsQueryOptions {
filter?: FilterForSamples
- unit?: string
+ unit?: TUnit
}
+export interface StatisticsQueryOptionsWithStringUnit
+ extends StatisticsQueryOptions {}
+
/**
* @see {@link https://developer.apple.com/documentation/healthkit/hkstatisticsoptions Apple Docs }
*/
@@ -94,99 +86,7 @@ export type StatisticsOptions =
| 'discreteMin'
| 'duration'
| 'mostRecent'
-// | 'separateBySource' (removed since it's handled by separate functions)
export type UnitForIdentifier<
T extends QuantityTypeIdentifier = QuantityTypeIdentifier,
-> = T extends 'QuantityTypeIdentifierBloodGlucose'
- ? BloodGlucoseUnit
- : T extends
- | 'QuantityTypeIdentifierAppleExerciseTime'
- | 'QuantityTypeIdentifierAppleMoveTime'
- | 'QuantityTypeIdentifierAppleStandTime'
- ? TimeUnit
- : T extends
- | 'QuantityTypeIdentifierActiveEnergyBurned'
- | 'QuantityTypeIdentifierBasalEnergyBurned'
- | 'QuantityTypeIdentifierDietaryEnergyConsumed'
- ? EnergyUnit
- : T extends
- | 'QuantityTypeIdentifierDistanceCycling'
- | 'QuantityTypeIdentifierDistanceDownhillSnowSports'
- | 'QuantityTypeIdentifierDistanceSwimming'
- | 'QuantityTypeIdentifierDistanceWalkingRunning'
- | 'QuantityTypeIdentifierDistanceWheelchair'
- | 'QuantityTypeIdentifierSixMinuteWalkTestDistance'
- | 'QuantityTypeIdentifierWaistCircumference'
- ? LengthUnit
- : T extends
- | 'QuantityTypeIdentifierBodyFatPercentage'
- | 'QuantityTypeIdentifierOxygenSaturation'
- | 'QuantityTypeIdentifierWalkingAsymmetryPercentage'
- | 'QuantityTypeIdentifierWalkingDoubleSupportPercentage'
- ? '%'
- : T extends 'QuantityTypeIdentifierBasalBodyTemperature'
- ? TemperatureUnit
- : T extends
- | 'QuantityTypeIdentifierRunningSpeed'
- | 'QuantityTypeIdentifierStairAscentSpeed'
- | 'QuantityTypeIdentifierStairDescentSpeed'
- | 'QuantityTypeIdentifierWalkingSpeed'
- ? SpeedUnit
- : T extends
- | 'QuantityTypeIdentifierFlightsClimbed'
- | 'QuantityTypeIdentifierNumberOfAlcoholicBeverages'
- | 'QuantityTypeIdentifierNumberOfTimesFallen'
- | 'QuantityTypeIdentifierPushCount'
- | 'QuantityTypeIdentifierStepCount'
- | 'QuantityTypeIdentifierSwimmingStrokeCount'
- ? 'count'
- : T extends
- | 'QuantityTypeIdentifierDietaryBiotin'
- | 'QuantityTypeIdentifierDietaryCaffeine'
- | 'QuantityTypeIdentifierDietaryCalcium'
- | 'QuantityTypeIdentifierDietaryCarbohydrates'
- | 'QuantityTypeIdentifierDietaryChloride'
- | 'QuantityTypeIdentifierDietaryCholesterol'
- | 'QuantityTypeIdentifierDietaryChromium'
- | 'QuantityTypeIdentifierDietaryCopper'
- | 'QuantityTypeIdentifierDietaryFatMonounsaturated'
- | 'QuantityTypeIdentifierDietaryFatPolyunsaturated'
- | 'QuantityTypeIdentifierDietaryFatSaturated'
- | 'QuantityTypeIdentifierDietaryFatTotal'
- | 'QuantityTypeIdentifierDietaryFiber'
- | 'QuantityTypeIdentifierDietaryFolate'
- | 'QuantityTypeIdentifierDietaryIodine'
- | 'QuantityTypeIdentifierDietaryIron'
- | 'QuantityTypeIdentifierDietaryMagnesium'
- | 'QuantityTypeIdentifierDietaryManganese'
- | 'QuantityTypeIdentifierDietaryMolybdenum'
- | 'QuantityTypeIdentifierDietaryNiacin'
- | 'QuantityTypeIdentifierDietaryPantothenicAcid'
- | 'QuantityTypeIdentifierDietaryPhosphorus'
- | 'QuantityTypeIdentifierDietaryPotassium'
- | 'QuantityTypeIdentifierDietaryProtein'
- | 'QuantityTypeIdentifierDietaryRiboflavin'
- | 'QuantityTypeIdentifierDietarySelenium'
- | 'QuantityTypeIdentifierDietarySodium'
- | 'QuantityTypeIdentifierDietarySugar'
- | 'QuantityTypeIdentifierDietaryThiamin'
- | 'QuantityTypeIdentifierDietaryVitaminA'
- | 'QuantityTypeIdentifierDietaryVitaminB6'
- | 'QuantityTypeIdentifierDietaryVitaminB12'
- | 'QuantityTypeIdentifierDietaryVitaminC'
- | 'QuantityTypeIdentifierDietaryVitaminD'
- | 'QuantityTypeIdentifierDietaryVitaminE'
- | 'QuantityTypeIdentifierDietaryVitaminK'
- | 'QuantityTypeIdentifierDietaryZinc'
- ? MassUnit
- : T extends 'QuantityTypeIdentifierDietaryWater'
- ? VolumeUnit
- : T extends 'QuantityTypeIdentifierInsulinDelivery'
- ? 'IU'
- : T extends
- | 'QuantityTypeIdentifierHeartRate'
- | 'QuantityTypeIdentifierRestingHeartRate'
- | 'QuantityTypeIdentifierWalkingHeartRateAverage'
- ? CountPerTime
- : Unit
+> = UnitForIdentifierGenerated
diff --git a/packages/react-native-healthkit/src/types/QuantityTypeIdentifier.ts b/packages/react-native-healthkit/src/types/QuantityTypeIdentifier.ts
index 9abfdddd..4ffcf916 100644
--- a/packages/react-native-healthkit/src/types/QuantityTypeIdentifier.ts
+++ b/packages/react-native-healthkit/src/types/QuantityTypeIdentifier.ts
@@ -1,772 +1,5 @@
-/**
- * Represents a quantity type identifier.
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifier Apple Docs HKQuantityTypeIdentifier}
- */
-export type QuantityTypeIdentifierReadOnly =
- /**
- * Walking Heart Rate Average
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierwalkingheartrateaverage Apple Docs HKQuantityTypeIdentifierWalkingHeartRateAverage}
- * @since iOS 11.0
- */
- | 'HKQuantityTypeIdentifierWalkingHeartRateAverage'
-
- /**
- * Atrial Fibrillation Burden
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifieratrialfibrillationburden Apple Docs HKQuantityTypeIdentifierAtrialFibrillationBurden}
- * @since iOS 16
- */
- | 'HKQuantityTypeIdentifierAtrialFibrillationBurden' // Scalar(Percent, 0.0 - 1.0), Discrete
- /**
- * Apple Exercise Time
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierappleexercisetime Apple Docs HKQuantityTypeIdentifierAppleExerciseTime}
- */
- | 'HKQuantityTypeIdentifierAppleExerciseTime'
- /**
- * Apple Stand Time
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierapplestandtime Apple Docs HKQuantityTypeIdentifierAppleStandTime}
- */
- | 'HKQuantityTypeIdentifierAppleStandTime'
-
- /**
- * Apple Walking Steadiness
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierapplewalkingsteadiness Apple Docs HKQuantityTypeIdentifierAppleWalkingSteadiness}
- * @since iOS 15
- */
- | 'HKQuantityTypeIdentifierAppleWalkingSteadiness' // Scalar(Percent, 0.0 - 1.0), Discrete
-
-/**
- * Represents a quantity type identifier.
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifier Apple Docs HKQuantityTypeIdentifier}
- */
-export type QuantityTypeIdentifierWriteable =
- /**
- * Body Mass Index
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbodymassindex Apple Docs HKQuantityTypeIdentifierBodyMassIndex}
- */
- | 'HKQuantityTypeIdentifierBodyMassIndex'
-
- /**
- * Body Fat Percentage
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbodyfatpercentage Apple Docs HKQuantityTypeIdentifierBodyFatPercentage}
- */
- | 'HKQuantityTypeIdentifierBodyFatPercentage'
-
- /**
- * Height
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierheight Apple Docs HKQuantityTypeIdentifierHeight}
- */
- | 'HKQuantityTypeIdentifierHeight'
-
- /**
- * Body Mass
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbodymass Apple Docs HKQuantityTypeIdentifierBodyMass}
- */
- | 'HKQuantityTypeIdentifierBodyMass'
-
- /**
- * Lean Body Mass
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierleanbodymass Apple Docs HKQuantityTypeIdentifierLeanBodyMass}
- */
- | 'HKQuantityTypeIdentifierLeanBodyMass'
-
- /**
- * Waist Circumference
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierwaistcircumference Apple Docs HKQuantityTypeIdentifierWaistCircumference}
- */
- | 'HKQuantityTypeIdentifierWaistCircumference'
-
- /**
- * Step Count
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierstepcount Apple Docs HKQuantityTypeIdentifierStepCount}
- */
- | 'HKQuantityTypeIdentifierStepCount'
-
- /**
- * Distance Walking Running
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdistancewalkingrunning Apple Docs HKQuantityTypeIdentifierDistanceWalkingRunning}
- */
- | 'HKQuantityTypeIdentifierDistanceWalkingRunning'
-
- /**
- * Distance Cycling
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdistancecycling Apple Docs HKQuantityTypeIdentifierDistanceCycling}
- */
- | 'HKQuantityTypeIdentifierDistanceCycling'
-
- /**
- * Distance Wheelchair
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdistancewheelchair Apple Docs HKQuantityTypeIdentifierDistanceWheelchair}
- */
- | 'HKQuantityTypeIdentifierDistanceWheelchair'
- /**
- * Basal Energy Burned
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbasalenergyburned Apple Docs HKQuantityTypeIdentifierBasalEnergyBurned}
- */
- | 'HKQuantityTypeIdentifierBasalEnergyBurned'
- /**
- * Active Energy Burned
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifieractiveenergyburned Apple Docs HKQuantityTypeIdentifierActiveEnergyBurned}
- */
- | 'HKQuantityTypeIdentifierActiveEnergyBurned'
- /**
- * Flights Climbed
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierflightsclimbed Apple Docs HKQuantityTypeIdentifierFlightsClimbed}
- */
- | 'HKQuantityTypeIdentifierFlightsClimbed'
- /**
- * Nike Fuel
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifiernikefuel Apple Docs HKQuantityTypeIdentifierNikeFuel}
- */
- | 'HKQuantityTypeIdentifierNikeFuel'
- /**
- * Push Count
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierpushcount Apple Docs HKQuantityTypeIdentifierPushCount}
- */
- | 'HKQuantityTypeIdentifierPushCount'
- /**
- * Distance Swimming
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdistanceswimming Apple Docs HKQuantityTypeIdentifierDistanceSwimming}
- */
- | 'HKQuantityTypeIdentifierDistanceSwimming'
- /**
- * Swimming Stroke Count
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierswimmingstrokecount Apple Docs HKQuantityTypeIdentifierSwimmingStrokeCount}
- */
- | 'HKQuantityTypeIdentifierSwimmingStrokeCount'
- /**
- * VO2 Max
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifiervo2max Apple Docs HKQuantityTypeIdentifierVO2Max}
- */
- | 'HKQuantityTypeIdentifierVO2Max'
- /**
- * Distance Downhill Snow Sports
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdistancedownhillsnowsports Apple Docs HKQuantityTypeIdentifierDistanceDownhillSnowSports}
- */
- | 'HKQuantityTypeIdentifierDistanceDownhillSnowSports'
-
- // Vitals
- /**
- * Heart Rate
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierheartrate Apple Docs HKQuantityTypeIdentifierHeartRate}
- */
- | 'HKQuantityTypeIdentifierHeartRate'
- /**
- * Body Temperature
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbodytemperature Apple Docs HKQuantityTypeIdentifierBodyTemperature}
- */
- | 'HKQuantityTypeIdentifierBodyTemperature'
- /**
- * Basal Body Temperature
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbasalbodytemperature Apple Docs HKQuantityTypeIdentifierBasalBodyTemperature}
- */
- | 'HKQuantityTypeIdentifierBasalBodyTemperature'
- /**
- * Blood Pressure Systolic
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbloodpressuresystolic Apple Docs HKQuantityTypeIdentifierBloodPressureSystolic}
- */
- | 'HKQuantityTypeIdentifierBloodPressureSystolic'
- /**
- * Blood Pressure Diastolic
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbloodpressurediastolic Apple Docs HKQuantityTypeIdentifierBloodPressureDiastolic}
- */
- | 'HKQuantityTypeIdentifierBloodPressureDiastolic'
- /**
- * Respiratory Rate
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierrespiratoryrate Apple Docs HKQuantityTypeIdentifierRespiratoryRate}
- */
- | 'HKQuantityTypeIdentifierRespiratoryRate'
- /**
- * Resting Heart Rate
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierrestingheartrate Apple Docs HKQuantityTypeIdentifierRestingHeartRate}
- */
- | 'HKQuantityTypeIdentifierRestingHeartRate'
- /**
- * Heart Rate Variability SDNN
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierheartratevariabilitysdnn Apple Docs HKQuantityTypeIdentifierHeartRateVariabilitySDNN}
- * @since iOS 11.0
- */
- | 'HKQuantityTypeIdentifierHeartRateVariabilitySDNN'
- /**
- * Oxygen Saturation
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifieroxygensaturation Apple Docs HKQuantityTypeIdentifierOxygenSaturation}
- * @since iOS 8.0
- */
- | 'HKQuantityTypeIdentifierOxygenSaturation'
- /**
- * Peripheral Perfusion Index
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierperipheralperfusionindex Apple Docs HKQuantityTypeIdentifierPeripheralPerfusionIndex}
- * @since iOS 8.0
- */
- | 'HKQuantityTypeIdentifierPeripheralPerfusionIndex'
- /**
- * Blood Glucose
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbloodglucose Apple Docs HKQuantityTypeIdentifierBloodGlucose}
- */
- | 'HKQuantityTypeIdentifierBloodGlucose'
-
- /**
- * Blood Ketones
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbloodketones Apple Docs HKQuantityTypeIdentifierBloodKetones}
- * @since iOS 8.0
- */
- | 'HKQuantityTypeIdentifierBloodKetones'
-
- /**
- * Number Of Times Fallen
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifiernumberoftimesfallen Apple Docs HKQuantityTypeIdentifierNumberOfTimesFallen}
- */
- | 'HKQuantityTypeIdentifierNumberOfTimesFallen'
-
- /**
- * Electrodermal Activity
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierelectrodermalactivity Apple Docs HKQuantityTypeIdentifierElectrodermalActivity}
- */
- | 'HKQuantityTypeIdentifierElectrodermalActivity'
-
- /**
- * Inhaler Usage
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierinhalerusage Apple Docs HKQuantityTypeIdentifierInhalerUsage}
- * @since iOS 8
- */
- | 'HKQuantityTypeIdentifierInhalerUsage'
-
- /**
- * Insulin Delivery
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierinsulindelivery Apple Docs HKQuantityTypeIdentifierInsulinDelivery}
- * @since iOS 11
- */
- | 'HKQuantityTypeIdentifierInsulinDelivery'
-
- /**
- * Blood Alcohol Content
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierbloodalcoholcontent Apple Docs HKQuantityTypeIdentifierBloodAlcoholContent}
- * @since iOS 8
- */
- | 'HKQuantityTypeIdentifierBloodAlcoholContent'
-
- /**
- * Forced Vital Capacity
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierforcedvitalcapacity Apple Docs HKQuantityTypeIdentifierForcedVitalCapacity}
- */
- | 'HKQuantityTypeIdentifierForcedVitalCapacity'
-
- /**
- * Forced Expiratory Volume1
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierforcedexpiratoryvolume1 Apple Docs HKQuantityTypeIdentifierForcedExpiratoryVolume1}
- * @since iOS 8
- */
- | 'HKQuantityTypeIdentifierForcedExpiratoryVolume1'
-
- /**
- * Peak Expiratory Flow Rate
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierpeakexpiratoryflowrate Apple Docs HKQuantityTypeIdentifierPeakExpiratoryFlowRate}
- * @since iOS 8
- */
- | 'HKQuantityTypeIdentifierPeakExpiratoryFlowRate'
-
- /**
- * Environmental Audio Exposure
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierenvironmentalaudioexposure Apple Docs HKQuantityTypeIdentifierEnvironmentalAudioExposure}
- * @since iOS 13
- */
- | 'HKQuantityTypeIdentifierEnvironmentalAudioExposure'
-
- /**
- * Headphone Audio Exposure
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierheadphoneaudioexposure Apple Docs HKQuantityTypeIdentifierHeadphoneAudioExposure}
- * @since iOS 13
- */
- | 'HKQuantityTypeIdentifierHeadphoneAudioExposure'
-
- // Nutrition
- /**
- * Dietary Fat Total
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryfattotal Apple Docs HKQuantityTypeIdentifierDietaryFatTotal}
- * @since iOS 8
- */
- | 'HKQuantityTypeIdentifierDietaryFatTotal'
-
- /**
- * Dietary Fat Polyunsaturated
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryfatpolyunsaturated Apple Docs HKQuantityTypeIdentifierDietaryFatPolyunsaturated}
- */
- | 'HKQuantityTypeIdentifierDietaryFatPolyunsaturated'
-
- /**
- * Dietary Fat Monounsaturated
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryfatmonounsaturated Apple Docs HKQuantityTypeIdentifierDietaryFatMonounsaturated}
- */
- | 'HKQuantityTypeIdentifierDietaryFatMonounsaturated'
- /**
- * Dietary Fat Saturated
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryfatsaturated Apple Docs HKQuantityTypeIdentifierDietaryFatSaturated}
- */
- | 'HKQuantityTypeIdentifierDietaryFatSaturated'
-
- /**
- * Dietary Cholesterol
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarycholesterol Apple Docs HKQuantityTypeIdentifierDietaryCholesterol}
- */
- | 'HKQuantityTypeIdentifierDietaryCholesterol'
-
- /**
- * Dietary Sodium
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarysodium Apple Docs HKQuantityTypeIdentifierDietarySodium}
- */
- | 'HKQuantityTypeIdentifierDietarySodium'
-
- /**
- * Dietary Carbohydrates
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarycarbohydrates Apple Docs HKQuantityTypeIdentifierDietaryCarbohydrates}
- */
- | 'HKQuantityTypeIdentifierDietaryCarbohydrates'
-
- /**
- * Dietary Fiber
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryfiber Apple Docs HKQuantityTypeIdentifierDietaryFiber}
- */
- | 'HKQuantityTypeIdentifierDietaryFiber'
- /**
- * Dietary Sugar
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarysugar Apple Docs HKQuantityTypeIdentifierDietarySugar}
- */
- | 'HKQuantityTypeIdentifierDietarySugar'
-
- /**
- * Dietary Energy Consumed
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryenergyconsumed Apple Docs HKQuantityTypeIdentifierDietaryEnergyConsumed}
- */
- | 'HKQuantityTypeIdentifierDietaryEnergyConsumed'
-
- /**
- * Dietary Protein
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryprotein Apple Docs HKQuantityTypeIdentifierDietaryProtein}
- */
- | 'HKQuantityTypeIdentifierDietaryProtein'
-
- /**
- * Dietary Vitamin A
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryvitamina Apple Docs HKQuantityTypeIdentifierDietaryVitaminA}
- */
- | 'HKQuantityTypeIdentifierDietaryVitaminA'
-
- /**
- * Dietary Vitamin B6
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryvitaminb6 Apple Docs HKQuantityTypeIdentifierDietaryVitaminB6}
- */
- | 'HKQuantityTypeIdentifierDietaryVitaminB6'
-
- /**
- * Dietary Vitamin B12
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryvitaminb12 Apple Docs HKQuantityTypeIdentifierDietaryVitaminB12}
- */
- | 'HKQuantityTypeIdentifierDietaryVitaminB12'
-
- /**
- * Dietary Vitamin C
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryvitaminc Apple Docs HKQuantityTypeIdentifierDietaryVitaminC}
- */
- | 'HKQuantityTypeIdentifierDietaryVitaminC'
-
- /**
- * Dietary Vitamin D
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryvitamind Apple Docs HKQuantityTypeIdentifierDietaryVitaminD}
- */
- | 'HKQuantityTypeIdentifierDietaryVitaminD'
-
- /**
- * Dietary Vitamin E
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryvitamine Apple Docs HKQuantityTypeIdentifierDietaryVitaminE}
- */
- | 'HKQuantityTypeIdentifierDietaryVitaminE'
-
- /**
- * Dietary Vitamin K
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryvitamink Apple Docs HKQuantityTypeIdentifierDietaryVitaminK}
- */
- | 'HKQuantityTypeIdentifierDietaryVitaminK'
- /**
- * Dietary Calcium
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarycalcium Apple Docs HKQuantityTypeIdentifierDietaryCalcium}
- */
- | 'HKQuantityTypeIdentifierDietaryCalcium'
-
- /**
- * Dietary Iron
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryiron Apple Docs HKQuantityTypeIdentifierDietaryIron}
- */
- | 'HKQuantityTypeIdentifierDietaryIron'
-
- /**
- * Dietary Thiamin
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarythiamin Apple Docs HKQuantityTypeIdentifierDietaryThiamin}
- */
- | 'HKQuantityTypeIdentifierDietaryThiamin'
-
- /**
- * Dietary Riboflavin
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryriboflavin Apple Docs HKQuantityTypeIdentifierDietaryRiboflavin}
- */
- | 'HKQuantityTypeIdentifierDietaryRiboflavin'
-
- /**
- * Dietary Niacin
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryniacin Apple Docs HKQuantityTypeIdentifierDietaryNiacin}
- */
- | 'HKQuantityTypeIdentifierDietaryNiacin'
-
- /**
- * Dietary Folate
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryfolate Apple Docs HKQuantityTypeIdentifierDietaryFolate}
- */
- | 'HKQuantityTypeIdentifierDietaryFolate'
-
- /**
- * Dietary Biotin
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarybiotin Apple Docs HKQuantityTypeIdentifierDietaryBiotin}
- */
- | 'HKQuantityTypeIdentifierDietaryBiotin'
-
- /**
- * Dietary Pantothenic Acid
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarypantothenicacid Apple Docs HKQuantityTypeIdentifierDietaryPantothenicAcid}
- */
- | 'HKQuantityTypeIdentifierDietaryPantothenicAcid'
-
- /**
- * Dietary Phosphorus
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryphosphorus Apple Docs HKQuantityTypeIdentifierDietaryPhosphorus}
- */
- | 'HKQuantityTypeIdentifierDietaryPhosphorus'
-
- /**
- * Dietary Iodine
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryiodine Apple Docs HKQuantityTypeIdentifierDietaryIodine}
- */
- | 'HKQuantityTypeIdentifierDietaryIodine'
- /**
- * Dietary Magnesium
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarymagnesium Apple Docs HKQuantityTypeIdentifierDietaryMagnesium}
- */
- | 'HKQuantityTypeIdentifierDietaryMagnesium'
-
- /**
- * Dietary Zinc
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryzinc Apple Docs HKQuantityTypeIdentifierDietaryZinc}
- */
- | 'HKQuantityTypeIdentifierDietaryZinc'
-
- /**
- * Dietary Selenium
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietaryselenium Apple Docs HKQuantityTypeIdentifierDietarySelenium}
- */
- | 'HKQuantityTypeIdentifierDietarySelenium'
-
- /**
- * Dietary Copper
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarycopper Apple Docs HKQuantityTypeIdentifierDietaryCopper}
- */
- | 'HKQuantityTypeIdentifierDietaryCopper'
-
- /**
- * Dietary Manganese
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarymanganese Apple Docs HKQuantityTypeIdentifierDietaryManganese}
- */
- | 'HKQuantityTypeIdentifierDietaryManganese'
-
- /**
- * Dietary Chromium
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarychromium Apple Docs HKQuantityTypeIdentifierDietaryChromium}
- */
- | 'HKQuantityTypeIdentifierDietaryChromium'
-
- /**
- * Dietary Molybdenum
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarymolybdenum Apple Docs HKQuantityTypeIdentifierDietaryMolybdenum}
- */
- | 'HKQuantityTypeIdentifierDietaryMolybdenum'
-
- /**
- * Dietary Chloride
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarychloride Apple Docs HKQuantityTypeIdentifierDietaryChloride}
- * @since iOS 8
- */
- | 'HKQuantityTypeIdentifierDietaryChloride'
-
- /**
- * Dietary Potassium
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarypotassium Apple Docs HKQuantityTypeIdentifierDietaryPotassium}
- * @since iOS 8
- */
- | 'HKQuantityTypeIdentifierDietaryPotassium'
-
- /**
- * Dietary Caffeine
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarycaffeine Apple Docs HKQuantityTypeIdentifierDietaryCaffeine}
- * @since iOS 8
- */
- | 'HKQuantityTypeIdentifierDietaryCaffeine'
-
- /**
- * Dietary Water
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierdietarywater Apple Docs HKQuantityTypeIdentifierDietaryWater}
- * @since iOS 9
- */
- | 'HKQuantityTypeIdentifierDietaryWater'
-
- // Mobility
- /**
- * Six Minute Walk Test Distance
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifiersixminutewalktestdistance Apple Docs HKQuantityTypeIdentifierSixMinuteWalkTestDistance}
- * @since iOS 14
- */
- | 'HKQuantityTypeIdentifierSixMinuteWalkTestDistance'
-
- /**
- * Walking Speed
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierwalkingspeed Apple Docs HKQuantityTypeIdentifierWalkingSpeed}
- * @since iOS 14
- */
- | 'HKQuantityTypeIdentifierWalkingSpeed'
-
- /**
- * Walking Step Length
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierwalkingsteplength Apple Docs HKQuantityTypeIdentifierWalkingStepLength}
- * @since iOS 14
- */
- | 'HKQuantityTypeIdentifierWalkingStepLength'
-
- /**
- * Walking Asymmetry Percentage
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierwalkingasymmetrypercentage Apple Docs HKQuantityTypeIdentifierWalkingAsymmetryPercentage}
- * @since iOS 14
- */
- | 'HKQuantityTypeIdentifierWalkingAsymmetryPercentage'
-
- /**
- * Walking Double Support Percentage
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierwalkingdoublesupportpercentage Apple Docs HKQuantityTypeIdentifierWalkingDoubleSupportPercentage}
- * @since iOS 14
- */
- | 'HKQuantityTypeIdentifierWalkingDoubleSupportPercentage'
-
- /**
- * Stair Ascent Speed
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierstairascentspeed Apple Docs HKQuantityTypeIdentifierStairAscentSpeed}
- * @since iOS 14
- */
- | 'HKQuantityTypeIdentifierStairAscentSpeed'
-
- /**
- * Stair Descent Speed
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierstairdescentspeed Apple Docs HKQuantityTypeIdentifierStairDescentSpeed}
- * @since iOS 14
- */
- | 'HKQuantityTypeIdentifierStairDescentSpeed'
-
- /**
- * UV Exposure
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifieruvexposure Apple Docs HKQuantityTypeIdentifierUVExposure}
- * @since iOS 9
- */
- | 'HKQuantityTypeIdentifierUVExposure' // Scalar (Count), Discrete
-
- /**
- * Apple Move Time
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierapplemovetime Apple Docs HKQuantityTypeIdentifierAppleMoveTime}
- * @since iOS 14.5
- */
- | 'HKQuantityTypeIdentifierAppleMoveTime' // Time, Cumulative
-
- /**
- * Number Of Alcoholic Beverages
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifiernumberofalcoholicbeverages Apple Docs HKQuantityTypeIdentifierNumberOfAlcoholicBeverages}
- * @since iOS 15
- */
- | 'HKQuantityTypeIdentifierNumberOfAlcoholicBeverages' // Scalar(Count), Cumulative
-
- /**
- * Underwater Depth
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierunderwaterdepth Apple Docs HKQuantityTypeIdentifierUnderwaterDepth}
- * @since iOS 16
- */
- | 'HKQuantityTypeIdentifierUnderwaterDepth'
-
- /**
- * Water Temperature
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierwatertemperature Apple Docs HKQuantityTypeIdentifierWaterTemperature}
- * @since iOS 16
- */
- | 'HKQuantityTypeIdentifierWaterTemperature'
-
- /**
- * Apple Sleeping Wrist Temperature
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierapplesleepingwristtemperature Apple Docs HKQuantityTypeIdentifierAppleSleepingWristTemperature}
- * @since iOS 17
- */
- | 'HKQuantityTypeIdentifierAppleSleepingWristTemperature'
-
- /**
- * Apple Sleeping Breathing Disturbances
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifier/applesleepingbreathingdisturbances Apple Docs HKQuantityTypeIdentifierAppleSleepingBreathingDisturbances}
- * @since iOS 18
- */
- | 'HKQuantityTypeIdentifierAppleSleepingBreathingDisturbances'
-
- /**
- * Time In Daylight
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifiertimeindaylight Apple Docs HKQuantityTypeIdentifierTimeInDaylight}
- * @since iOS 17
- */
- | 'HKQuantityTypeIdentifierTimeInDaylight'
-
- /**
- * Physical Effort
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierphysicaleffort Apple Docs HKQuantityTypeIdentifierPhysicalEffort}
- * @since iOS 17
- */
- | 'HKQuantityTypeIdentifierPhysicalEffort'
-
- /**
- * Cycling Speed
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifiercyclingspeed Apple Docs HKQuantityTypeIdentifierCyclingSpeed}
- * @since iOS 17
- */
- | 'HKQuantityTypeIdentifierCyclingSpeed'
-
- /**
- * Cycling Power
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifiercyclingpower Apple Docs HKQuantityTypeIdentifierCyclingPower}
- * @since iOS 17
- */
- | 'HKQuantityTypeIdentifierCyclingPower'
-
- /**
- * Cycling Functional Threshold Power
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifiercyclingfunctionalthresholdpower Apple Docs HKQuantityTypeIdentifierCyclingFunctionalThresholdPower}
- * @since iOS 17
- */
- | 'HKQuantityTypeIdentifierCyclingFunctionalThresholdPower'
-
- /**
- * Cycling Cadence
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifiercyclingcadence Apple Docs HKQuantityTypeIdentifierCyclingCadence}
- * @since iOS 17
- */
- | 'HKQuantityTypeIdentifierCyclingCadence'
-
- /**
- * Environmental Sound Reduction
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierenvironmentalsoundreduction Apple Docs HKQuantityTypeIdentifierEnvironmentalSoundReduction}
- * @since iOS 16
- */
- | 'HKQuantityTypeIdentifierEnvironmentalSoundReduction'
-
- /**
- * Heart Rate Recovery One Minute
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierheartraterecoveryoneminute Apple Docs HKQuantityTypeIdentifierHeartRateRecoveryOneMinute}
- * @since iOS 16
- */
- | 'HKQuantityTypeIdentifierHeartRateRecoveryOneMinute'
-
- /**
- * Running Ground Contact Time
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierrunninggroundcontacttime Apple Docs HKQuantityTypeIdentifierRunningGroundContactTime}
- * @since iOS 16
- */
- | 'HKQuantityTypeIdentifierRunningGroundContactTime'
-
- /**
- * Running Stride Length
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierrunningstridelength Apple Docs HKQuantityTypeIdentifierRunningStrideLength}
- * @since iOS 16
- */
- | 'HKQuantityTypeIdentifierRunningStrideLength'
-
- /**
- * Running Power
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierrunningpower Apple Docs HKQuantityTypeIdentifierRunningPower}
- * @since iOS 16
- */
- | 'HKQuantityTypeIdentifierRunningPower'
-
- /**
- * Running Vertical Oscillation
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierrunningverticaloscillation Apple Docs HKQuantityTypeIdentifierRunningVerticalOscillation}
- * @since iOS 16
- */
- | 'HKQuantityTypeIdentifierRunningVerticalOscillation'
-
- /**
- * Running Speed
- * @see {@link https://developer.apple.com/documentation/healthkit/hkquantitytypeidentifierrunningspeed Apple Docs HKQuantityTypeIdentifierRunningSpeed}
- * @since iOS 16
- */
- | 'HKQuantityTypeIdentifierRunningSpeed'
-
- /**
- * Cross Country Skiing Speed
- * @see {@link https://developer.apple.com/documentation/healthkit/HKQuantityTypeIdentifierCrossCountrySkiingSpeed Apple Docs HKQuantityTypeIdentifierCrossCountrySkiingSpeed}
- * @since iOS 18
- */
- | 'HKQuantityTypeIdentifierCrossCountrySkiingSpeed'
-
- /**
- * Cross Country Skiing Distance
- * @see {@link https://developer.apple.com/documentation/healthkit/HKQuantityTypeIdentifierDistanceCrossCountrySkiing Apple Docs HKQuantityTypeIdentifierCrossCountrySkiingDistance}
- * @since iOS 18
- */
- | 'HKQuantityTypeIdentifierDistanceCrossCountrySkiing'
-
- /**
- * Paddle Sports Distance
- * @see {@link https://developer.apple.com/documentation/healthkit/HKQuantityTypeIdentifierDistancePaddleSports Apple Docs HKQuantityTypeIdentifierDistancePaddleSports}
- * @since iOS 18
- */
- | 'HKQuantityTypeIdentifierDistancePaddleSports'
-
- /**
- * Rowing Distance
- * @see {@link https://developer.apple.com/documentation/healthkit/HKQuantityTypeIdentifierDistanceRowing Apple Docs HKQuantityTypeIdentifierDistanceRowing}
- * @since iOS 18
- */
- | 'HKQuantityTypeIdentifierDistanceRowing'
-
- /**
- * Skating Sports Distance
- * @see {@link https://developer.apple.com/documentation/healthkit/HKQuantityTypeIdentifierDistanceSkatingSports Apple Docs HKQuantityTypeIdentifierDistanceSkatingSports}
- * @since iOS 18
- */
- | 'HKQuantityTypeIdentifierDistanceSkatingSports'
-
- /**
- * Estimated Workout Effort Score
- * @see {@link https://developer.apple.com/documentation/healthkit/HKQuantityTypeIdentifierEstimatedWorkoutEffortScore Apple Docs HKQuantityTypeIdentifierEstimatedWorkoutEffortScore}
- * @since iOS 18
- */
- | 'HKQuantityTypeIdentifierEstimatedWorkoutEffortScore'
-
- /**
- * Paddle Sports Speed
- * @see {@link https://developer.apple.com/documentation/healthkit/HKQuantityTypeIdentifierPaddleSportsSpeed Apple Docs HKQuantityTypeIdentifierPaddleSportsSpeed}
- * @since iOS 18
- */
- | 'HKQuantityTypeIdentifierPaddleSportsSpeed'
-
- /**
- * Rowing Speed
- * @see {@link https://developer.apple.com/documentation/healthkit/HKQuantityTypeIdentifierRowingSpeed Apple Docs HKQuantityTypeIdentifierRowingSpeed}
- * @since iOS 18
- */
- | 'HKQuantityTypeIdentifierRowingSpeed'
-
- /**
- * Workout Effort Score
- * @see {@link https://developer.apple.com/documentation/healthkit/HKQuantityTypeIdentifierWorkoutEffortScore Apple Docs HKQuantityTypeIdentifierWorkoutEffortScore}
- * @since iOS 18
- */
- | 'HKQuantityTypeIdentifierWorkoutEffortScore'
-
-export type QuantityTypeIdentifier =
- | QuantityTypeIdentifierReadOnly
- | QuantityTypeIdentifierWriteable
+export type {
+ QuantityTypeIdentifier,
+ QuantityTypeIdentifierReadOnly,
+ QuantityTypeIdentifierWriteable,
+} from '../generated/healthkit.generated'
diff --git a/packages/react-native-healthkit/src/types/QueryOptions.ts b/packages/react-native-healthkit/src/types/QueryOptions.ts
index c054506b..c1af55d3 100644
--- a/packages/react-native-healthkit/src/types/QueryOptions.ts
+++ b/packages/react-native-healthkit/src/types/QueryOptions.ts
@@ -102,11 +102,18 @@ export interface QueryOptionsWithSortOrder extends GenericQueryOptions {
readonly ascending?: boolean
}
-export interface QueryOptionsWithSortOrderAndUnit
+export interface QueryOptionsWithSortOrderAndUnit
extends QueryOptionsWithSortOrder {
- readonly unit?: string
+ readonly unit?: TUnit
}
-export interface QueryOptionsWithAnchorAndUnit extends QueryOptionsWithAnchor {
- readonly unit?: string
+export interface QueryOptionsWithAnchorAndUnit
+ extends QueryOptionsWithAnchor {
+ readonly unit?: TUnit
}
+
+export interface QueryOptionsWithSortOrderAndStringUnit
+ extends QueryOptionsWithSortOrderAndUnit {}
+
+export interface QueryOptionsWithAnchorAndStringUnit
+ extends QueryOptionsWithAnchorAndUnit {}
diff --git a/packages/react-native-healthkit/src/types/Shared.ts b/packages/react-native-healthkit/src/types/Shared.ts
index fd132294..6cfc237d 100644
--- a/packages/react-native-healthkit/src/types/Shared.ts
+++ b/packages/react-native-healthkit/src/types/Shared.ts
@@ -1,4 +1,8 @@
import type { AnyMap } from 'react-native-nitro-modules'
+import type {
+ KnownObjectMetadata,
+ KnownSampleMetadata,
+} from '../generated/healthkit.generated'
import type {
CategoryTypeIdentifier,
CategoryTypeIdentifierWriteable,
@@ -17,17 +21,11 @@ import type {
} from './Constants'
import type { CorrelationTypeIdentifier } from './CorrelationType'
import type { Device } from './Device'
-import type {
- HeartRateMotionContext,
- InsulinDeliveryReason,
- Quantity,
-} from './QuantityType'
import type {
QuantityTypeIdentifier,
QuantityTypeIdentifierWriteable,
} from './QuantityTypeIdentifier'
import type { SourceRevision } from './Source'
-import type { WeatherCondition } from './WeatherCondition'
export interface DeletedSample {
readonly uuid: string
@@ -64,45 +62,30 @@ export type SampleTypeIdentifierWriteable =
| typeof WorkoutRouteTypeIdentifier
| typeof WorkoutTypeIdentifier
-export interface GenericMetadata {
- readonly HKExternalUUID?: string
- readonly HKTimeZone?: string
- readonly HKWasUserEntered?: boolean
- readonly HKDeviceSerialNumber?: string
- readonly HKUDIDeviceIdentifier?: string
- readonly HKUDIProductionIdentifier?: string
- readonly HKDigitalSignature?: string
- readonly HKDeviceName?: string
- readonly HKDeviceManufacturerName?: string
- readonly HKSyncIdentifier?: string
- readonly HKSyncVersion?: number
- readonly HKWasTakenInLab?: boolean
- readonly HKReferenceRangeLowerLimit?: number
- readonly HKReferenceRangeUpperLimit?: number
+export type MetadataWithUnknown = AnyMap & T
+export type GenericMetadata = AnyMap & KnownObjectMetadata
+export type WithTypedMetadata = Omit<
+ T,
+ 'metadata'
+> & {
+ readonly metadata: AnyMap & TMetadata
+}
+export type WithOptionalTypedMetadata = Omit<
+ T,
+ 'metadata'
+> & {
+ readonly metadata?: AnyMap & TMetadata
}
+export type BaseObjectTyped =
+ WithTypedMetadata
+export type BaseSampleTyped =
+ WithTypedMetadata
export interface BaseObject {
readonly uuid: string
readonly sourceRevision: SourceRevision
readonly device?: Device
readonly metadata: AnyMap
-
- // metadata
- readonly metadataExternalUUID?: string
- readonly metadataTimeZone?: string
- readonly metadataWasUserEntered?: boolean
- readonly metadataDeviceSerialNumber?: string
- readonly metadataUdiDeviceIdentifier?: string
- readonly metadataUdiProductionIdentifier?: string
- readonly metadataDigitalSignature?: string
- readonly metadataDeviceName?: string
- readonly metadataDeviceManufacturerName?: string
- readonly metadataSyncIdentifier?: string
- readonly metadataSyncVersion?: number
- readonly metadataWasTakenInLab?: boolean
- readonly metadataReferenceRangeLowerLimit?: number
- readonly metadataReferenceRangeUpperLimit?: number
- readonly metadataAlgorithmVersion?: number
}
export interface SampleType {
@@ -117,15 +100,5 @@ export interface BaseSample extends BaseObject {
readonly startDate: Date
readonly endDate: Date
readonly hasUndeterminedDuration: boolean
-
- // metadata
- readonly metadataWeatherCondition?: WeatherCondition
- readonly metadataWeatherHumidity?: Quantity
- readonly metadataWeatherTemperature?: Quantity
- readonly metadataInsulinDeliveryReason?: InsulinDeliveryReason
- /**
- * postprandial or preprandial (https://developer.apple.com/documentation/healthkit/hkbloodglucosemealtime)
- */
- // readonly metadataBloodGlucoseMealTime?: number
- readonly metadataHeartRateMotionContext?: HeartRateMotionContext
+ readonly metadata: AnyMap
}
diff --git a/packages/react-native-healthkit/src/types/StateOfMind.ts b/packages/react-native-healthkit/src/types/StateOfMind.ts
index 177e7343..4c5fcbbc 100644
--- a/packages/react-native-healthkit/src/types/StateOfMind.ts
+++ b/packages/react-native-healthkit/src/types/StateOfMind.ts
@@ -1,4 +1,5 @@
-import type { BaseSample, DeletedSample } from './Shared'
+import type { KnownSampleMetadata } from '../generated/healthkit.generated'
+import type { BaseSample, DeletedSample, WithTypedMetadata } from './Shared'
export enum StateOfMindValenceClassification {
veryUnpleasant = 1,
@@ -23,12 +24,23 @@ export interface StateOfMindSample extends BaseSample {
readonly labels: readonly StateOfMindLabel[]
}
+export type StateOfMindSampleTyped = WithTypedMetadata<
+ StateOfMindSample,
+ KnownSampleMetadata
+>
+
export interface StateOfMindSamplesWithAnchorResponse {
readonly samples: readonly StateOfMindSample[]
readonly deletedSamples: readonly DeletedSample[]
readonly newAnchor: string
}
+export interface StateOfMindSamplesWithAnchorResponseTyped {
+ readonly samples: readonly StateOfMindSampleTyped[]
+ readonly deletedSamples: readonly DeletedSample[]
+ readonly newAnchor: string
+}
+
/**
* @see {@link https://developer.apple.com/documentation/healthkit/hkstateofmind/label Apple Docs}
*/
diff --git a/packages/react-native-healthkit/src/types/Subscriptions.ts b/packages/react-native-healthkit/src/types/Subscriptions.ts
index 9421090d..7bd214f8 100644
--- a/packages/react-native-healthkit/src/types/Subscriptions.ts
+++ b/packages/react-native-healthkit/src/types/Subscriptions.ts
@@ -1,6 +1,6 @@
import type { CategorySampleTyped } from './CategoryType'
import type { CategoryTypeIdentifier } from './CategoryTypeIdentifier'
-import type { QuantitySample } from './QuantitySample'
+import type { QuantitySampleTyped } from './QuantitySample'
import type { QuantityTypeIdentifier } from './QuantityTypeIdentifier'
import type { SampleTypeIdentifier } from './Shared'
@@ -13,32 +13,38 @@ export interface OnChangeCallbackArgs {
readonly errorMessage?: string
}
-export interface OnQuantitySamplesCallbackError {
- readonly typeIdentifier: QuantityTypeIdentifier
+export interface OnQuantitySamplesCallbackError<
+ T extends QuantityTypeIdentifier = QuantityTypeIdentifier,
+> {
+ readonly typeIdentifier: T
readonly errorMessage: string
}
-export interface OnQuantitySamplesCallbackSuccess {
- readonly typeIdentifier: QuantityTypeIdentifier
- readonly samples: readonly QuantitySample[]
+export interface OnQuantitySamplesCallbackSuccess<
+ T extends QuantityTypeIdentifier = QuantityTypeIdentifier,
+> {
+ readonly typeIdentifier: T
+ readonly samples: readonly QuantitySampleTyped[]
}
-export type OnQuantitySamplesCallback =
- | OnQuantitySamplesCallbackError
- | OnQuantitySamplesCallbackSuccess
+export type OnQuantitySamplesCallback<
+ T extends QuantityTypeIdentifier = QuantityTypeIdentifier,
+> = OnQuantitySamplesCallbackError | OnQuantitySamplesCallbackSuccess
-export interface OnCategorySamplesCallbackError {
- readonly typeIdentifier: CategoryTypeIdentifier
+export interface OnCategorySamplesCallbackError<
+ T extends CategoryTypeIdentifier = CategoryTypeIdentifier,
+> {
+ readonly typeIdentifier: T
readonly errorMessage: string
}
export interface OnCategorySamplesCallbackSuccess<
T extends CategoryTypeIdentifier,
> {
- readonly typeIdentifier: CategoryTypeIdentifier
+ readonly typeIdentifier: T
readonly samples: readonly CategorySampleTyped[]
}
export type OnCategorySamplesCallback =
- | OnCategorySamplesCallbackError
+ | OnCategorySamplesCallbackError
| OnCategorySamplesCallbackSuccess
diff --git a/packages/react-native-healthkit/src/types/WeatherCondition.ts b/packages/react-native-healthkit/src/types/WeatherCondition.ts
index b13011ee..5d921b2e 100644
--- a/packages/react-native-healthkit/src/types/WeatherCondition.ts
+++ b/packages/react-native-healthkit/src/types/WeatherCondition.ts
@@ -1,31 +1 @@
-// documented at https://developer.apple.com/documentation/healthkit/hkweathercondition
-export enum WeatherCondition {
- none = 0,
- clear = 1,
- fair = 2,
- partlyCloudy = 3,
- mostlyCloudy = 4,
- cloudy = 5,
- foggy = 6,
- haze = 7,
- windy = 8,
- blustery = 9,
- smoky = 10,
- dust = 11,
- snow = 12,
- hail = 13,
- sleet = 14,
- freezingDrizzle = 15,
- freezingRain = 16,
- mixedRainAndHail = 17,
- mixedRainAndSnow = 18,
- mixedRainAndSleet = 19,
- mixedSnowAndSleet = 20,
- drizzle = 21,
- scatteredShowers = 22,
- showers = 23,
- thunderstorms = 24,
- tropicalStorm = 25,
- hurricane = 26,
- tornado = 27,
-}
+export { WeatherCondition } from '../generated/healthkit.generated'
diff --git a/packages/react-native-healthkit/src/types/Workouts.ts b/packages/react-native-healthkit/src/types/Workouts.ts
index 2144acff..b10ee076 100644
--- a/packages/react-native-healthkit/src/types/Workouts.ts
+++ b/packages/react-native-healthkit/src/types/Workouts.ts
@@ -1,96 +1,23 @@
import type { AnyMap } from 'react-native-nitro-modules'
+import type {
+ WorkoutEventTypedMetadata,
+ WorkoutTypedMetadata,
+} from '../generated/healthkit.generated'
+import {
+ WorkoutActivityType,
+ WorkoutEventType,
+} from '../generated/healthkit.generated'
import type { WorkoutProxy } from '../specs/WorkoutProxy.nitro'
import type { BaseSample, ComparisonPredicateOperator } from '../types'
import type { Quantity } from './QuantityType'
import type { FilterForSamplesBase } from './QueryOptions'
-import type { DeletedSample } from './Shared'
-
-export enum WorkoutActivityType {
- americanFootball = 1,
- archery = 2,
- australianFootball = 3,
- badminton = 4,
- baseball = 5,
- basketball = 6,
- bowling = 7,
- boxing = 8, // See also HKWorkoutActivityTypeKickboxing.,
- climbing = 9,
- cricket = 10,
- crossTraining = 11, // Any mix of cardio and/or strength training. See also HKWorkoutActivityTypeCoreTraining and HKWorkoutActivityTypeFlexibility.,
- curling = 12,
- cycling = 13,
- dance = 14,
- danceInspiredTraining = 15, // This enum remains available to access older data.,
- elliptical = 16,
- equestrianSports = 17, // Polo, Horse Racing, Horse Riding, etc.,
- fencing = 18,
- fishing = 19,
- functionalStrengthTraining = 20, // Primarily free weights and/or body weight and/or accessories,
- golf = 21,
- gymnastics = 22,
- handball = 23,
- hiking = 24,
- hockey = 25, // Ice Hockey, Field Hockey, etc.,
- hunting = 26,
- lacrosse = 27,
- martialArts = 28,
- mindAndBody = 29, // Qigong, meditation, etc.,
- mixedMetabolicCardioTraining = 30, // This enum remains available to access older data.,
- paddleSports = 31, // Canoeing, Kayaking, Outrigger, Stand Up Paddle Board, etc.,
- play = 32, // Dodge Ball, Hopscotch, Tetherball, Jungle Gym, etc.,
- preparationAndRecovery = 33, // Foam rolling, stretching, etc.,
- racquetball = 34,
- rowing = 35,
- rugby = 36,
- running = 37,
- sailing = 38,
- skatingSports = 39, // Ice Skating, Speed Skating, Inline Skating, Skateboarding, etc.,
- snowSports = 40, // Sledding, Snowmobiling, Building a Snowman, etc. See also HKWorkoutActivityTypeCrossCountrySkiing, HKWorkoutActivityTypeSnowboarding, and HKWorkoutActivityTypeDownhillSkiing.,
- soccer = 41,
- softball = 42,
- squash = 43,
- stairClimbing = 44, // See also HKWorkoutActivityTypeStairs and HKWorkoutActivityTypeStepTraining.,
- surfingSports = 45, // Traditional Surfing, Kite Surfing, Wind Surfing, etc.,
- swimming = 46,
- tableTennis = 47,
- tennis = 48,
- trackAndField = 49, // Shot Put, Javelin, Pole Vaulting, etc.,
- traditionalStrengthTraining = 50, // Primarily machines and/or free weights,
- volleyball = 51,
- walking = 52,
- waterFitness = 53,
- waterPolo = 54,
- waterSports = 55, // Water Skiing, Wake Boarding, etc.,
- wrestling = 56,
- yoga = 57,
- barre = 58, // HKWorkoutActivityTypeDanceInspiredTraining,
- coreTraining = 59,
- crossCountrySkiing = 60,
- downhillSkiing = 61,
- flexibility = 62,
- highIntensityIntervalTraining = 63,
- jumpRope = 64,
- kickboxing = 65,
- pilates = 66, // HKWorkoutActivityTypeDanceInspiredTraining,
- snowboarding = 67,
- stairs = 68,
- stepTraining = 69,
- wheelchairWalkPace = 70,
- wheelchairRunPace = 71,
- taiChi = 72,
- mixedCardio = 73, // HKWorkoutActivityTypeMixedMetabolicCardioTraining,
- handCycling = 74,
- discSports = 75,
- fitnessGaming = 76,
- cardioDance = 77,
- socialDance = 78,
- pickleball = 79,
- cooldown = 80,
- swimBikeRun = 82,
- transition = 83,
- underwaterDiving = 84,
- other = 3000,
-}
+import type {
+ DeletedSample,
+ WithOptionalTypedMetadata,
+ WithTypedMetadata,
+} from './Shared'
+
+export { WorkoutActivityType, WorkoutEventType }
export interface WorkoutEvent {
readonly type: WorkoutEventType
@@ -99,16 +26,10 @@ export interface WorkoutEvent {
readonly metadata?: AnyMap
}
-export enum WorkoutEventType {
- pause = 1,
- resume = 2,
- lap = 3,
- marker = 4,
- motionPaused = 5,
- motionResumed = 6,
- segment = 7,
- pauseOrResumeRequest = 8,
-}
+export type WorkoutEventTyped = WithOptionalTypedMetadata<
+ WorkoutEvent,
+ WorkoutEventTypedMetadata
+>
export interface WorkoutActivity {
readonly startDate: Date
@@ -129,6 +50,12 @@ export interface QueryWorkoutSamplesWithAnchorResponse {
readonly newAnchor: string
}
+export interface QueryWorkoutSamplesWithAnchorResponseTyped {
+ readonly workouts: readonly WorkoutProxyTyped[]
+ readonly deletedSamples: readonly DeletedSample[]
+ readonly newAnchor: string
+}
+
export type WorkoutDurationPredicate = {
readonly predicateOperator: ComparisonPredicateOperator
readonly durationInSeconds: number
@@ -180,7 +107,7 @@ export interface WorkoutRouteLocation {
export interface LocationForSaving {
readonly altitude: number
readonly course: number
- readonly date: Date // unix timestamp in milliseconds
+ readonly date: Date
readonly horizontalAccuracy: number
readonly latitude: number
readonly longitude: number
@@ -198,7 +125,7 @@ export interface WorkoutTotals {
readonly energyBurned?: number
}
-export interface WorkoutSample extends BaseSample {
+export interface WorkoutSample extends Omit {
readonly workoutActivityType: WorkoutActivityType
readonly duration: Quantity
readonly totalEnergyBurned?: Quantity
@@ -207,11 +134,20 @@ export interface WorkoutSample extends BaseSample {
readonly totalFlightsClimbed?: Quantity
readonly events?: readonly WorkoutEvent[]
readonly activities?: readonly WorkoutActivity[]
-
- readonly metadataAverageMETs?: Quantity
- readonly metadataElevationAscended?: Quantity
- readonly metadataElevationDescended?: Quantity
- readonly metadataIndoorWorkout?: boolean
- readonly metadataAverageSpeed?: Quantity
- readonly metadataMaximumSpeed?: Quantity
-}
+ readonly metadata: AnyMap
+}
+
+export type WorkoutSampleTyped = WithTypedMetadata<
+ Omit & {
+ readonly events?: readonly WorkoutEventTyped[]
+ },
+ WorkoutTypedMetadata
+>
+
+export type WorkoutProxyTyped = Omit<
+ WorkoutProxy,
+ keyof WorkoutSample | 'toJSON'
+> &
+ WorkoutSampleTyped & {
+ toJSON(key?: string): WorkoutSampleTyped
+ }
diff --git a/packages/react-native-healthkit/src/types/index.ts b/packages/react-native-healthkit/src/types/index.ts
index d8439de3..688c5c64 100644
--- a/packages/react-native-healthkit/src/types/index.ts
+++ b/packages/react-native-healthkit/src/types/index.ts
@@ -7,6 +7,8 @@ export * from './Constants'
export * from './CorrelationType'
export * from './Device'
export * from './HeartbeatSeries'
+export * from './Medication'
+export * from './MetadataEnums'
export * from './QuantitySample'
export * from './QuantityType'
export * from './QuantityTypeIdentifier'
diff --git a/packages/react-native-healthkit/src/utils/getMostRecentQuantitySample.ts b/packages/react-native-healthkit/src/utils/getMostRecentQuantitySample.ts
index 0abc4e2b..43bf37dc 100644
--- a/packages/react-native-healthkit/src/utils/getMostRecentQuantitySample.ts
+++ b/packages/react-native-healthkit/src/utils/getMostRecentQuantitySample.ts
@@ -1,9 +1,10 @@
import { QuantityTypes } from '../modules'
+import type { UnitForIdentifier } from '../types/QuantityType'
import type { QuantityTypeIdentifier } from '../types/QuantityTypeIdentifier'
-async function getMostRecentQuantitySample(
- identifier: QuantityTypeIdentifier,
- unit?: string,
+async function getMostRecentQuantitySample(
+ identifier: T,
+ unit?: UnitForIdentifier,
) {
const samples = await QuantityTypes.queryQuantitySamples(identifier, {
limit: 1,
diff --git a/packages/react-native-healthkit/src/utils/getMostRecentWorkout.ts b/packages/react-native-healthkit/src/utils/getMostRecentWorkout.ts
index 02a24f08..7cadae2d 100644
--- a/packages/react-native-healthkit/src/utils/getMostRecentWorkout.ts
+++ b/packages/react-native-healthkit/src/utils/getMostRecentWorkout.ts
@@ -1,12 +1,15 @@
import { Workouts } from '../modules'
+import type { WorkoutProxyTyped } from '../types/Workouts'
-const getMostRecentWorkout = async () => {
+const getMostRecentWorkout = async (): Promise<
+ WorkoutProxyTyped | undefined
+> => {
const workouts = await Workouts.queryWorkoutSamples({
limit: 1,
ascending: false,
})
- return workouts[0]
+ return workouts[0] as WorkoutProxyTyped | undefined
}
export default getMostRecentWorkout
diff --git a/packages/react-native-healthkit/src/utils/getPreferredUnit.ts b/packages/react-native-healthkit/src/utils/getPreferredUnit.ts
index f3ff76b4..11ecf603 100644
--- a/packages/react-native-healthkit/src/utils/getPreferredUnit.ts
+++ b/packages/react-native-healthkit/src/utils/getPreferredUnit.ts
@@ -1,9 +1,10 @@
import { Core } from '../modules'
+import type { UnitForIdentifier } from '../types/QuantityType'
import type { QuantityTypeIdentifier } from '../types/QuantityTypeIdentifier'
-const getPreferredUnit = async (
- quantityType: QuantityTypeIdentifier,
-): Promise => {
+const getPreferredUnit = async (
+ quantityType: T,
+): Promise> => {
const units = await Core.getPreferredUnits([quantityType])
const unit = units[0]?.unit
@@ -13,6 +14,6 @@ const getPreferredUnit = async (
)
}
- return unit
+ return unit as UnitForIdentifier
}
export default getPreferredUnit
diff --git a/packages/react-native-healthkit/src/utils/getQuantitySampleById.ts b/packages/react-native-healthkit/src/utils/getQuantitySampleById.ts
index 19c08f94..2f423134 100644
--- a/packages/react-native-healthkit/src/utils/getQuantitySampleById.ts
+++ b/packages/react-native-healthkit/src/utils/getQuantitySampleById.ts
@@ -1,10 +1,11 @@
import { QuantityTypes } from '../modules'
+import type { UnitForIdentifier } from '../types/QuantityType'
import type { QuantityTypeIdentifier } from '../types/QuantityTypeIdentifier'
-async function getQuantitySampleById(
- identifier: QuantityTypeIdentifier,
+async function getQuantitySampleById