@kingstinct/react-native-healthkit 4.0.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/{ios/.DS_Store → .DS_Store} +0 -0
  2. package/.circleci/config.yml +98 -0
  3. package/.editorconfig +15 -0
  4. package/.gitattributes +3 -0
  5. package/.github/workflows/main.yml +32 -0
  6. package/.gitignore +60 -0
  7. package/CONTRIBUTING.md +184 -0
  8. package/README.md +1 -1
  9. package/babel.config.js +3 -0
  10. package/example-expo/.expo/README.md +15 -0
  11. package/example-expo/.expo/devices.json +8 -0
  12. package/example-expo/.expo/packager-info.json +5 -0
  13. package/example-expo/.expo/settings.json +9 -0
  14. package/example-expo/.expo-shared/assets.json +4 -0
  15. package/example-expo/.gitignore +14 -0
  16. package/example-expo/App.tsx +376 -0
  17. package/example-expo/app.json +43 -0
  18. package/example-expo/assets/adaptive-icon.png +0 -0
  19. package/example-expo/assets/favicon.png +0 -0
  20. package/example-expo/assets/icon.png +0 -0
  21. package/example-expo/assets/splash.png +0 -0
  22. package/example-expo/babel.config.js +8 -0
  23. package/example-expo/build-1653040579600.ipa +0 -0
  24. package/example-expo/build-1653041063216.ipa +0 -0
  25. package/example-expo/eas.json +18 -0
  26. package/example-expo/package.json +32 -0
  27. package/example-expo/tsconfig.json +6 -0
  28. package/example-expo/yarn.lock +6857 -0
  29. package/ios/ReactNativeHealthkit.m +9 -9
  30. package/ios/ReactNativeHealthkit.swift +38 -4
  31. package/lib/commonjs/index.ios.js +9 -5
  32. package/lib/commonjs/index.ios.js.map +1 -1
  33. package/lib/commonjs/native-types.js +8 -1
  34. package/lib/commonjs/native-types.js.map +1 -1
  35. package/lib/module/index.ios.js +9 -5
  36. package/lib/module/index.ios.js.map +1 -1
  37. package/lib/module/native-types.js +7 -0
  38. package/lib/module/native-types.js.map +1 -1
  39. package/lib/typescript/example-expo/App.d.ts +3 -0
  40. package/lib/typescript/src/__tests__/index.test.d.ts +0 -0
  41. package/lib/typescript/src/native-types.d.ts +20 -0
  42. package/package.json +11 -10
  43. package/src/__tests__/index.test.tsx +1 -0
  44. package/src/index.ios.tsx +8 -10
  45. package/src/native-types.ts +54 -31
  46. package/tsconfig.json +27 -0
  47. package/yarn.lock +10241 -0
  48. package/ios/ReactNativeHealthkit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -4
  49. package/ios/ReactNativeHealthkit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  50. package/ios/ReactNativeHealthkit.xcodeproj/project.xcworkspace/xcuserdata/robertherber.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  51. package/ios/ReactNativeHealthkit.xcodeproj/xcuserdata/robertherber.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
