@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.
- package/README.md +37 -3
- package/ReactNativeHealthkit.podspec +11 -7
- package/app.plugin.js +33 -94
- package/app.plugin.ts +29 -104
- package/ios/CategoryTypeModule.swift +1 -0
- package/ios/CharacteristicTypeModule.swift +7 -6
- package/ios/Constants.swift +9 -10
- package/ios/CoreModule.swift +77 -42
- package/ios/CorrelationTypeModule.swift +3 -2
- package/ios/ElectrocardiogramModule.swift +4 -3
- package/ios/HeartbeatSeriesModule.swift +4 -3
- package/ios/Helpers.swift +11 -216
- package/ios/MedicationModule.swift +9 -8
- package/ios/MemoryEstimation.swift +84 -0
- package/ios/PredicateHelpers.swift +16 -86
- package/ios/QuantityTypeModule.swift +37 -28
- package/ios/Serializers.swift +34 -70
- package/ios/SourceProxy.swift +15 -5
- package/ios/StateOfMindModule.swift +3 -2
- package/ios/WorkoutProxy.swift +31 -13
- package/ios/WorkoutsModule.swift +4 -3
- package/lib/commonjs/generated/healthkit.generated.js +15 -3
- package/lib/commonjs/healthkit.ios.js +6 -2
- package/lib/commonjs/healthkit.js +24 -79
- package/lib/commonjs/types/Auth.js +3 -18
- package/lib/commonjs/types/Background.js +2 -10
- package/lib/commonjs/types/QueryOptions.js +2 -17
- package/lib/module/generated/healthkit.generated.js +14 -2
- package/lib/module/healthkit.ios.js +4 -0
- package/lib/module/healthkit.js +22 -77
- package/lib/module/types/Auth.js +1 -18
- package/lib/module/types/Background.js +1 -10
- package/lib/module/types/QueryOptions.js +1 -17
- package/lib/typescript/generated/healthkit.generated.d.ts +20 -7
- package/lib/typescript/healthkit.d.ts +4 -2
- package/lib/typescript/healthkit.ios.d.ts +13 -9
- package/lib/typescript/specs/CoreModule.nitro.d.ts +22 -4
- package/lib/typescript/specs/SourceProxy.nitro.d.ts +2 -5
- package/lib/typescript/types/Auth.d.ts +1 -16
- package/lib/typescript/types/Background.d.ts +1 -9
- package/lib/typescript/types/Device.d.ts +1 -13
- package/lib/typescript/types/QuantityType.d.ts +3 -3
- package/lib/typescript/types/QueryOptions.d.ts +3 -37
- package/lib/typescript/types/Shared.d.ts +2 -25
- package/lib/typescript/types/Source.d.ts +1 -10
- package/lib/typescript/types/Subscriptions.d.ts +2 -3
- package/lib/typescript/types/Units.d.ts +1 -1
- package/nitrogen/generated/ios/ReactNativeHealthkit-Swift-Cxx-Bridge.cpp +24 -16
- package/nitrogen/generated/ios/ReactNativeHealthkit-Swift-Cxx-Bridge.hpp +91 -12
- package/nitrogen/generated/ios/c++/HybridCategoryTypeModuleSpecSwift.hpp +8 -5
- package/nitrogen/generated/ios/c++/HybridCoreModuleSpecSwift.hpp +8 -0
- package/nitrogen/generated/ios/c++/HybridCorrelationTypeModuleSpecSwift.hpp +8 -5
- package/nitrogen/generated/ios/c++/HybridElectrocardiogramModuleSpecSwift.hpp +8 -5
- package/nitrogen/generated/ios/c++/HybridHeartbeatSeriesModuleSpecSwift.hpp +8 -5
- package/nitrogen/generated/ios/c++/HybridMedicationModuleSpecSwift.hpp +8 -5
- package/nitrogen/generated/ios/c++/HybridQuantityTypeModuleSpecSwift.hpp +8 -5
- package/nitrogen/generated/ios/c++/HybridStateOfMindModuleSpecSwift.hpp +8 -5
- package/nitrogen/generated/ios/c++/HybridWorkoutProxySpecSwift.hpp +3 -4
- package/nitrogen/generated/ios/c++/HybridWorkoutsModuleSpecSwift.hpp +6 -3
- package/nitrogen/generated/ios/swift/CategorySample.swift +8 -8
- package/nitrogen/generated/ios/swift/CategoryTypeIdentifier.swift +8 -0
- package/nitrogen/generated/ios/swift/CategoryTypeIdentifierWriteable.swift +8 -0
- package/nitrogen/generated/ios/swift/CorrelationSample.swift +8 -8
- package/nitrogen/generated/ios/swift/ElectrocardiogramSample.swift +8 -8
- package/nitrogen/generated/ios/swift/Func_void_std__unordered_map_std__string__std__chrono__system_clock__time_point_.swift +54 -0
- package/nitrogen/generated/ios/swift/HeartbeatSeriesSample.swift +8 -8
- package/nitrogen/generated/ios/swift/HybridCoreModuleSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridCoreModuleSpec_cxx.swift +25 -0
- package/nitrogen/generated/ios/swift/MedicationDoseEvent.swift +8 -8
- package/nitrogen/generated/ios/swift/ObjectTypeIdentifier.swift +12 -4
- package/nitrogen/generated/ios/swift/QuantitySample.swift +8 -8
- package/nitrogen/generated/ios/swift/QuantityTypeIdentifier.swift +4 -4
- package/nitrogen/generated/ios/swift/QuantityTypeIdentifierWriteable.swift +4 -4
- package/nitrogen/generated/ios/swift/QueryStatisticsResponse.swift +6 -13
- package/nitrogen/generated/ios/swift/QueryStatisticsResponseFromSingleSource.swift +4 -11
- package/nitrogen/generated/ios/swift/SampleTypeIdentifier.swift +12 -4
- package/nitrogen/generated/ios/swift/SampleTypeIdentifierWriteable.swift +12 -4
- package/nitrogen/generated/ios/swift/SourceRevision.swift +4 -11
- package/nitrogen/generated/ios/swift/StateOfMindSample.swift +8 -8
- package/nitrogen/generated/ios/swift/WorkoutActivityType.swift +8 -0
- package/nitrogen/generated/shared/c++/CategorySample.hpp +7 -7
- package/nitrogen/generated/shared/c++/CategoryTypeIdentifier.hpp +67 -59
- package/nitrogen/generated/shared/c++/CategoryTypeIdentifierWriteable.hpp +67 -59
- package/nitrogen/generated/shared/c++/CorrelationSample.hpp +7 -7
- package/nitrogen/generated/shared/c++/ElectrocardiogramSample.hpp +7 -7
- package/nitrogen/generated/shared/c++/HeartbeatSeriesSample.hpp +7 -7
- package/nitrogen/generated/shared/c++/HybridCoreModuleSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridCoreModuleSpec.hpp +3 -1
- package/nitrogen/generated/shared/c++/MedicationDoseEvent.hpp +7 -7
- package/nitrogen/generated/shared/c++/ObjectTypeIdentifier.hpp +206 -198
- package/nitrogen/generated/shared/c++/QuantitySample.hpp +7 -7
- package/nitrogen/generated/shared/c++/QuantityTypeIdentifier.hpp +73 -73
- package/nitrogen/generated/shared/c++/QuantityTypeIdentifierWriteable.hpp +73 -73
- package/nitrogen/generated/shared/c++/QueryStatisticsResponse.hpp +8 -9
- package/nitrogen/generated/shared/c++/QueryStatisticsResponseFromSingleSource.hpp +8 -9
- package/nitrogen/generated/shared/c++/SampleTypeIdentifier.hpp +199 -191
- package/nitrogen/generated/shared/c++/SampleTypeIdentifierWriteable.hpp +193 -185
- package/nitrogen/generated/shared/c++/SourceRevision.hpp +8 -9
- package/nitrogen/generated/shared/c++/StateOfMindSample.hpp +7 -7
- package/nitrogen/generated/shared/c++/WorkoutActivityType.hpp +4 -0
- package/package.json +8 -3
- package/src/generated/healthkit-schema.json +53 -8
- package/src/generated/healthkit.generated.ts +22 -6
- package/src/healthkit.ios.ts +6 -0
- package/src/healthkit.ts +32 -84
- package/src/specs/CoreModule.nitro.ts +25 -4
- package/src/specs/SourceProxy.nitro.ts +2 -4
- package/src/test-setup.ts +1 -0
- package/src/type-tests/generated-typing.ts +30 -8
- package/src/types/Auth.ts +4 -17
- package/src/types/Background.ts +1 -9
- package/src/types/Device.ts +1 -13
- package/src/types/QuantityType.ts +3 -3
- package/src/types/QueryOptions.ts +7 -65
- package/src/types/Shared.ts +7 -28
- package/src/types/Source.ts +1 -11
- package/src/types/Subscriptions.ts +2 -3
- package/src/types/Units.ts +1 -1
- package/ios/BackgroundDeliveryManager.swift +0 -277
- package/ios/Bridge.h +0 -10
- package/ios/ExceptionCatcher.h +0 -5
- package/ios/ExceptionCatcher.mm +0 -32
- 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
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
[
|
|
124
|
-
|
|
125
|
-
|
|
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 CharacteristicTypeModule: HybridCharacteristicTypeModuleSpec {
|
|
5
6
|
func getBloodTypeAsync() -> Promise<BloodType> {
|
|
@@ -32,10 +33,10 @@ class CharacteristicTypeModule: HybridCharacteristicTypeModuleSpec {
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
//
|
|
36
|
+
// Uses the healthStore shared through ReactNativeHealthkitCore
|
|
36
37
|
|
|
37
38
|
func getBiologicalSex() throws -> BiologicalSex {
|
|
38
|
-
let biologicalSexObject = try
|
|
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
|
|
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
|
|
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
|
|
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
|
|
95
|
+
let wheelchairUseObject = try healthStore.wheelchairUse()
|
|
95
96
|
if let wheelChairUse = WheelchairUse(
|
|
96
97
|
rawValue: Int32(wheelchairUseObject.wheelchairUse.rawValue)) {
|
|
97
98
|
return wheelChairUse
|
package/ios/Constants.swift
CHANGED
|
@@ -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"
|