@kingstinct/react-native-healthkit 14.1.0 → 16.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +37 -3
  2. package/ReactNativeHealthkit.podspec +11 -7
  3. package/app.plugin.js +33 -94
  4. package/app.plugin.ts +29 -104
  5. package/ios/CategoryTypeModule.swift +1 -0
  6. package/ios/CharacteristicTypeModule.swift +7 -6
  7. package/ios/Constants.swift +9 -10
  8. package/ios/CoreModule.swift +77 -42
  9. package/ios/CorrelationTypeModule.swift +3 -2
  10. package/ios/ElectrocardiogramModule.swift +4 -3
  11. package/ios/HeartbeatSeriesModule.swift +4 -3
  12. package/ios/Helpers.swift +11 -216
  13. package/ios/MedicationModule.swift +9 -8
  14. package/ios/MemoryEstimation.swift +84 -0
  15. package/ios/PredicateHelpers.swift +16 -86
  16. package/ios/QuantityTypeModule.swift +37 -28
  17. package/ios/Serializers.swift +34 -70
  18. package/ios/SourceProxy.swift +15 -5
  19. package/ios/StateOfMindModule.swift +3 -2
  20. package/ios/WorkoutProxy.swift +31 -13
  21. package/ios/WorkoutsModule.swift +4 -3
  22. package/lib/commonjs/generated/healthkit.generated.js +15 -3
  23. package/lib/commonjs/healthkit.ios.js +6 -2
  24. package/lib/commonjs/healthkit.js +24 -79
  25. package/lib/commonjs/types/Auth.js +3 -18
  26. package/lib/commonjs/types/Background.js +2 -10
  27. package/lib/commonjs/types/QueryOptions.js +2 -17
  28. package/lib/module/generated/healthkit.generated.js +14 -2
  29. package/lib/module/healthkit.ios.js +4 -0
  30. package/lib/module/healthkit.js +22 -77
  31. package/lib/module/types/Auth.js +1 -18
  32. package/lib/module/types/Background.js +1 -10
  33. package/lib/module/types/QueryOptions.js +1 -17
  34. package/lib/typescript/generated/healthkit.generated.d.ts +20 -7
  35. package/lib/typescript/healthkit.d.ts +4 -2
  36. package/lib/typescript/healthkit.ios.d.ts +13 -9
  37. package/lib/typescript/specs/CoreModule.nitro.d.ts +22 -4
  38. package/lib/typescript/specs/SourceProxy.nitro.d.ts +2 -5
  39. package/lib/typescript/types/Auth.d.ts +1 -16
  40. package/lib/typescript/types/Background.d.ts +1 -9
  41. package/lib/typescript/types/Device.d.ts +1 -13
  42. package/lib/typescript/types/QuantityType.d.ts +3 -3
  43. package/lib/typescript/types/QueryOptions.d.ts +3 -37
  44. package/lib/typescript/types/Shared.d.ts +2 -25
  45. package/lib/typescript/types/Source.d.ts +1 -10
  46. package/lib/typescript/types/Subscriptions.d.ts +2 -3
  47. package/lib/typescript/types/Units.d.ts +1 -1
  48. package/nitrogen/generated/ios/ReactNativeHealthkit-Swift-Cxx-Bridge.cpp +24 -16
  49. package/nitrogen/generated/ios/ReactNativeHealthkit-Swift-Cxx-Bridge.hpp +91 -12
  50. package/nitrogen/generated/ios/c++/HybridCategoryTypeModuleSpecSwift.hpp +8 -5
  51. package/nitrogen/generated/ios/c++/HybridCoreModuleSpecSwift.hpp +8 -0
  52. package/nitrogen/generated/ios/c++/HybridCorrelationTypeModuleSpecSwift.hpp +8 -5
  53. package/nitrogen/generated/ios/c++/HybridElectrocardiogramModuleSpecSwift.hpp +8 -5
  54. package/nitrogen/generated/ios/c++/HybridHeartbeatSeriesModuleSpecSwift.hpp +8 -5
  55. package/nitrogen/generated/ios/c++/HybridMedicationModuleSpecSwift.hpp +8 -5
  56. package/nitrogen/generated/ios/c++/HybridQuantityTypeModuleSpecSwift.hpp +8 -5
  57. package/nitrogen/generated/ios/c++/HybridStateOfMindModuleSpecSwift.hpp +8 -5
  58. package/nitrogen/generated/ios/c++/HybridWorkoutProxySpecSwift.hpp +3 -4
  59. package/nitrogen/generated/ios/c++/HybridWorkoutsModuleSpecSwift.hpp +6 -3
  60. package/nitrogen/generated/ios/swift/CategorySample.swift +8 -8
  61. package/nitrogen/generated/ios/swift/CategoryTypeIdentifier.swift +8 -0
  62. package/nitrogen/generated/ios/swift/CategoryTypeIdentifierWriteable.swift +8 -0
  63. package/nitrogen/generated/ios/swift/CorrelationSample.swift +8 -8
  64. package/nitrogen/generated/ios/swift/ElectrocardiogramSample.swift +8 -8
  65. package/nitrogen/generated/ios/swift/Func_void_std__unordered_map_std__string__std__chrono__system_clock__time_point_.swift +54 -0
  66. package/nitrogen/generated/ios/swift/HeartbeatSeriesSample.swift +8 -8
  67. package/nitrogen/generated/ios/swift/HybridCoreModuleSpec.swift +1 -0
  68. package/nitrogen/generated/ios/swift/HybridCoreModuleSpec_cxx.swift +25 -0
  69. package/nitrogen/generated/ios/swift/MedicationDoseEvent.swift +8 -8
  70. package/nitrogen/generated/ios/swift/ObjectTypeIdentifier.swift +12 -4
  71. package/nitrogen/generated/ios/swift/QuantitySample.swift +8 -8
  72. package/nitrogen/generated/ios/swift/QuantityTypeIdentifier.swift +4 -4
  73. package/nitrogen/generated/ios/swift/QuantityTypeIdentifierWriteable.swift +4 -4
  74. package/nitrogen/generated/ios/swift/QueryStatisticsResponse.swift +6 -13
  75. package/nitrogen/generated/ios/swift/QueryStatisticsResponseFromSingleSource.swift +4 -11
  76. package/nitrogen/generated/ios/swift/SampleTypeIdentifier.swift +12 -4
  77. package/nitrogen/generated/ios/swift/SampleTypeIdentifierWriteable.swift +12 -4
  78. package/nitrogen/generated/ios/swift/SourceRevision.swift +4 -11
  79. package/nitrogen/generated/ios/swift/StateOfMindSample.swift +8 -8
  80. package/nitrogen/generated/ios/swift/WorkoutActivityType.swift +8 -0
  81. package/nitrogen/generated/shared/c++/CategorySample.hpp +7 -7
  82. package/nitrogen/generated/shared/c++/CategoryTypeIdentifier.hpp +67 -59
  83. package/nitrogen/generated/shared/c++/CategoryTypeIdentifierWriteable.hpp +67 -59
  84. package/nitrogen/generated/shared/c++/CorrelationSample.hpp +7 -7
  85. package/nitrogen/generated/shared/c++/ElectrocardiogramSample.hpp +7 -7
  86. package/nitrogen/generated/shared/c++/HeartbeatSeriesSample.hpp +7 -7
  87. package/nitrogen/generated/shared/c++/HybridCoreModuleSpec.cpp +1 -0
  88. package/nitrogen/generated/shared/c++/HybridCoreModuleSpec.hpp +3 -1
  89. package/nitrogen/generated/shared/c++/MedicationDoseEvent.hpp +7 -7
  90. package/nitrogen/generated/shared/c++/ObjectTypeIdentifier.hpp +206 -198
  91. package/nitrogen/generated/shared/c++/QuantitySample.hpp +7 -7
  92. package/nitrogen/generated/shared/c++/QuantityTypeIdentifier.hpp +73 -73
  93. package/nitrogen/generated/shared/c++/QuantityTypeIdentifierWriteable.hpp +73 -73
  94. package/nitrogen/generated/shared/c++/QueryStatisticsResponse.hpp +8 -9
  95. package/nitrogen/generated/shared/c++/QueryStatisticsResponseFromSingleSource.hpp +8 -9
  96. package/nitrogen/generated/shared/c++/SampleTypeIdentifier.hpp +199 -191
  97. package/nitrogen/generated/shared/c++/SampleTypeIdentifierWriteable.hpp +193 -185
  98. package/nitrogen/generated/shared/c++/SourceRevision.hpp +8 -9
  99. package/nitrogen/generated/shared/c++/StateOfMindSample.hpp +7 -7
  100. package/nitrogen/generated/shared/c++/WorkoutActivityType.hpp +4 -0
  101. package/package.json +8 -3
  102. package/src/generated/healthkit-schema.json +53 -8
  103. package/src/generated/healthkit.generated.ts +22 -6
  104. package/src/healthkit.ios.ts +6 -0
  105. package/src/healthkit.ts +32 -84
  106. package/src/specs/CoreModule.nitro.ts +25 -4
  107. package/src/specs/SourceProxy.nitro.ts +2 -4
  108. package/src/test-setup.ts +1 -0
  109. package/src/type-tests/generated-typing.ts +30 -8
  110. package/src/types/Auth.ts +4 -17
  111. package/src/types/Background.ts +1 -9
  112. package/src/types/Device.ts +1 -13
  113. package/src/types/QuantityType.ts +3 -3
  114. package/src/types/QueryOptions.ts +7 -65
  115. package/src/types/Shared.ts +7 -28
  116. package/src/types/Source.ts +1 -11
  117. package/src/types/Subscriptions.ts +2 -3
  118. package/src/types/Units.ts +1 -1
  119. package/ios/BackgroundDeliveryManager.swift +0 -277
  120. package/ios/Bridge.h +0 -10
  121. package/ios/ExceptionCatcher.h +0 -5
  122. package/ios/ExceptionCatcher.mm +0 -32
  123. package/ios/module.modulemap +0 -5
