@kingstinct/react-native-healthkit 4.2.0 → 4.4.1
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 +1 -1
- package/ios/ReactNativeHealthkit.m +6 -1
- package/ios/ReactNativeHealthkit.swift +365 -176
- package/lib/commonjs/index.ios.js +129 -105
- package/lib/commonjs/index.ios.js.map +1 -1
- package/lib/commonjs/index.js +58 -44
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native-types.js +136 -106
- package/lib/commonjs/native-types.js.map +1 -1
- package/lib/commonjs/types.js +4 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/example/App.js +197 -0
- package/lib/index.ios.js +310 -0
- package/lib/index.js +44 -0
- package/lib/module/index.ios.js +130 -106
- package/lib/module/index.ios.js.map +1 -1
- package/lib/module/index.js +52 -37
- package/lib/module/index.js.map +1 -1
- package/lib/module/native-types.js +127 -104
- package/lib/module/native-types.js.map +1 -1
- package/lib/module/types.js +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/native-types.js +447 -0
- package/lib/src/index.ios.js +314 -0
- package/lib/src/index.js +45 -0
- package/lib/src/native-types.js +453 -0
- package/lib/src/types.js +1 -0
- package/lib/types.js +1 -0
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.ios.d.ts +1 -1
- package/lib/typescript/src/native-types.d.ts +458 -205
- package/lib/typescript/src/types.d.ts +87 -81
- package/package.json +21 -35
- package/src/index.ios.tsx +303 -269
- package/src/index.tsx +63 -45
- package/src/native-types.ts +318 -290
- package/src/types.ts +120 -105
- package/.DS_Store +0 -0
- package/.circleci/config.yml +0 -98
- package/.editorconfig +0 -15
- package/.gitattributes +0 -3
- package/.github/workflows/main.yml +0 -32
- package/.gitignore +0 -60
- package/CONTRIBUTING.md +0 -184
- package/babel.config.js +0 -3
- package/example-expo/.expo/README.md +0 -15
- package/example-expo/.expo/devices.json +0 -8
- package/example-expo/.expo/packager-info.json +0 -5
- package/example-expo/.expo/settings.json +0 -9
- package/example-expo/.expo-shared/assets.json +0 -4
- package/example-expo/.gitignore +0 -14
- package/example-expo/App.tsx +0 -376
- package/example-expo/app.json +0 -43
- package/example-expo/assets/adaptive-icon.png +0 -0
- package/example-expo/assets/favicon.png +0 -0
- package/example-expo/assets/icon.png +0 -0
- package/example-expo/assets/splash.png +0 -0
- package/example-expo/babel.config.js +0 -8
- package/example-expo/build-1653040579600.ipa +0 -0
- package/example-expo/build-1653041063216.ipa +0 -0
- package/example-expo/eas.json +0 -18
- package/example-expo/package.json +0 -32
- package/example-expo/tsconfig.json +0 -6
- package/example-expo/yarn.lock +0 -6857
- package/lib/typescript/example-expo/App.d.ts +0 -3
- package/lib/typescript/src/__tests__/index.test.d.ts +0 -0
- package/src/__tests__/index.test.tsx +0 -1
- package/tsconfig.json +0 -27
- package/yarn.lock +0 -10241
package/src/index.ios.tsx
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
2
|
+
|
|
2
3
|
import Native, {
|
|
3
4
|
EventEmitter,
|
|
4
|
-
|
|
5
|
+
HKQuantityTypeIdentifier,
|
|
6
|
+
HKUnit,
|
|
5
7
|
HKCategoryTypeIdentifier,
|
|
8
|
+
} from './native-types'
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
HKSampleTypeIdentifier,
|
|
12
|
+
HKCategorySampleRaw,
|
|
6
13
|
HKCategoryValueForIdentifier,
|
|
7
14
|
HKCharacteristicTypeIdentifier,
|
|
8
15
|
HKCorrelationRaw,
|
|
9
16
|
HKCorrelationTypeIdentifier,
|
|
10
17
|
HKQuantitySampleRaw,
|
|
11
|
-
HKQuantityTypeIdentifier,
|
|
12
|
-
HKSampleTypeIdentifier,
|
|
13
|
-
HKUnit,
|
|
14
18
|
HKUnitSI,
|
|
15
19
|
HKUnitSIPrefix,
|
|
16
20
|
HKWorkoutRaw,
|
|
17
21
|
MetadataMapperForCategoryIdentifier,
|
|
18
|
-
|
|
22
|
+
ReadPermissions,
|
|
23
|
+
WritePermissions,
|
|
24
|
+
HealthkitAuthorization,
|
|
25
|
+
HKAuthorizationRequestStatus,
|
|
26
|
+
} from './native-types'
|
|
19
27
|
import type {
|
|
20
28
|
GenericQueryOptions,
|
|
21
29
|
GetMostRecentCategorySampleFn,
|
|
@@ -23,6 +31,7 @@ import type {
|
|
|
23
31
|
GetMostRecentWorkoutFn,
|
|
24
32
|
GetPreferredUnitFn,
|
|
25
33
|
GetPreferredUnitsFn,
|
|
34
|
+
GetWorkoutRoutesFn,
|
|
26
35
|
HKCategorySample,
|
|
27
36
|
HKCorrelation,
|
|
28
37
|
HKQuantitySample,
|
|
@@ -37,76 +46,74 @@ import type {
|
|
|
37
46
|
SaveQuantitySampleFn,
|
|
38
47
|
SaveWorkoutSampleFn,
|
|
39
48
|
SubscribeToChangesFn,
|
|
40
|
-
} from './types'
|
|
49
|
+
} from './types'
|
|
50
|
+
import type { ValueOf } from 'type-fest'
|
|
41
51
|
|
|
42
52
|
const getPreferredUnit: GetPreferredUnitFn = async (type) => {
|
|
43
|
-
const [unit] = await getPreferredUnits([type])
|
|
44
|
-
return unit
|
|
45
|
-
}
|
|
53
|
+
const [unit] = await getPreferredUnits([type])
|
|
54
|
+
return unit
|
|
55
|
+
}
|
|
46
56
|
|
|
47
57
|
const ensureUnit = async <TUnit extends HKUnit>(
|
|
48
58
|
type: HKQuantityTypeIdentifier,
|
|
49
|
-
providedUnit?: TUnit
|
|
59
|
+
providedUnit?: TUnit,
|
|
50
60
|
) => {
|
|
51
61
|
if (providedUnit) {
|
|
52
|
-
return providedUnit
|
|
62
|
+
return providedUnit
|
|
53
63
|
}
|
|
54
|
-
const unit = await Native.getPreferredUnits([type])
|
|
55
|
-
return unit[type] as TUnit
|
|
56
|
-
}
|
|
64
|
+
const unit = await Native.getPreferredUnits([type])
|
|
65
|
+
return unit[type] as TUnit
|
|
66
|
+
}
|
|
57
67
|
|
|
58
68
|
function deserializeSample<
|
|
59
69
|
TIdentifier extends HKQuantityTypeIdentifier,
|
|
60
70
|
TUnit extends HKUnit
|
|
61
71
|
>(
|
|
62
|
-
sample: HKQuantitySampleRaw<TIdentifier, TUnit
|
|
72
|
+
sample: HKQuantitySampleRaw<TIdentifier, TUnit>,
|
|
63
73
|
): HKQuantitySample<TIdentifier, TUnit> {
|
|
64
74
|
return {
|
|
65
75
|
...sample,
|
|
66
76
|
startDate: new Date(sample.startDate),
|
|
67
77
|
endDate: new Date(sample.endDate),
|
|
68
|
-
}
|
|
78
|
+
}
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
function deserializeWorkout<TEnergy extends HKUnit, TDistance extends HKUnit>(
|
|
72
|
-
sample: HKWorkoutRaw<TEnergy, TDistance
|
|
82
|
+
sample: HKWorkoutRaw<TEnergy, TDistance>,
|
|
73
83
|
): HKWorkout<TEnergy, TDistance> {
|
|
74
84
|
return {
|
|
75
85
|
...sample,
|
|
76
86
|
startDate: new Date(sample.startDate),
|
|
77
87
|
endDate: new Date(sample.endDate),
|
|
78
|
-
}
|
|
88
|
+
}
|
|
79
89
|
}
|
|
80
90
|
|
|
81
91
|
const deserializCategorySample = <T extends HKCategoryTypeIdentifier>(
|
|
82
|
-
sample: HKCategorySampleRaw<T
|
|
83
|
-
): HKCategorySample<T> => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
};
|
|
89
|
-
};
|
|
92
|
+
sample: HKCategorySampleRaw<T>,
|
|
93
|
+
): HKCategorySample<T> => ({
|
|
94
|
+
...sample,
|
|
95
|
+
startDate: new Date(sample.startDate),
|
|
96
|
+
endDate: new Date(sample.endDate),
|
|
97
|
+
})
|
|
90
98
|
|
|
91
|
-
const serializeDate = (date?: Date | null): string =>
|
|
92
|
-
return date ? date.toISOString() : new Date(0).toISOString();
|
|
93
|
-
};
|
|
99
|
+
const serializeDate = (date?: Date | null): string => (date ? date.toISOString() : new Date(0).toISOString())
|
|
94
100
|
|
|
95
101
|
const prepareOptions = (options: GenericQueryOptions) => {
|
|
96
|
-
const limit =
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
102
|
+
const limit = !options.limit || options.limit === Infinity ? 0 : options.limit
|
|
103
|
+
const ascending = options.ascending ?? limit === 0
|
|
104
|
+
const from = serializeDate(options.from)
|
|
105
|
+
const to = serializeDate(options.to)
|
|
106
|
+
return {
|
|
107
|
+
limit, ascending, from, to,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
103
110
|
|
|
104
111
|
const queryQuantitySamples: QueryQuantitySamplesFn = async (
|
|
105
112
|
identifier,
|
|
106
|
-
options
|
|
113
|
+
options,
|
|
107
114
|
) => {
|
|
108
|
-
const unit = await ensureUnit(identifier, options.unit)
|
|
109
|
-
const opts = prepareOptions(options)
|
|
115
|
+
const unit = await ensureUnit(identifier, options.unit)
|
|
116
|
+
const opts = prepareOptions(options)
|
|
110
117
|
|
|
111
118
|
const quantitySamples = await Native.queryQuantitySamples(
|
|
112
119
|
identifier,
|
|
@@ -114,121 +121,155 @@ const queryQuantitySamples: QueryQuantitySamplesFn = async (
|
|
|
114
121
|
opts.from,
|
|
115
122
|
opts.to,
|
|
116
123
|
opts.limit,
|
|
117
|
-
opts.ascending
|
|
118
|
-
)
|
|
124
|
+
opts.ascending,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return quantitySamples.map(deserializeSample)
|
|
128
|
+
}
|
|
119
129
|
|
|
120
|
-
|
|
121
|
-
|
|
130
|
+
async function getPreferredUnitsTyped<
|
|
131
|
+
TEnergy extends HKUnit,
|
|
132
|
+
TDistance extends HKUnit
|
|
133
|
+
>(options?: { readonly energyUnit?: TEnergy; readonly distanceUnit?: TDistance }) {
|
|
134
|
+
let energyUnit = options?.energyUnit
|
|
135
|
+
let distanceUnit = options?.distanceUnit
|
|
136
|
+
if (!energyUnit || !distanceUnit) {
|
|
137
|
+
const units = await Native.getPreferredUnits([
|
|
138
|
+
HKQuantityTypeIdentifier.distanceWalkingRunning,
|
|
139
|
+
HKQuantityTypeIdentifier.activeEnergyBurned,
|
|
140
|
+
])
|
|
141
|
+
if (!energyUnit) {
|
|
142
|
+
energyUnit = units[HKQuantityTypeIdentifier.distanceWalkingRunning] as
|
|
143
|
+
| TEnergy
|
|
144
|
+
| undefined
|
|
145
|
+
}
|
|
146
|
+
if (!distanceUnit) {
|
|
147
|
+
distanceUnit = units[HKQuantityTypeIdentifier.activeEnergyBurned] as
|
|
148
|
+
| TDistance
|
|
149
|
+
| undefined
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (!energyUnit) {
|
|
153
|
+
energyUnit = HKUnit.Kilocalories as TEnergy
|
|
154
|
+
}
|
|
155
|
+
if (!distanceUnit) {
|
|
156
|
+
distanceUnit = HKUnit.Meters as TDistance
|
|
157
|
+
}
|
|
158
|
+
return { energyUnit, distanceUnit }
|
|
159
|
+
}
|
|
122
160
|
|
|
123
161
|
const subscribeToChanges: SubscribeToChangesFn = async (
|
|
124
162
|
identifier,
|
|
125
|
-
callback
|
|
163
|
+
callback,
|
|
126
164
|
) => {
|
|
127
165
|
const subscription = EventEmitter.addListener(
|
|
128
166
|
'onChange',
|
|
129
167
|
({ typeIdentifier }) => {
|
|
130
168
|
if (typeIdentifier === identifier) {
|
|
131
|
-
callback()
|
|
169
|
+
callback()
|
|
132
170
|
}
|
|
133
|
-
}
|
|
134
|
-
)
|
|
171
|
+
},
|
|
172
|
+
)
|
|
135
173
|
|
|
136
174
|
const queryId = await Native.subscribeToObserverQuery(identifier).catch(
|
|
137
|
-
(error) => {
|
|
138
|
-
subscription.remove()
|
|
139
|
-
return Promise.reject(error)
|
|
140
|
-
}
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
return () => {
|
|
144
|
-
subscription.remove()
|
|
145
|
-
return Native.unsubscribeQuery(queryId)
|
|
146
|
-
}
|
|
147
|
-
}
|
|
175
|
+
async (error) => {
|
|
176
|
+
subscription.remove()
|
|
177
|
+
return Promise.reject(error)
|
|
178
|
+
},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
return async () => {
|
|
182
|
+
subscription.remove()
|
|
183
|
+
return Native.unsubscribeQuery(queryId)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
148
186
|
|
|
149
187
|
const getMostRecentQuantitySample: GetMostRecentQuantitySampleFn = async (
|
|
150
188
|
identifier,
|
|
151
|
-
unit
|
|
189
|
+
unit,
|
|
152
190
|
) => {
|
|
153
191
|
const samples = await queryQuantitySamples(identifier, {
|
|
154
192
|
limit: 1,
|
|
155
|
-
unit
|
|
156
|
-
})
|
|
157
|
-
return samples[0]
|
|
158
|
-
}
|
|
193
|
+
unit,
|
|
194
|
+
})
|
|
195
|
+
return samples[0]
|
|
196
|
+
}
|
|
159
197
|
|
|
160
198
|
function useMostRecentWorkout<
|
|
161
199
|
TEnergy extends HKUnit,
|
|
162
200
|
TDistance extends HKUnit
|
|
163
|
-
>(options?: { energyUnit?: TEnergy; distanceUnit?: TDistance }) {
|
|
201
|
+
>(options?: { readonly energyUnit?: TEnergy; readonly distanceUnit?: TDistance }) {
|
|
164
202
|
const [workout, setWorkout] = useState<HKWorkout<TEnergy, TDistance> | null>(
|
|
165
|
-
null
|
|
166
|
-
)
|
|
203
|
+
null,
|
|
204
|
+
)
|
|
167
205
|
useEffect(() => {
|
|
168
|
-
let cancelSubscription: (() => Promise<boolean>) | undefined
|
|
206
|
+
let cancelSubscription: (() => Promise<boolean>) | undefined
|
|
169
207
|
|
|
170
208
|
const init = async () => {
|
|
171
209
|
const { energyUnit, distanceUnit } = await getPreferredUnitsTyped(
|
|
172
|
-
options
|
|
173
|
-
)
|
|
210
|
+
options,
|
|
211
|
+
)
|
|
174
212
|
|
|
175
213
|
cancelSubscription = await subscribeToChanges(
|
|
176
214
|
'HKWorkoutTypeIdentifier',
|
|
177
|
-
() => {
|
|
178
|
-
getMostRecentWorkout({ energyUnit, distanceUnit })
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
215
|
+
async () => {
|
|
216
|
+
const w = await getMostRecentWorkout({ energyUnit, distanceUnit })
|
|
217
|
+
setWorkout(w)
|
|
218
|
+
},
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
void init()
|
|
183
222
|
return () => {
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
}, [options])
|
|
187
|
-
return workout
|
|
223
|
+
void cancelSubscription?.()
|
|
224
|
+
}
|
|
225
|
+
}, [options])
|
|
226
|
+
return workout
|
|
188
227
|
}
|
|
189
228
|
|
|
190
229
|
const getMostRecentCategorySample: GetMostRecentCategorySampleFn = async (
|
|
191
|
-
identifier
|
|
230
|
+
identifier,
|
|
192
231
|
) => {
|
|
193
232
|
const samples = await queryCategorySamples(identifier, {
|
|
194
233
|
limit: 1,
|
|
195
234
|
ascending: false,
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
return samples[0];
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
function useMostRecentCategorySample<
|
|
202
|
-
TCategory extends HKCategoryTypeIdentifier
|
|
203
|
-
>(identifier: TCategory) {
|
|
204
|
-
const [category, setCategory] = useState<HKCategorySample<TCategory> | null>(
|
|
205
|
-
null
|
|
206
|
-
);
|
|
207
|
-
const updater = useCallback(() => {
|
|
208
|
-
getMostRecentCategorySample(identifier).then(setCategory);
|
|
209
|
-
}, [identifier]);
|
|
235
|
+
})
|
|
210
236
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
return category;
|
|
237
|
+
return samples[0]
|
|
214
238
|
}
|
|
215
239
|
|
|
216
|
-
function useSubscribeToChanges<TIdentifier extends HKSampleTypeIdentifier
|
|
240
|
+
function useSubscribeToChanges<TIdentifier extends ValueOf<typeof HKSampleTypeIdentifier>>(
|
|
217
241
|
identifier: TIdentifier,
|
|
218
|
-
onChange: () => void
|
|
242
|
+
onChange: () => void,
|
|
219
243
|
): void {
|
|
220
244
|
useEffect(() => {
|
|
221
|
-
let cancelSubscription: (() => Promise<boolean>) | undefined
|
|
245
|
+
let cancelSubscription: (() => Promise<boolean>) | undefined
|
|
222
246
|
|
|
223
247
|
const init = async () => {
|
|
224
|
-
cancelSubscription = await subscribeToChanges(identifier, onChange)
|
|
225
|
-
}
|
|
226
|
-
init()
|
|
248
|
+
cancelSubscription = await subscribeToChanges(identifier, onChange)
|
|
249
|
+
}
|
|
250
|
+
void init()
|
|
227
251
|
|
|
228
252
|
return () => {
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
}, [identifier, onChange])
|
|
253
|
+
void cancelSubscription?.()
|
|
254
|
+
}
|
|
255
|
+
}, [identifier, onChange])
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
useSubscribeToChanges(HKCategoryTypeIdentifier.appleStandHour, () => {})
|
|
259
|
+
|
|
260
|
+
function useMostRecentCategorySample<
|
|
261
|
+
TCategory extends HKCategoryTypeIdentifier
|
|
262
|
+
>(identifier: TCategory) {
|
|
263
|
+
const [category, setCategory] = useState<HKCategorySample<TCategory> | null>(
|
|
264
|
+
null,
|
|
265
|
+
)
|
|
266
|
+
const updater = useCallback(() => {
|
|
267
|
+
void getMostRecentCategorySample(identifier).then(setCategory)
|
|
268
|
+
}, [identifier])
|
|
269
|
+
|
|
270
|
+
useSubscribeToChanges(identifier, updater)
|
|
271
|
+
|
|
272
|
+
return category
|
|
232
273
|
}
|
|
233
274
|
|
|
234
275
|
function useMostRecentQuantitySample<
|
|
@@ -236,41 +277,40 @@ function useMostRecentQuantitySample<
|
|
|
236
277
|
TUnit extends HKUnit = HKUnit
|
|
237
278
|
>(identifier: TIdentifier, unit?: TUnit) {
|
|
238
279
|
const [lastSample, setLastSample] = useState<HKQuantitySample<
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
> | null>(null)
|
|
280
|
+
TIdentifier,
|
|
281
|
+
TUnit
|
|
282
|
+
> | null>(null)
|
|
242
283
|
|
|
243
284
|
useEffect(() => {
|
|
244
|
-
let cancelSubscription: (() => Promise<boolean>) | undefined
|
|
285
|
+
let cancelSubscription: (() => Promise<boolean>) | undefined
|
|
245
286
|
|
|
246
287
|
const init = async () => {
|
|
247
|
-
const actualUnit = await ensureUnit(identifier, unit)
|
|
288
|
+
const actualUnit = await ensureUnit(identifier, unit)
|
|
248
289
|
|
|
249
|
-
cancelSubscription = await subscribeToChanges(identifier, () => {
|
|
250
|
-
getMostRecentQuantitySample(identifier, actualUnit)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
init();
|
|
290
|
+
cancelSubscription = await subscribeToChanges(identifier, async () => {
|
|
291
|
+
const value = await getMostRecentQuantitySample(identifier, actualUnit)
|
|
292
|
+
setLastSample(value)
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
void init()
|
|
256
296
|
|
|
257
297
|
return () => {
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
}, [identifier, unit])
|
|
298
|
+
void cancelSubscription?.()
|
|
299
|
+
}
|
|
300
|
+
}, [identifier, unit])
|
|
261
301
|
|
|
262
|
-
return lastSample
|
|
302
|
+
return lastSample
|
|
263
303
|
}
|
|
264
304
|
|
|
265
|
-
const saveQuantitySample: SaveQuantitySampleFn = (
|
|
305
|
+
const saveQuantitySample: SaveQuantitySampleFn = async (
|
|
266
306
|
identifier,
|
|
267
307
|
unit,
|
|
268
308
|
value,
|
|
269
|
-
options
|
|
309
|
+
options,
|
|
270
310
|
) => {
|
|
271
|
-
const start = options?.start || options?.end || new Date()
|
|
272
|
-
const end = options?.end || options?.start || new Date()
|
|
273
|
-
const metadata = options?.metadata || {}
|
|
311
|
+
const start = options?.start || options?.end || new Date()
|
|
312
|
+
const end = options?.end || options?.start || new Date()
|
|
313
|
+
const metadata = options?.metadata || {}
|
|
274
314
|
|
|
275
315
|
return Native.saveQuantitySample(
|
|
276
316
|
identifier,
|
|
@@ -278,131 +318,91 @@ const saveQuantitySample: SaveQuantitySampleFn = (
|
|
|
278
318
|
value,
|
|
279
319
|
start.toISOString(),
|
|
280
320
|
end.toISOString(),
|
|
281
|
-
metadata
|
|
282
|
-
)
|
|
283
|
-
}
|
|
321
|
+
metadata,
|
|
322
|
+
)
|
|
323
|
+
}
|
|
284
324
|
|
|
285
325
|
const queryStatisticsForQuantity: QueryStatisticsForQuantityFn = async (
|
|
286
326
|
identifier,
|
|
287
327
|
options,
|
|
288
328
|
from,
|
|
289
329
|
to,
|
|
290
|
-
unit
|
|
330
|
+
unit,
|
|
291
331
|
) => {
|
|
292
|
-
const actualUnit = await ensureUnit(identifier, unit)
|
|
293
|
-
const toDate = to || new Date()
|
|
294
|
-
const { mostRecentQuantityDateInterval, ...rawResponse } =
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
);
|
|
332
|
+
const actualUnit = await ensureUnit(identifier, unit)
|
|
333
|
+
const toDate = to || new Date()
|
|
334
|
+
const { mostRecentQuantityDateInterval, ...rawResponse } = await Native.queryStatisticsForQuantity(
|
|
335
|
+
identifier,
|
|
336
|
+
actualUnit,
|
|
337
|
+
from.toISOString(),
|
|
338
|
+
toDate.toISOString(),
|
|
339
|
+
options,
|
|
340
|
+
)
|
|
302
341
|
|
|
303
342
|
const response = {
|
|
304
343
|
...rawResponse,
|
|
305
344
|
...(mostRecentQuantityDateInterval
|
|
306
345
|
? {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
346
|
+
mostRecentQuantityDateInterval: {
|
|
347
|
+
from: new Date(mostRecentQuantityDateInterval.from),
|
|
348
|
+
to: new Date(mostRecentQuantityDateInterval.to),
|
|
349
|
+
},
|
|
350
|
+
}
|
|
312
351
|
: {}),
|
|
313
|
-
}
|
|
352
|
+
}
|
|
314
353
|
|
|
315
|
-
return response
|
|
316
|
-
}
|
|
354
|
+
return response
|
|
355
|
+
}
|
|
317
356
|
|
|
318
|
-
const requestAuthorization = (
|
|
319
|
-
read:
|
|
320
|
-
write: HKSampleTypeIdentifier[] = []
|
|
357
|
+
const requestAuthorization = async (
|
|
358
|
+
read: readonly ValueOf<typeof HealthkitAuthorization>[],
|
|
359
|
+
write: readonly ValueOf<typeof HKSampleTypeIdentifier>[] = [],
|
|
321
360
|
): Promise<boolean> => {
|
|
322
|
-
const readPermissions = read.reduce((obj, cur) => {
|
|
323
|
-
return { ...obj, [cur]: true };
|
|
324
|
-
}, {});
|
|
361
|
+
const readPermissions = read.reduce((obj, cur) => ({ ...obj, [cur]: true }), {} as ReadPermissions)
|
|
325
362
|
|
|
326
|
-
const writePermissions = write.reduce((obj, cur) => {
|
|
327
|
-
return { ...obj, [cur]: true };
|
|
328
|
-
}, {});
|
|
363
|
+
const writePermissions = write.reduce((obj, cur) => ({ ...obj, [cur]: true }), {} as WritePermissions)
|
|
329
364
|
|
|
330
|
-
return Native.requestAuthorization(writePermissions, readPermissions)
|
|
331
|
-
}
|
|
365
|
+
return Native.requestAuthorization(writePermissions, readPermissions)
|
|
366
|
+
}
|
|
332
367
|
|
|
333
368
|
const getDateOfBirth = async () => {
|
|
334
|
-
const dateOfBirth = await Native.getDateOfBirth()
|
|
335
|
-
return new Date(dateOfBirth)
|
|
336
|
-
}
|
|
369
|
+
const dateOfBirth = await Native.getDateOfBirth()
|
|
370
|
+
return new Date(dateOfBirth)
|
|
371
|
+
}
|
|
337
372
|
|
|
338
|
-
const getRequestStatusForAuthorization = (
|
|
339
|
-
read: (HKCharacteristicTypeIdentifier | HKSampleTypeIdentifier)[],
|
|
340
|
-
write: HKSampleTypeIdentifier[] = []
|
|
373
|
+
const getRequestStatusForAuthorization = async (
|
|
374
|
+
read: readonly (HKCharacteristicTypeIdentifier | ValueOf<typeof HKSampleTypeIdentifier>)[],
|
|
375
|
+
write: readonly (ValueOf<typeof HKSampleTypeIdentifier>)[] = [],
|
|
341
376
|
) => {
|
|
342
|
-
const readPermissions = read.reduce((obj, cur) => {
|
|
343
|
-
return { ...obj, [cur]: true };
|
|
344
|
-
}, {});
|
|
377
|
+
const readPermissions = read.reduce((obj, cur) => ({ ...obj, [cur]: true }), {} as ReadPermissions)
|
|
345
378
|
|
|
346
|
-
const writePermissions = write.reduce((obj, cur) => {
|
|
347
|
-
return { ...obj, [cur]: true };
|
|
348
|
-
}, {});
|
|
379
|
+
const writePermissions = write.reduce((obj, cur) => ({ ...obj, [cur]: true }), {} as WritePermissions)
|
|
349
380
|
|
|
350
381
|
return Native.getRequestStatusForAuthorization(
|
|
351
382
|
writePermissions,
|
|
352
|
-
readPermissions
|
|
353
|
-
)
|
|
354
|
-
}
|
|
383
|
+
readPermissions,
|
|
384
|
+
)
|
|
385
|
+
}
|
|
355
386
|
|
|
356
387
|
const queryCategorySamples: QueryCategorySamplesFn = async (
|
|
357
388
|
identifier,
|
|
358
|
-
options
|
|
389
|
+
options,
|
|
359
390
|
) => {
|
|
360
|
-
const opts = prepareOptions(options)
|
|
391
|
+
const opts = prepareOptions(options)
|
|
361
392
|
const results = await Native.queryCategorySamples(
|
|
362
393
|
identifier,
|
|
363
394
|
opts.from,
|
|
364
395
|
opts.to,
|
|
365
396
|
opts.limit,
|
|
366
|
-
opts.ascending
|
|
367
|
-
)
|
|
397
|
+
opts.ascending,
|
|
398
|
+
)
|
|
368
399
|
|
|
369
|
-
return results.map(deserializCategorySample)
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
async function getPreferredUnitsTyped<
|
|
373
|
-
TEnergy extends HKUnit,
|
|
374
|
-
TDistance extends HKUnit
|
|
375
|
-
>(options?: { energyUnit?: TEnergy; distanceUnit?: TDistance }) {
|
|
376
|
-
let energyUnit = options?.energyUnit;
|
|
377
|
-
let distanceUnit = options?.distanceUnit;
|
|
378
|
-
if (!energyUnit || !distanceUnit) {
|
|
379
|
-
const units = await Native.getPreferredUnits([
|
|
380
|
-
HKQuantityTypeIdentifier.distanceWalkingRunning,
|
|
381
|
-
HKQuantityTypeIdentifier.activeEnergyBurned,
|
|
382
|
-
]);
|
|
383
|
-
if (!energyUnit) {
|
|
384
|
-
energyUnit = units[HKQuantityTypeIdentifier.distanceWalkingRunning] as
|
|
385
|
-
| TEnergy
|
|
386
|
-
| undefined;
|
|
387
|
-
}
|
|
388
|
-
if (!distanceUnit) {
|
|
389
|
-
distanceUnit = units[HKQuantityTypeIdentifier.activeEnergyBurned] as
|
|
390
|
-
| TDistance
|
|
391
|
-
| undefined;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
if (!energyUnit) {
|
|
395
|
-
energyUnit = HKUnit.Kilocalories as TEnergy;
|
|
396
|
-
}
|
|
397
|
-
if (!distanceUnit) {
|
|
398
|
-
distanceUnit = HKUnit.Meters as TDistance;
|
|
399
|
-
}
|
|
400
|
-
return { energyUnit, distanceUnit };
|
|
400
|
+
return results.map(deserializCategorySample)
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
const queryWorkouts: QueryWorkoutsFn = async (options) => {
|
|
404
|
-
const { energyUnit, distanceUnit } = await getPreferredUnitsTyped(options)
|
|
405
|
-
const opts = prepareOptions(options)
|
|
404
|
+
const { energyUnit, distanceUnit } = await getPreferredUnitsTyped(options)
|
|
405
|
+
const opts = prepareOptions(options)
|
|
406
406
|
|
|
407
407
|
const workouts = await Native.queryWorkoutSamples(
|
|
408
408
|
energyUnit,
|
|
@@ -410,11 +410,11 @@ const queryWorkouts: QueryWorkoutsFn = async (options) => {
|
|
|
410
410
|
opts.from,
|
|
411
411
|
opts.to,
|
|
412
412
|
opts.limit,
|
|
413
|
-
opts.ascending
|
|
414
|
-
)
|
|
413
|
+
opts.ascending,
|
|
414
|
+
)
|
|
415
415
|
|
|
416
|
-
return workouts.map(deserializeWorkout)
|
|
417
|
-
}
|
|
416
|
+
return workouts.map(deserializeWorkout)
|
|
417
|
+
}
|
|
418
418
|
|
|
419
419
|
const getMostRecentWorkout: GetMostRecentWorkoutFn = async (options) => {
|
|
420
420
|
const workouts = await queryWorkouts({
|
|
@@ -422,41 +422,39 @@ const getMostRecentWorkout: GetMostRecentWorkoutFn = async (options) => {
|
|
|
422
422
|
ascending: false,
|
|
423
423
|
energyUnit: options?.energyUnit,
|
|
424
424
|
distanceUnit: options?.distanceUnit,
|
|
425
|
-
})
|
|
425
|
+
})
|
|
426
426
|
|
|
427
|
-
return workouts[0]
|
|
428
|
-
}
|
|
427
|
+
return workouts[0]
|
|
428
|
+
}
|
|
429
429
|
|
|
430
|
-
function saveCategorySample<T extends HKCategoryTypeIdentifier>(
|
|
430
|
+
async function saveCategorySample<T extends HKCategoryTypeIdentifier>(
|
|
431
431
|
identifier: T,
|
|
432
432
|
value: HKCategoryValueForIdentifier<T>,
|
|
433
433
|
options?: {
|
|
434
|
-
start?: Date;
|
|
435
|
-
end?: Date;
|
|
436
|
-
metadata?: MetadataMapperForCategoryIdentifier<T>;
|
|
437
|
-
}
|
|
434
|
+
readonly start?: Date;
|
|
435
|
+
readonly end?: Date;
|
|
436
|
+
readonly metadata?: MetadataMapperForCategoryIdentifier<T>;
|
|
437
|
+
},
|
|
438
438
|
) {
|
|
439
|
-
const start = options?.start || options?.end || new Date()
|
|
440
|
-
const end = options?.end || options?.start || new Date()
|
|
441
|
-
const metadata = options?.metadata || {}
|
|
439
|
+
const start = options?.start || options?.end || new Date()
|
|
440
|
+
const end = options?.end || options?.start || new Date()
|
|
441
|
+
const metadata = options?.metadata || {}
|
|
442
442
|
|
|
443
443
|
return Native.saveCategorySample(
|
|
444
444
|
identifier,
|
|
445
445
|
value,
|
|
446
446
|
start.toISOString(),
|
|
447
447
|
end.toISOString(),
|
|
448
|
-
metadata || {}
|
|
449
|
-
)
|
|
448
|
+
metadata || {},
|
|
449
|
+
)
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
const getPreferredUnits: GetPreferredUnitsFn = async (identifiers) => {
|
|
453
|
-
const units = await Native.getPreferredUnits(identifiers)
|
|
454
|
-
return identifiers.map((i) => units[i])
|
|
455
|
-
}
|
|
453
|
+
const units = await Native.getPreferredUnits(identifiers)
|
|
454
|
+
return identifiers.map((i) => units[i])
|
|
455
|
+
}
|
|
456
456
|
|
|
457
|
-
const buildUnitWithPrefix = (prefix: HKUnitSIPrefix, unit: HKUnitSI) => {
|
|
458
|
-
return `${prefix}${unit}` as HKUnit;
|
|
459
|
-
};
|
|
457
|
+
const buildUnitWithPrefix = (prefix: HKUnitSIPrefix, unit: HKUnitSI) => `${prefix}${unit}` as HKUnit
|
|
460
458
|
|
|
461
459
|
function deserializeCorrelation<
|
|
462
460
|
TIdentifier extends HKCorrelationTypeIdentifier
|
|
@@ -464,75 +462,78 @@ function deserializeCorrelation<
|
|
|
464
462
|
return {
|
|
465
463
|
...s,
|
|
466
464
|
objects: s.objects.map((o) => {
|
|
465
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
467
466
|
// @ts-ignore
|
|
468
467
|
if (o.quantity !== undefined) {
|
|
469
|
-
return deserializeSample(o as HKQuantitySampleRaw)
|
|
468
|
+
return deserializeSample(o as HKQuantitySampleRaw)
|
|
470
469
|
}
|
|
471
470
|
|
|
472
|
-
return deserializCategorySample(o as HKCategorySampleRaw)
|
|
471
|
+
return deserializCategorySample(o as HKCategorySampleRaw)
|
|
473
472
|
}),
|
|
474
473
|
endDate: new Date(s.endDate),
|
|
475
474
|
startDate: new Date(s.startDate),
|
|
476
|
-
}
|
|
475
|
+
}
|
|
477
476
|
}
|
|
478
477
|
|
|
479
478
|
function ensureMetadata<TMetadata>(metadata?: TMetadata) {
|
|
480
|
-
return metadata || ({} as TMetadata)
|
|
479
|
+
return metadata || ({} as TMetadata)
|
|
481
480
|
}
|
|
482
481
|
|
|
483
482
|
const queryCorrelationSamples: QueryCorrelationSamplesFn = async (
|
|
484
483
|
typeIdentifier,
|
|
485
|
-
options
|
|
484
|
+
options,
|
|
486
485
|
) => {
|
|
487
|
-
const opts = prepareOptions(options)
|
|
486
|
+
const opts = prepareOptions(options)
|
|
488
487
|
const correlations = await Native.queryCorrelationSamples(
|
|
489
488
|
typeIdentifier,
|
|
490
489
|
opts.from,
|
|
491
|
-
opts.to
|
|
492
|
-
)
|
|
490
|
+
opts.to,
|
|
491
|
+
)
|
|
493
492
|
|
|
494
|
-
return correlations.map(deserializeCorrelation)
|
|
495
|
-
}
|
|
493
|
+
return correlations.map(deserializeCorrelation)
|
|
494
|
+
}
|
|
496
495
|
|
|
497
496
|
const saveCorrelationSample: SaveCorrelationSampleFn = async (
|
|
498
497
|
typeIdentifier,
|
|
499
498
|
samples,
|
|
500
|
-
options
|
|
499
|
+
options,
|
|
501
500
|
) => {
|
|
502
|
-
const start = (options?.start || new Date()).toISOString()
|
|
503
|
-
const end = (options?.end || new Date()).toISOString()
|
|
501
|
+
const start = (options?.start || new Date()).toISOString()
|
|
502
|
+
const end = (options?.end || new Date()).toISOString()
|
|
504
503
|
|
|
505
504
|
return Native.saveCorrelationSample(
|
|
506
505
|
typeIdentifier,
|
|
507
506
|
samples,
|
|
508
507
|
start,
|
|
509
508
|
end,
|
|
510
|
-
ensureMetadata(options?.metadata)
|
|
511
|
-
)
|
|
512
|
-
}
|
|
509
|
+
ensureMetadata(options?.metadata),
|
|
510
|
+
)
|
|
511
|
+
}
|
|
513
512
|
|
|
514
|
-
const saveWorkoutSample: SaveWorkoutSampleFn = (
|
|
513
|
+
const saveWorkoutSample: SaveWorkoutSampleFn = async (
|
|
515
514
|
typeIdentifier,
|
|
516
515
|
quantities,
|
|
517
516
|
_start,
|
|
518
|
-
options
|
|
517
|
+
options,
|
|
519
518
|
) => {
|
|
520
|
-
const start = _start.toISOString()
|
|
521
|
-
const end = (options?.end || new Date()).toISOString()
|
|
519
|
+
const start = _start.toISOString()
|
|
520
|
+
const end = (options?.end || new Date()).toISOString()
|
|
522
521
|
|
|
523
522
|
return Native.saveWorkoutSample(
|
|
524
523
|
typeIdentifier,
|
|
525
524
|
quantities,
|
|
526
525
|
start,
|
|
527
526
|
end,
|
|
528
|
-
ensureMetadata(options?.metadata)
|
|
529
|
-
)
|
|
530
|
-
}
|
|
527
|
+
ensureMetadata(options?.metadata),
|
|
528
|
+
)
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const getWorkoutRoutes: GetWorkoutRoutesFn = async (workoutUUID: string) => Native.getWorkoutRoutes(workoutUUID)
|
|
531
532
|
|
|
532
533
|
const Healthkit: ReactNativeHealthkit = {
|
|
533
|
-
authorizationStatusFor: Native.authorizationStatusFor,
|
|
534
|
+
authorizationStatusFor: Native.authorizationStatusFor.bind(Native),
|
|
534
535
|
|
|
535
|
-
isHealthDataAvailable: Native.isHealthDataAvailable,
|
|
536
|
+
isHealthDataAvailable: Native.isHealthDataAvailable.bind(Native),
|
|
536
537
|
|
|
537
538
|
buildUnitWithPrefix,
|
|
538
539
|
|
|
@@ -541,10 +542,10 @@ const Healthkit: ReactNativeHealthkit = {
|
|
|
541
542
|
enableBackgroundDelivery: Native.enableBackgroundDelivery,
|
|
542
543
|
|
|
543
544
|
// simple convenience getters
|
|
544
|
-
getBiologicalSex: Native.getBiologicalSex,
|
|
545
|
-
getFitzpatrickSkinType: Native.getFitzpatrickSkinType,
|
|
545
|
+
getBiologicalSex: Native.getBiologicalSex.bind(Native),
|
|
546
|
+
getFitzpatrickSkinType: Native.getFitzpatrickSkinType.bind(Native),
|
|
546
547
|
getWheelchairUse: Native.getWheelchairUse,
|
|
547
|
-
getBloodType: Native.getBloodType,
|
|
548
|
+
getBloodType: Native.getBloodType.bind(Native),
|
|
548
549
|
getDateOfBirth,
|
|
549
550
|
|
|
550
551
|
getMostRecentQuantitySample,
|
|
@@ -555,6 +556,8 @@ const Healthkit: ReactNativeHealthkit = {
|
|
|
555
556
|
getPreferredUnits,
|
|
556
557
|
getRequestStatusForAuthorization,
|
|
557
558
|
|
|
559
|
+
getWorkoutRoutes,
|
|
560
|
+
|
|
558
561
|
// query methods
|
|
559
562
|
queryCategorySamples,
|
|
560
563
|
queryCorrelationSamples,
|
|
@@ -580,9 +583,40 @@ const Healthkit: ReactNativeHealthkit = {
|
|
|
580
583
|
useMostRecentWorkout,
|
|
581
584
|
|
|
582
585
|
useSubscribeToChanges,
|
|
583
|
-
};
|
|
584
586
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
+
useIsHealthDataAvailable: () => {
|
|
588
|
+
const [isAvailable, setIsAvailable] = useState<boolean | null>(null)
|
|
589
|
+
useEffect(() => {
|
|
590
|
+
const init = async () => {
|
|
591
|
+
const res = await Native.isHealthDataAvailable()
|
|
592
|
+
setIsAvailable(res)
|
|
593
|
+
}
|
|
594
|
+
void init()
|
|
595
|
+
}, [])
|
|
596
|
+
return isAvailable
|
|
597
|
+
},
|
|
598
|
+
useHealthkitAuthorization: (read, write) => {
|
|
599
|
+
const [status, setStatus] = useState<HKAuthorizationRequestStatus | null>(null)
|
|
600
|
+
const refreshAuthStatus = useCallback(async () => {
|
|
601
|
+
const auth = await getRequestStatusForAuthorization(read, write)
|
|
602
|
+
setStatus(auth)
|
|
603
|
+
return auth
|
|
604
|
+
}, [])
|
|
605
|
+
|
|
606
|
+
const request = useCallback(async () => {
|
|
607
|
+
await requestAuthorization(read, write)
|
|
608
|
+
return refreshAuthStatus()
|
|
609
|
+
}, [])
|
|
610
|
+
|
|
611
|
+
useEffect(() => {
|
|
612
|
+
void refreshAuthStatus()
|
|
613
|
+
}, [])
|
|
614
|
+
|
|
615
|
+
return [status, request]
|
|
616
|
+
},
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export * from './native-types'
|
|
620
|
+
export * from './types'
|
|
587
621
|
|
|
588
|
-
export default Healthkit
|
|
622
|
+
export default Healthkit
|