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