package/README.md CHANGED
@@ -23,6 +23,8 @@ This library is provided as-is without any warranty and is not affiliated with A
23
23
 
24
24
  ## Installation
25
25
 
26
+ [`@react-native-healthkit/core`](packages/core) holds the code shared with [`@react-native-healthkit/health-records`](packages/health-records): types, config plugin building blocks and the `ReactNativeHealthkitCore` pod. It is a regular dependency pinned to an exact version, so your package manager installs the matching copy for you and there is no version to keep in sync. In a bare React Native app you additionally need it in your own `package.json`, because the React Native CLI only autolinks packages listed there.
27
+
26
28
  ### Expo
27
29
  Usage with Expo is possible - just keep in mind it will not work in Expo Go and [you'll need to roll your own Dev Client](https://docs.expo.dev/development/getting-started/).
28
30
 
@@ -52,7 +54,9 @@ this will give you defaults that make the app build without any further configur
52
54
  3. Build a new Dev Client
53
55
 
54
56
  ### Native or Expo Bare Workflow
55
- 1. `yarn add @kingstinct/react-native-healthkit react-native-nitro-modules`
57
+ 1. `yarn add @kingstinct/react-native-healthkit @react-native-healthkit/core react-native-nitro-modules`
58
+
59
+ `@react-native-healthkit/core` is installed automatically as a dependency, but the React Native CLI only autolinks packages listed in your own `package.json`, so name it explicitly here.
56
60
  2. `npx pod-install`
57
61
  3. Set `NSHealthUpdateUsageDescription` and `NSHealthShareUsageDescription` in your `Info.plist`
58
62
  4. Enable the HealthKit capability for the project in Xcode.
@@ -121,13 +125,13 @@ Some imperative examples:
121
125
  'HKQuantityTypeIdentifierInsulinDelivery',
122
126
  'IU',
123
127
  5.5,
128
+ new Date(),
129
+ new Date(),
124
130
  {
125
- metadata: {
126
131
  // Metadata keys could be arbirtary string to store app-specific data.
127
132
  // To use built-in types from https://developer.apple.com/documentation/healthkit/samples/metadata_keys
128
133
  // you need to specify string values instead of variable names (by dropping MetadataKey from the name).
129
134
  HKInsulinDeliveryReason: HKInsulinDeliveryReason.basal,
130
- },
131
135
  }
132
136
  );