@@ -0,0 +1,376 @@
1
+ /* eslint-disable react-native/no-inline-styles */
2
+ import 'expo-dev-client';
3
+ import * as React from 'react';
4
+ import { Button, ScrollView, Text } from 'react-native';
5
+ import { DataTable } from 'react-native-paper';
6
+ import dayjs from 'dayjs';
7
+ import Healthkit, {
8
+ HKCategorySample,
9
+ HKCategoryTypeIdentifier,
10
+ HKCharacteristicTypeIdentifier,
11
+ HKCorrelationTypeIdentifier,
12
+ HKInsulinDeliveryReason,
13
+ HKQuantity,
14
+ HKQuantitySample,
15
+ HKQuantityTypeIdentifier,
16
+ HKStatisticsOptions,
17
+ HKUnit,
18
+ HKWeatherCondition,
19
+ HKWorkout,
20
+ HKWorkoutActivityType,
21
+ QueryStatisticsResponse,
22
+ } from '../src/index';
23
+
24
+ const DisplayWorkout: React.FunctionComponent<{
25
+ workout: HKWorkout;
26
+ }> = ({ workout }) => {
27
+ return (
28
+ <DataTable.Row>
29
+ <DataTable.Cell>
30
+ {HKWorkoutActivityType[workout.workoutActivityType]}
31
+ </DataTable.Cell>
32
+ <DataTable.Cell style={{ paddingRight: 10 }} numeric>
33
+ {workout ? workout.duration.toFixed(0) + 's' : '-'}
34
+ </DataTable.Cell>
35
+ <DataTable.Cell>
36
+ {workout
37
+ ? workout.totalDistance?.quantity.toFixed(1) +
38
+ ' ' +
39
+ workout.totalDistance?.unit
40
+ : '-'}
41
+ </DataTable.Cell>
42
+ <DataTable.Cell>
43
+ {workout
44
+ ? workout.totalEnergyBurned?.quantity.toFixed(1) +
45
+ ' ' +
46
+ workout.totalEnergyBurned?.unit
47
+ : '-'}
48
+ </DataTable.Cell>
49
+ </DataTable.Row>
50
+ );
51
+ };
52
+
53
+ const DisplayQuantitySample: React.FunctionComponent<{
54
+ title: string;
55
+ sample: HKQuantitySample | null;
56
+ }> = ({ title, sample }) => {
57
+ return (
58
+ <DataTable.Row>
59
+ <DataTable.Cell>{title}</DataTable.Cell>
60
+ <DataTable.Cell style={{ paddingRight: 10 }} numeric>
61
+ {sample ? sample.quantity.toFixed(1) : '-'}
62
+ </DataTable.Cell>
63
+ <DataTable.Cell>{sample ? sample.unit : '-'}</DataTable.Cell>
64
+ <DataTable.Cell>
65
+ {sample ? sample.startDate.toLocaleTimeString() : '-'}
66
+ </DataTable.Cell>
67
+ </DataTable.Row>
68
+ );
69
+ };
70
+
71
+ const DisplayCategorySample: React.FunctionComponent<{
72
+ title: string;
73
+ sample: HKCategorySample | null;
74
+ }> = ({ title, sample }) => {
75
+ return (
76
+ <DataTable.Row>
77
+ <DataTable.Cell>{title}</DataTable.Cell>
78
+ <DataTable.Cell style={{ paddingRight: 10 }} numeric>
79
+ {sample ? sample.value : '-'}
80
+ </DataTable.Cell>
81
+ <DataTable.Cell>
82
+ {sample ? sample.startDate.toLocaleTimeString() : '-'}
83
+ </DataTable.Cell>
84
+ <DataTable.Cell>
85
+ {sample ? sample.endDate.toLocaleTimeString() : '-'}
86
+ </DataTable.Cell>
87
+ </DataTable.Row>
88
+ );
89
+ };
90
+
91
+ const DisplayStat: React.FunctionComponent<{
92
+ title: string;
93
+ sample: HKQuantity | undefined;
94
+ }> = ({ title, sample }) => {
95
+ return (
96
+ <DataTable.Row>
97
+ <DataTable.Cell>{title}</DataTable.Cell>
98
+ <DataTable.Cell style={{ paddingRight: 10 }} numeric>
99
+ {sample ? sample.quantity.toFixed(1) : '-'}
100
+ </DataTable.Cell>
101
+ <DataTable.Cell>{sample ? sample.unit : '-'}</DataTable.Cell>
102
+ <DataTable.Cell>N/A</DataTable.Cell>
103
+ </DataTable.Row>
104
+ );
105
+ };
106
+
107
+ function DataView() {
108
+ const [dateOfBirth, setDateOfBirth] = React.useState<Date | null>(null);
109
+
110
+ const [bloodGlucoseSamples, setBloodGlucoseSamples] =
111
+ React.useState<Array<HKQuantitySample> | null>(null);
112
+
113
+ const bodyFat = Healthkit.useMostRecentQuantitySample(
114
+ HKQuantityTypeIdentifier.bodyFatPercentage
115
+ );
116
+
117
+ const bloodGlucose = Healthkit.useMostRecentQuantitySample(
118
+ HKQuantityTypeIdentifier.bloodGlucose
119
+ );
120
+
121
+ const bodyWeight = Healthkit.useMostRecentQuantitySample(
122
+ HKQuantityTypeIdentifier.bodyMass
123
+ );
124
+ const heartRate = Healthkit.useMostRecentQuantitySample(
125
+ HKQuantityTypeIdentifier.heartRate
126
+ );
127
+ const lastWorkout = Healthkit.useMostRecentWorkout();
128
+ const lastMindfulSession = Healthkit.useMostRecentCategorySample(
129
+ HKCategoryTypeIdentifier.mindfulSession
130
+ );
131
+
132
+ const walkingSpeed = Healthkit.useMostRecentQuantitySample(
133
+ HKQuantityTypeIdentifier.walkingSpeed
134
+ );
135
+ const sixMinWalk = Healthkit.useMostRecentQuantitySample(
136
+ HKQuantityTypeIdentifier.sixMinuteWalkTestDistance
137
+ );
138
+ const walkingStepLength = Healthkit.useMostRecentQuantitySample(
139
+ HKQuantityTypeIdentifier.walkingStepLength
140
+ );
141
+ const walkingAsymmetryPercentage = Healthkit.useMostRecentQuantitySample(
142
+ HKQuantityTypeIdentifier.walkingAsymmetryPercentage
143
+ );
144
+ const walkingDoubleSupportPercentage = Healthkit.useMostRecentQuantitySample(
145
+ HKQuantityTypeIdentifier.walkingDoubleSupportPercentage
146
+ );
147
+
148
+ const stairAscentSpeed = Healthkit.useMostRecentQuantitySample(
149
+ HKQuantityTypeIdentifier.stairAscentSpeed
150
+ );
151
+
152
+ const stairDescentSpeed = Healthkit.useMostRecentQuantitySample(
153
+ HKQuantityTypeIdentifier.stairDescentSpeed
154
+ );
155
+
156
+ const [queryStatisticsResponse, setQueryStatisticsResponse] =
157
+ React.useState<QueryStatisticsResponse | null>(null);
158
+
159
+ const writeSampleToHealthkit = () => {
160
+ Healthkit.saveQuantitySample(
161
+ HKQuantityTypeIdentifier.insulinDelivery,
162
+ HKUnit.InternationalUnit,
163
+ 4.2,
164
+ {
165
+ metadata: {
166
+ HKInsulinDeliveryReason: HKInsulinDeliveryReason.basal,
167
+ },
168
+ }
169
+ );
170
+ Healthkit.saveCorrelationSample(HKCorrelationTypeIdentifier.food, [
171
+ {
172
+ quantityType: HKQuantityTypeIdentifier.dietaryCaffeine,
173
+ unit: HKUnit.Grams,
174
+ quantity: 1,
175
+ metadata: {},
176
+ },
177
+ {
178
+ quantityType: HKQuantityTypeIdentifier.dietaryEnergyConsumed,
179
+ unit: HKUnit.Kilocalories,
180
+ quantity: 1,
181
+ metadata: {},
182
+ },
183
+ ]);
184
+
185
+ Healthkit.saveWorkoutSample(
186
+ HKWorkoutActivityType.archery,
187
+ [
188
+ {
189
+ quantityType: HKQuantityTypeIdentifier.activeEnergyBurned,
190
+ unit: HKUnit.Kilocalories,
191
+ quantity: 63,
192
+ metadata: {},
193
+ },
194
+ {
195
+ quantityType: HKQuantityTypeIdentifier.appleExerciseTime,
196
+ unit: HKUnit.Minutes,
197
+ quantity: 11,
198
+ metadata: {},
199
+ },
200
+ ],
201
+ new Date(),
202
+ {
203
+ metadata: {
204
+ HKWeatherCondition: HKWeatherCondition.hurricane,
205
+ },
206
+ }
207
+ );
208
+
209
+ Healthkit.getDateOfBirth().then(setDateOfBirth);
210
+
211
+ Healthkit.queryStatisticsForQuantity(
212
+ HKQuantityTypeIdentifier.heartRate,
213
+ [
214
+ HKStatisticsOptions.discreteAverage,
215
+ HKStatisticsOptions.discreteMax,
216
+ HKStatisticsOptions.discreteMin,
217
+ ],
218
+ dayjs().startOf('day').toDate()
219
+ ).then(setQueryStatisticsResponse);
220
+
221
+ Healthkit.queryQuantitySamples(HKQuantityTypeIdentifier.bloodGlucose, {
222
+ ascending: true,
223
+ from: dayjs().startOf('day').toDate(),
224
+ to: new Date(),
225
+ }).then(setBloodGlucoseSamples);
226
+ };
227
+
228
+ console.log(walkingDoubleSupportPercentage);
229
+
230
+ return (
231
+ <ScrollView style={{ flex: 1, paddingTop: 40 }}>
232
+ <Button
233
+ title="Write Sample to HealthKit"
234
+ onPress={() => writeSampleToHealthkit()}
235
+ />
236
+ <Text>Date of birth: {dateOfBirth?.toLocaleDateString()}</Text>
237
+ <DataTable>
238
+ <DataTable.Header>
239
+ <DataTable.Title>Metric</DataTable.Title>
240
+ <DataTable.Title style={{ paddingRight: 10 }} numeric>
241
+ Value
242
+ </DataTable.Title>
243
+ <DataTable.Title>Unit</DataTable.Title>
244
+ <DataTable.Title>Time</DataTable.Title>
245
+ </DataTable.Header>
246
+
247
+ <DisplayQuantitySample sample={bodyFat} title="Body fat" />
248
+ <DisplayQuantitySample sample={bodyWeight} title="Weight" />
249
+ <DisplayQuantitySample sample={heartRate} title="Heart rate" />
250
+ <DisplayQuantitySample sample={bloodGlucose} title="Glucose" />
251
+
252
+ <DisplayStat
253
+ sample={queryStatisticsResponse?.averageQuantity}
254
+ title="Avg. HR"
255
+ />
256
+ <DisplayStat
257
+ sample={queryStatisticsResponse?.maximumQuantity}
258
+ title="High HR"
259
+ />
260
+ <DisplayStat
261
+ sample={queryStatisticsResponse?.minimumQuantity}
262
+ title="Low HR"
263
+ />
264
+
265
+ <DisplayCategorySample sample={lastMindfulSession} title="Mindful" />
266
+
267
+ <DataTable.Header>
268
+ <DataTable.Title>Workout</DataTable.Title>
269
+ <DataTable.Title style={{ paddingRight: 10 }} numeric>
270
+ Duration
271
+ </DataTable.Title>
272
+ <DataTable.Title>Distance</DataTable.Title>
273
+ <DataTable.Title>Energy</DataTable.Title>
274
+ </DataTable.Header>
275
+ {lastWorkout ? <DisplayWorkout workout={lastWorkout} /> : null}
276
+
277
+ <DataTable.Header>
278
+ <DataTable.Title>Blood Glucose</DataTable.Title>
279
+ <DataTable.Title style={{ paddingRight: 10 }} numeric>
280
+ Value
281
+ </DataTable.Title>
282
+ <DataTable.Title>Units</DataTable.Title>
283
+ <DataTable.Title>Time</DataTable.Title>
284
+ </DataTable.Header>
285
+ {bloodGlucoseSamples
286
+ ? bloodGlucoseSamples.map((sample: HKQuantitySample) => (
287
+ <DisplayQuantitySample sample={sample} title="Glucose" />
288
+ ))
289
+ : null}
290
+
291
+ <DataTable.Header>
292
+ <DataTable.Title>Mobility</DataTable.Title>
293
+ <DataTable.Title style={{ paddingRight: 10 }} numeric>
294
+ Value
295
+ </DataTable.Title>
296
+ <DataTable.Title>Units</DataTable.Title>
297
+ <DataTable.Title>Time</DataTable.Title>
298
+ </DataTable.Header>
299
+ <DisplayQuantitySample sample={walkingSpeed} title="Walking speed" />
300
+ <DisplayQuantitySample
301
+ sample={sixMinWalk}
302
+ title="Six-minute walk test"
303
+ />
304
+ <DisplayQuantitySample
305
+ sample={walkingStepLength}
306
+ title="Walking Step Length"
307
+ />
308
+ <DisplayQuantitySample
309
+ sample={walkingAsymmetryPercentage}
310
+ title="Walking Asymmetry"
311
+ />
312
+ <DisplayQuantitySample
313
+ sample={walkingDoubleSupportPercentage}
314
+ title="Walking Double Support"
315
+ />
316
+ <DisplayQuantitySample sample={stairAscentSpeed} title="Stair Ascent" />
317
+ <DisplayQuantitySample
318
+ sample={stairDescentSpeed}
319
+ title="Stair Descent"
320
+ />
321
+ </DataTable>
322
+ </ScrollView>
323
+ );
324
+ }
325
+
326
+ const App = () => {
327
+ const [hasPermissions, setHasPermissions] = React.useState<boolean>(false);
328
+ React.useEffect(() => {
329
+ Healthkit.requestAuthorization(
330
+ [
331
+ HKCharacteristicTypeIdentifier.biologicalSex,
332
+ HKCharacteristicTypeIdentifier.bloodType,
333
+ HKCharacteristicTypeIdentifier.dateOfBirth,
334
+ HKCharacteristicTypeIdentifier.fitzpatrickSkinType,
335
+ HKQuantityTypeIdentifier.waistCircumference,
336
+ HKQuantityTypeIdentifier.bodyMassIndex,
337
+ HKQuantityTypeIdentifier.bodyMass,
338
+ HKQuantityTypeIdentifier.heartRate,
339
+ HKQuantityTypeIdentifier.bloodGlucose,
340
+ HKQuantityTypeIdentifier.insulinDelivery,
341
+ HKQuantityTypeIdentifier.activeEnergyBurned,
342
+ HKCategoryTypeIdentifier.mindfulSession,
343
+ HKQuantityTypeIdentifier.dietaryCaffeine,
344
+ HKQuantityTypeIdentifier.dietaryEnergyConsumed,
345
+ HKQuantityTypeIdentifier.walkingSpeed,
346
+ HKQuantityTypeIdentifier.walkingAsymmetryPercentage,
347
+ HKQuantityTypeIdentifier.walkingDoubleSupportPercentage,
348
+ HKQuantityTypeIdentifier.stairAscentSpeed,
349
+ HKQuantityTypeIdentifier.stairDescentSpeed,
350
+ HKQuantityTypeIdentifier.walkingStepLength,
351
+ 'HKWorkoutTypeIdentifier',
352
+ ],
353
+ [
354
+ HKQuantityTypeIdentifier.waistCircumference,
355
+ HKQuantityTypeIdentifier.activeEnergyBurned,
356
+ HKQuantityTypeIdentifier.bloodGlucose,
357
+ HKQuantityTypeIdentifier.insulinDelivery,
358
+ HKQuantityTypeIdentifier.bodyFatPercentage,
359
+ HKCategoryTypeIdentifier.mindfulSession,
360
+ HKQuantityTypeIdentifier.dietaryCaffeine,
361
+ HKQuantityTypeIdentifier.dietaryEnergyConsumed,
362
+ 'HKWorkoutTypeIdentifier',
363
+ ]
364
+ ).then(setHasPermissions);
365
+ }, []);
366
+
367
+ return hasPermissions ? (
368
+ <DataView />
369
+ ) : (
370
+ <Text style={{ paddingTop: 40, textAlign: 'center' }}>
371
+ Waiting for user to authorize..
372
+ </Text>
373
+ );
374
+ };
375
+
376
+ export default App;
@@ -0,0 +1,43 @@
1
+ {
2
+ "expo": {
3
+ "name": "healthkit-example-expo",
4
+ "slug": "healthkit-example-expo",
5
+ "owner": "kingstinct",
6
+ "version": "1.0.0",
7
+ "orientation": "portrait",
8
+ "icon": "./assets/icon.png",
9
+ "userInterfaceStyle": "light",
10
+ "splash": {
11
+ "image": "./assets/splash.png",
12
+ "resizeMode": "contain",
13
+ "backgroundColor": "#ffffff"
14
+ },
15
+ "updates": {
16
+ "fallbackToCacheTimeout": 0
17
+ },
18
+ "assetBundlePatterns": [
19
+ "**/*"
20
+ ],
21
+ "ios": {
22
+ "supportsTablet": true,
23
+ "bundleIdentifier": "com.kingstinct.healthkitexample.expo",
24
+ "infoPlist": {
25
+ "NSHealthShareUsageDescription": "This app uses HealthKit to store and sync your data",
26
+ "NSHealthUpdateUsageDescription": "This app uses HealthKit to store and sync your data"
27
+ },
28
+ "entitlements": {
29
+ "com.apple.developer.healthkit": true,
30
+ "com.apple.developer.healthkit.background-delivery": true
31
+ }
32
+ },
33
+ "android": {
34
+ "adaptiveIcon": {
35
+ "foregroundImage": "./assets/adaptive-icon.png",
36
+ "backgroundColor": "#FFFFFF"
37
+ }
38
+ },
39
+ "web": {
40
+ "favicon": "./assets/favicon.png"
41
+ }
42
+ }
43
+ }
Binary file
Binary file
Binary file
@@ -0,0 +1,8 @@
1
+ /* eslint-disable prettier/prettier */
2
+
3
+ module.exports = function(api) {
4
+ api.cache(true);
5
+ return {
6
+ presets: ['babel-preset-expo'],
7
+ };
8
+ };
@@ -0,0 +1,18 @@
1
+ {
2
+ "cli": {
3
+ "version": ">= 0.51.0"
4
+ },
5
+ "build": {
6
+ "development": {
7
+ "developmentClient": true,
8
+ "distribution": "internal"
9
+ },
10
+ "preview": {
11
+ "distribution": "internal"
12
+ },
13
+ "production": {}
14
+ },
15
+ "submit": {
16
+ "production": {}
17
+ }
18
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "healthkit-example-expo",
3
+ "version": "1.0.0",
4
+ "main": "node_modules/expo/AppEntry.js",
5
+ "scripts": {
6
+ "start": "expo start",
7
+ "ios": "expo start --ios",
8
+ "eject": "expo eject",
9
+ "build-local": "eas build --platform=ios --profile=development --local"
10
+ },
11
+ "dependencies": {
12
+ "@kingstinct/react-native-healthkit": "^4.1.1",
13
+ "expo": "~45.0.0",
14
+ "expo-dev-client": "~0.9.6",
15
+ "expo-status-bar": "~1.3.0",
16
+ "react": "17.0.2",
17
+ "react-dom": "17.0.2",
18
+ "react-native": "0.68.2",
19
+ "react-native-paper": "^4.12.1",
20
+ "react-native-web": "0.17.7"
21
+ },
22
+ "devDependencies": {
23
+ "@babel/core": "^7.12.9",
24
+ "@types/react": "~17.0.21",
25
+ "@types/react-native": "0.67",
26
+ "typescript": "4"
27
+ },
28
+ "resolutions": {
29
+ "@types/react": "17.0.2"
30
+ },
31
+ "private": true
32
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "expo/tsconfig.base",
3
+ "compilerOptions": {
4
+ "strict": true
5
+ }
6
+ }