133
137
  ```
@@ -154,6 +158,36 @@ Example:
154
158
  // etc..
155
159
  ```
156
160
 
161
+ ### Memory considerations for workouts and other native objects
162
+
163
+ Workouts (and a few other results, like sources) are returned as *proxies*: native objects that keep the underlying `HKWorkout` alive so you can call follow-up functions such as `getWorkoutRoutes()`, `getStatistic()` or `getAllStatistics()` on them. The JavaScript garbage collector frees a proxy, and the HealthKit objects behind it, once it is no longer referenced. The library reports an estimate of each proxy's native size to the JS engine so it can schedule collections sensibly. Note that react-native-nitro-modules currently keeps a small fixed amount of bookkeeping (on the order of a hundred bytes) per native object it has handed to JS for the lifetime of the JS runtime, so memory will not return exactly to baseline even after collection. If you process large numbers of workouts in a loop you can release the HealthKit side eagerly by calling `dispose()` once you are done with a workout:
164
+
165
+ ```TypeScript
166
+ const workouts = await queryWorkoutSamples({ limit: 0 })
167
+
168
+ for (const workout of workouts) {
169
+ const routes = await workout.getWorkoutRoutes()
170
+ // ... do something with the routes
171
+ workout.dispose()
172
+ }
173
+ ```
174
+
175
+ After `dispose()` the proxy can no longer be used - calling any function or reading any property on it will throw. If you need to keep the data around, call `toJSON()` first to get a plain object and store that instead of the proxy:
176
+
177
+ ```TypeScript
178
+ const plainWorkouts = workouts.map((workout) => {
179
+ const json = workout.toJSON()
180
+ workout.dispose()
181
+ return json
182
+ })
183
+ ```
184
+
185
+ Other tips for keeping memory in check when working with large datasets:
186
+ - Query in chunks (for example per month, or with `limit` and anchors) instead of fetching everything at once.
187
+ - Prefer statistics queries (`queryStatisticsForQuantity`, `queryStatisticsCollectionForQuantity`) over fetching every sample when you only need aggregates.
188
+ - Keep plain data (from `toJSON()` or the sample queries) in React state rather than proxies.
189
+ - `sourceRevision.source` on samples and `sources` on statistics are plain objects, not proxies. To filter queries by a source, get a `SourceProxy` from `querySources()` and match it by `bundleIdentifier`.
190
+
157
191
  ## Migration to 9.0.0
158
192
 
159
193
  There are a lot of under-the-hood changes in version 9.0.0, some of them are breaking (although I've tried to reduce it as much as possible).
@@ -16,20 +16,15 @@ Pod::Spec.new do |s|
16
16
  s.source_files = [
17
17
  # Implementation (Swift)
18
18
  "ios/**/*.{swift}",
19
- # Autolinking/Registration (Objective-C++)
20
- "ios/**/*.{m,mm}",
21
- # Implementation (C++ objects)
19
+ # Implementation (C++ objects)
22
20
  "cpp/**/*.{hpp,cpp}",
23
21
  ]
24
22
 
25
- s.public_header_files = "ios/**/*.h"
26
-
27
23
  s.pod_target_xcconfig = {
28
24
  # C++ compiler flags, mainly for folly.
29
25
  "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES",
30
26
  # Allow importing Objective-C headers in Swift without bridging header
31
- "CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES" => "YES",
32
- "SWIFT_INCLUDE_PATHS" => "$(inherited) $(PODS_TARGET_SRCROOT)/ios"
27
+ "CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES" => "YES"
33
28
  }
34
29
 
35
30
  load 'nitrogen/generated/ios/ReactNativeHealthkit+autolinking.rb'
@@ -45,6 +40,15 @@ Pod::Spec.new do |s|
45
40
  :execution_position => :before_compile,
46
41
  }
47
42
 
43
+ # The three packages are released in lockstep on a single version (changesets
44
+ # `fixed`), so the only combination this pod is ever built and tested against
45
+ # is a core of the exact same version. Requiring that here turns a mismatch --
46
+ # including a mixed-version family, e.g. this pod at 16.0.0 next to its
47
+ # sibling at 16.1.0 -- into a `pod install` version conflict naming both pods,
48
+ # instead of a Swift compile error inside Pods or a runtime surprise. Core's
49
+ # Swift surface is internal to these packages and is not semver-managed, so a
50
+ # same-major requirement would not be enough.
51
+ s.dependency 'ReactNativeHealthkitCore', s.version.to_s
48
52
  s.dependency 'React-jsi'
49
53
  s.dependency 'React-callinvoker'
50
54
  install_modules_dependencies(s)
package/app.plugin.js CHANGED
@@ -1,94 +1,33 @@
1
- const {
2
- withPlugins,
3
- createRunOncePlugin,
4
- withEntitlementsPlist,
5
- withInfoPlist,
6
- } = require('@expo/config-plugins')
7
-
8
- /**
9
- * @typedef ConfigPlugin
10
- * @type {import('@expo/config-plugins').ConfigPlugin<T>}
11
- * @template T = void
12
- */
13
-
14
- /**
15
- * @typedef InfoPlistConfig
16
- * @type {{
17
- * NSHealthShareUsageDescription?: string | boolean,
18
- * NSHealthUpdateUsageDescription?: string | boolean
19
- * }}
20
- */
21
-
22
- /**
23
- * @typedef AppPluginConfig
24
- * @type {InfoPlistConfig & { background?: boolean }}
25
- */
26
-
27
- /**
28
- * @type {ConfigPlugin<{background: boolean}>}
29
- */
30
- const withEntitlementsPlugin = (
31
- config,
32
- /**
33
- * @type {{background: boolean} | undefined}
34
- * */
35
- props,
36
- ) =>
37
- withEntitlementsPlist(config, (config) => {
38
- config.modResults['com.apple.developer.healthkit'] = true
39
-
40
- // background is enabled by default, but possible to opt-out from
41
- // (haven't seen any drawbacks from having it enabled)
42
- if (props?.background !== false) {
43
- config.modResults['com.apple.developer.healthkit.background-delivery'] =
44
- true
45
- }
46
-
47
- return config
48
- })
49
-
50
- /**
51
- * @type {ConfigPlugin<InfoPlistConfig>}
52
- */
53
- const withInfoPlistPlugin = (
54
- config,
55
- /**
56
- * @type {{NSHealthShareUsageDescription?: string | true, NSHealthUpdateUsageDescription?: string | false} | undefined}
57
- * */
58
- props,
59
- ) =>
60
- withInfoPlist(config, (config) => {
61
- config.modResults.NSHealthShareUsageDescription =
62
- typeof props?.NSHealthShareUsageDescription === 'string'
63
- ? props.NSHealthShareUsageDescription
64
- : typeof config.modResults.NSHealthShareUsageDescription === 'string'
65
- ? config.modResults.NSHealthShareUsageDescription
66
- : `${config.name} wants to read your health data`
67
-
68
- if (props?.NSHealthUpdateUsageDescription !== false) {
69
- config.modResults.NSHealthUpdateUsageDescription =
70
- typeof props?.NSHealthUpdateUsageDescription === 'string'
71
- ? props.NSHealthUpdateUsageDescription
72
- : typeof config.modResults.NSHealthUpdateUsageDescription === 'string'
73
- ? config.modResults.NSHealthUpdateUsageDescription
74
- : `${config.name} wants to update your health data`
75
- }
76
-
77
- return config
78
- })
79
-
80
- const pkg = require('./package.json')
81
-
82
- /**
83
- * @type {ConfigPlugin<AppPluginConfig>}
84
- */
85
- const healthkitAppPlugin = (config, props) =>
86
- withPlugins(config, [
87
- [withEntitlementsPlugin, props],
88
- [withInfoPlistPlugin, props],
89
- ])
90
-
91
- /**
92
- * @type {ConfigPlugin<AppPluginConfig>}
93
- */
94
- module.exports = createRunOncePlugin(healthkitAppPlugin, pkg.name, pkg.version)
1
+ // GENERATED FILE - do not edit by hand (gitignored).
2
+ // Compiled from app.plugin.ts by `bun run build:plugin`.
3
+ // Expo loads this file, not the .ts source, so edit app.plugin.ts and rebuild.
4
+ "use strict";
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const config_plugins_1 = require("@expo/config-plugins");
10
+ const plugin_1 = require("@react-native-healthkit/core/plugin");
11
+ const package_json_1 = __importDefault(require("./package.json"));
12
+ const healthkitAppPlugin = (config, props) => {
13
+ const withUpdateUsageDescription = (config) => props?.NSHealthUpdateUsageDescription === false
14
+ ? config
15
+ : (0, plugin_1.withUsageDescription)(config, {
16
+ key: 'NSHealthUpdateUsageDescription',
17
+ value: props?.NSHealthUpdateUsageDescription,
18
+ fallback: (appName) => `${appName} wants to update your health data`,
19
+ fallbackAppName: package_json_1.default.name,
20
+ });
21
+ return (0, config_plugins_1.withPlugins)(config, [
22
+ [
23
+ plugin_1.withHealthKit,
24
+ {
25
+ background: props?.background,
26
+ NSHealthShareUsageDescription: props?.NSHealthShareUsageDescription,
27
+ fallbackAppName: package_json_1.default.name,
28
+ },
29
+ ],
30
+ withUpdateUsageDescription,
31
+ ]);
32
+ };
33
+ exports.default = (0, config_plugins_1.createRunOncePlugin)(healthkitAppPlugin, package_json_1.default.name, package_json_1.default.version);
package/app.plugin.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  type ConfigPlugin,
3
3
  createRunOncePlugin,
4
- withAppDelegate,
5
- withEntitlementsPlist,
6
- withInfoPlist,
7
4
  withPlugins,
8
5
  } from '@expo/config-plugins'
6
+ import {
7
+ withHealthKit,
8
+ withUsageDescription,
9
+ } from '@react-native-healthkit/core/plugin'
9
10
 
10
11
  import pkg from './package.json'
11
12
 
@@ -17,112 +18,36 @@ type InfoPlistConfig = {
17
18
  }
18
19
 
19
20
  type AppPluginConfig = InfoPlistConfig & {
21
+ /**
22
+ * Adds the background-delivery entitlement. Enabled by default; set to
23
+ * `false` to opt out. Launch-time observer registration needs no AppDelegate
24
+ * change: the core pod hooks UIApplicationDidFinishLaunchingNotification
25
+ * itself.
26
+ */
20
27
  background?: BackgroundConfig
21
28
  }
22
29
 
23
- const withEntitlementsPlugin: ConfigPlugin<{
24
- background?: BackgroundConfig
25
- }> = (config, props) => {
26
- return withEntitlementsPlist(config, (configPlist) => {
27
- configPlist.modResults['com.apple.developer.healthkit'] = true
28
-
29
- // background is enabled by default, but possible to opt-out from
30
- // (haven't seen any drawbacks from having it enabled)
31
- if (props?.background !== false) {
32
- configPlist.modResults[
33
- 'com.apple.developer.healthkit.background-delivery'
34
- ] = true
35
- }
36
-
37
- return configPlist
38
- })
39
- }
40
-
41
- const withInfoPlistPlugin: ConfigPlugin<InfoPlistConfig> = (config, props) => {
42
- return withInfoPlist(config, (configPlist) => {
43
- const existingShareDescription =
44
- configPlist.modResults.NSHealthShareUsageDescription
45
- configPlist.modResults.NSHealthShareUsageDescription =
46
- typeof props?.NSHealthShareUsageDescription === 'string'
47
- ? props.NSHealthShareUsageDescription
48
- : typeof existingShareDescription === 'string'
49
- ? existingShareDescription
50
- : `${config.name ?? pkg.name} wants to read your health data`
51
-
52
- if (props?.NSHealthUpdateUsageDescription !== false) {
53
- const existingUpdateDescription =
54
- configPlist.modResults.NSHealthUpdateUsageDescription
55
- configPlist.modResults.NSHealthUpdateUsageDescription =
56
- typeof props?.NSHealthUpdateUsageDescription === 'string'
57
- ? props.NSHealthUpdateUsageDescription
58
- : typeof existingUpdateDescription === 'string'
59
- ? existingUpdateDescription
60
- : `${config.name ?? pkg.name} wants to update your health data`
61
- }
62
-
63
- return configPlist
64
- })
65
- }
66
-
67
- const withAppDelegatePlugin: ConfigPlugin<{
68
- background?: BackgroundConfig
69
- }> = (config, props) => {
70
- if (props?.background === false) {
71
- return config
72
- }
73
-
74
- return withAppDelegate(config, (configDelegate) => {
75
- const contents = configDelegate.modResults.contents
76
-
77
- // Add import for HealthKit if not already present
78
- if (!contents.includes('import HealthKit')) {
79
- configDelegate.modResults.contents =
80
- configDelegate.modResults.contents.replace(
81
- /^(import .+\n)/m,
82
- '$1import HealthKit\n',
83
- )
84
- }
85
-
86
- // Insert BackgroundDeliveryManager setup into didFinishLaunchingWithOptions
87
- const setupCall =
88
- ' BackgroundDeliveryManager.shared.setupBackgroundObservers()\n'
89
-
90
- if (
91
- !configDelegate.modResults.contents.includes('BackgroundDeliveryManager')
92
- ) {
93
- // Match the opening of didFinishLaunchingWithOptions and insert after the
94
- // opening brace. `[^{]*` rather than `.+` for two reasons: `.` does not
95
- // match newlines, and Expo SDK 54+ templates spread this signature over
96
- // several lines; and refusing to cross a `{` keeps the match from
97
- // spanning out of an earlier `application(...)` overload into this one.
98
- const withSetup = configDelegate.modResults.contents.replace(
99
- /(func application\([^{]*didFinishLaunchingWithOptions[^{]*\{)\n/,
100
- `$1\n${setupCall}`,
101
- )
102
-
103
- if (withSetup === configDelegate.modResults.contents) {
104
- // Don't fail the build, but don't fail silently either — background
105
- // delivery just won't survive app termination, which is otherwise
106
- // very hard to trace back to here.
107
- console.warn(
108
- '[react-native-healthkit] Could not find didFinishLaunchingWithOptions in AppDelegate; ' +
109
- 'BackgroundDeliveryManager.shared.setupBackgroundObservers() was not inserted. ' +
110
- 'HealthKit background delivery will not work until it is added manually.',
111
- )
112
- }
113
-
114
- configDelegate.modResults.contents = withSetup
115
- }
116
-
117
- return configDelegate
118
- })
119
- }
120
-
121
30
  const healthkitAppPlugin: ConfigPlugin<AppPluginConfig> = (config, props) => {
31
+ const withUpdateUsageDescription: ConfigPlugin = (config) =>
32
+ props?.NSHealthUpdateUsageDescription === false
33
+ ? config
34
+ : withUsageDescription(config, {
35
+ key: 'NSHealthUpdateUsageDescription',
36
+ value: props?.NSHealthUpdateUsageDescription,
37
+ fallback: (appName) => `${appName} wants to update your health data`,
38
+ fallbackAppName: pkg.name,
39
+ })
40
+
122
41
  return withPlugins(config, [
123
- [withEntitlementsPlugin, props],
124
- [withInfoPlistPlugin, props],
125
- [withAppDelegatePlugin, props],
42
+ [
43
+ withHealthKit,
44
+ {
45
+ background: props?.background,
46
+ NSHealthShareUsageDescription: props?.NSHealthShareUsageDescription,
47
+ fallbackAppName: pkg.name,
48
+ },
49
+ ],
50
+ withUpdateUsageDescription,
126
51
  ])
127
52
  }
128
53
 
@@ -1,5 +1,6 @@
1
1
  import HealthKit
2
2
  import NitroModules
3
+ import ReactNativeHealthkitCore
3
4
 
4
5
  class CategoryTypeModule: HybridCategoryTypeModuleSpec {
5
6
  func saveCategorySample(
@@ -1,5 +1,6 @@
1
1
  import HealthKit
2
2
  import NitroModules
3
+ import ReactNativeHealthkitCore
3
4
 
4
5
  class CharacteristicTypeModule: HybridCharacteristicTypeModuleSpec {
5
6
  func getBloodTypeAsync() -> Promise<BloodType> {
@@ -32,10 +33,10 @@ class CharacteristicTypeModule: HybridCharacteristicTypeModuleSpec {
32
33
  }
33
34
  }
34
35
 
35
- // Using the global 'store' instance defined in Auth.swift
36
+ // Uses the healthStore shared through ReactNativeHealthkitCore
36
37
 
37
38
  func getBiologicalSex() throws -> BiologicalSex {
38
- let biologicalSexObject = try store.biologicalSex()
39
+ let biologicalSexObject = try healthStore.biologicalSex()
39
40
  if let biologicalSex = BiologicalSex(
40
41
  rawValue: Int32(biologicalSexObject.biologicalSex.rawValue)) {
41
42
  return biologicalSex
@@ -46,7 +47,7 @@ class CharacteristicTypeModule: HybridCharacteristicTypeModuleSpec {
46
47
 
47
48
  func getDateOfBirth() throws -> Date? {
48
49
  do {
49
- let components = try store.dateOfBirthComponents()
50
+ let components = try healthStore.dateOfBirthComponents()
50
51
  return components.date
51
52
  } catch {
52
53
  let nsError = error as NSError
@@ -69,7 +70,7 @@ class CharacteristicTypeModule: HybridCharacteristicTypeModuleSpec {
69
70
  }
70
71
 
71
72
  func getBloodType() throws -> BloodType {
72
- let bloodTypeObject = try store.bloodType()
73
+ let bloodTypeObject = try healthStore.bloodType()
73
74
 
74
75
  if let bloodType = BloodType(rawValue: Int32(bloodTypeObject.bloodType.rawValue)) {
75
76
  return bloodType
@@ -80,7 +81,7 @@ class CharacteristicTypeModule: HybridCharacteristicTypeModuleSpec {
80
81
  }
81
82
 
82
83
  func getFitzpatrickSkinType() throws -> FitzpatrickSkinType {
83
- let skinTypeObject = try store.fitzpatrickSkinType()
84
+ let skinTypeObject = try healthStore.fitzpatrickSkinType()
84
85
 
85
86
  if let skinType = FitzpatrickSkinType(rawValue: Int32(skinTypeObject.skinType.rawValue)) {
86
87
  return skinType
@@ -91,7 +92,7 @@ class CharacteristicTypeModule: HybridCharacteristicTypeModuleSpec {
91
92
  }
92
93
 
93
94
  func getWheelchairUse() throws -> WheelchairUse {
94
- let wheelchairUseObject = try store.wheelchairUse()
95
+ let wheelchairUseObject = try healthStore.wheelchairUse()
95
96
  if let wheelChairUse = WheelchairUse(
96
97
  rawValue: Int32(wheelchairUseObject.wheelchairUse.rawValue)) {
97
98
  return wheelChairUse
@@ -7,6 +7,15 @@
7
7
 
8
8
  import Foundation
9
9
  import HealthKit
10
+ import ReactNativeHealthkitCore
11
+
12
+ let logPrefix = "[react-native-healthkit]"
13
+
14
+ /// Namespaces this package's persisted background-delivery types in the shared
15
+ /// BackgroundDeliveryManager. The id matches the UserDefaults keys used before
16
+ /// the manager moved to ReactNativeHealthkitCore, so existing installs keep
17
+ /// their configuration.
18
+ let backgroundDeliveryScope = BackgroundDeliveryScope(id: "com.kingstinct.healthkit")
10
19
 
11
20
  let INIT_ERROR = "HEALTHKIT_INIT_ERROR"
12
21
  let INIT_ERROR_MESSAGE = "HealthKit not initialized"
@@ -14,17 +23,7 @@ let TYPE_IDENTIFIER_ERROR = "HEALTHKIT_TYPE_IDENTIFIER_NOT_RECOGNIZED_ERROR"
14
23
  let QUERY_ERROR = "HEALTHKIT_QUERY_ERROR"
15
24
  let GENERIC_ERROR = "HEALTHKIT_ERROR"
16
25
 
17
- let HKCharacteristicTypeIdentifier_PREFIX = "HKCharacteristicTypeIdentifier"
18
- let HKQuantityTypeIdentifier_PREFIX = "HKQuantityTypeIdentifier"
19
- let HKCategoryTypeIdentifier_PREFIX = "HKCategoryTypeIdentifier"
20
- let HKCorrelationTypeIdentifier_PREFIX = "HKCorrelationTypeIdentifier"
21
26
  let HKActivitySummaryTypeIdentifier = "HKActivitySummaryTypeIdentifier"
22
- let HKAudiogramTypeIdentifier = "HKAudiogramSampleType"
23
- let HKWorkoutTypeIdentifier = "HKWorkoutTypeIdentifier"
24
- let HKWorkoutRouteTypeIdentifier = "HKWorkoutRouteTypeIdentifier"
25
- let HKDataTypeIdentifierHeartbeatSeries = "HKDataTypeIdentifierHeartbeatSeries"
26
- let HKStateOfMindTypeIdentifier = "HKStateOfMindTypeIdentifier"
27
- let HKElectrocardiogramType = "HKElectrocardiogramType"
28
27
 
29
28
  let HKWorkoutActivityTypePropertyName = "activityType"
30
29
  let HKWorkoutSessionLocationTypePropertyName = "locationType"