@kingstinct/react-native-healthkit 8.7.2 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (449) hide show
  1. package/ReactNativeHealthkit.podspec +36 -0
  2. package/app.plugin.js +43 -33
  3. package/ios/Bridge.h +8 -0
  4. package/ios/CategoryTypeModule.swift +128 -0
  5. package/ios/CharacteristicTypeModule.swift +78 -0
  6. package/ios/Constants.swift +2 -0
  7. package/ios/CoreModule.swift +376 -0
  8. package/ios/CorrelationTypeModule.swift +153 -0
  9. package/ios/HeartbeatSeriesModule.swift +189 -0
  10. package/ios/Helpers.swift +529 -251
  11. package/ios/QuantityTypeModule.swift +461 -0
  12. package/ios/Serializers.swift +211 -124
  13. package/ios/SourceProxy.swift +35 -0
  14. package/ios/StateOfMindModule.swift +160 -0
  15. package/ios/WorkoutProxy.swift +405 -0
  16. package/ios/WorkoutSessionModule.swift +182 -0
  17. package/ios/WorkoutsModule.swift +357 -0
  18. package/package.json +43 -127
  19. package/react-native.config.js +16 -0
  20. package/src/hooks/useHealthkitAuthorization.test.ts +47 -29
  21. package/src/hooks/useHealthkitAuthorization.ts +21 -15
  22. package/src/hooks/useIsHealthDataAvailable.test.ts +17 -7
  23. package/src/hooks/useIsHealthDataAvailable.ts +7 -7
  24. package/src/hooks/useMostRecentCategorySample.ts +10 -12
  25. package/src/hooks/useMostRecentQuantitySample.ts +17 -36
  26. package/src/hooks/useMostRecentWorkout.ts +16 -38
  27. package/src/hooks/useSources.ts +8 -12
  28. package/src/hooks/useStatisticsForQuantity.ts +19 -14
  29. package/src/hooks/useSubscribeToChanges.ts +9 -13
  30. package/src/index.ios.ts +232 -0
  31. package/src/index.ts +444 -0
  32. package/src/modules.ts +43 -0
  33. package/src/specs/CategoryTypeModule.nitro.ts +64 -0
  34. package/src/specs/CharacteristicTypeModule.nitro.ts +22 -0
  35. package/src/specs/CoreModule.nitro.ts +107 -0
  36. package/src/specs/CorrelationTypeModule.nitro.ts +23 -0
  37. package/src/specs/HeartbeatSeriesModule.nitro.ts +19 -0
  38. package/src/specs/QuantityTypeModule.nitro.ts +60 -0
  39. package/src/specs/SourceProxy.nitro.ts +13 -0
  40. package/src/specs/StateOfMindModule.nitro.ts +23 -0
  41. package/src/specs/WorkoutProxy.nitro.ts +18 -0
  42. package/src/specs/WorkoutSessionModule.nitro.ts +71 -0
  43. package/src/specs/WorkoutsModule.nitro.ts +32 -0
  44. package/src/test-setup.ts +68 -54
  45. package/src/test-utils.ts +3 -2
  46. package/src/types/Auth.ts +17 -0
  47. package/src/types/Background.ts +9 -0
  48. package/src/types/CategoryType.ts +249 -0
  49. package/src/types/CategoryTypeIdentifier.ts +99 -0
  50. package/src/types/Characteristics.ts +53 -0
  51. package/src/types/Constants.ts +25 -0
  52. package/src/types/CorrelationType.ts +31 -0
  53. package/src/types/Device.ts +13 -0
  54. package/src/types/HeartbeatSeries.ts +29 -0
  55. package/src/types/InterfaceVerification.ts +164 -0
  56. package/src/types/InterfaceVerificationExample.ts +89 -0
  57. package/src/types/QuantitySample.ts +30 -0
  58. package/src/types/QuantityType.ts +192 -0
  59. package/src/types/QuantityTypeIdentifier.ts +758 -0
  60. package/src/types/QueryOptions.ts +69 -0
  61. package/src/types/README-InterfaceVerification.md +103 -0
  62. package/src/types/Shared.ts +79 -0
  63. package/src/types/Source.ts +11 -0
  64. package/src/types/StateOfMind.ts +110 -0
  65. package/src/types/Subscriptons.ts +10 -0
  66. package/src/types/Units.ts +190 -0
  67. package/src/types/WeatherCondition.ts +31 -0
  68. package/src/types/WorkoutKit.ts +18 -0
  69. package/src/types/Workouts.ts +282 -0
  70. package/src/utils/getMostRecentCategorySample.ts +7 -11
  71. package/src/utils/getMostRecentQuantitySample.ts +8 -18
  72. package/src/utils/getMostRecentWorkout.ts +7 -17
  73. package/src/utils/getPreferredUnit.ts +12 -8
  74. package/src/utils/subscribeToChanges.ts +9 -26
  75. package/LICENSE +0 -21
  76. package/README.md +0 -179
  77. package/ios/ReactNativeHealthkit-Bridging-Header.h +0 -2
  78. package/ios/ReactNativeHealthkit.m +0 -271
  79. package/ios/ReactNativeHealthkit.swift +0 -2333
  80. package/ios/ReactNativeHealthkit.xcodeproj/project.pbxproj +0 -279
  81. package/kingstinct-react-native-healthkit.podspec +0 -21
  82. package/lib/commonjs/hooks/useHealthkitAuthorization.js +0 -39
  83. package/lib/commonjs/hooks/useHealthkitAuthorization.js.map +0 -1
  84. package/lib/commonjs/hooks/useHealthkitAuthorization.test.js +0 -72
  85. package/lib/commonjs/hooks/useHealthkitAuthorization.test.js.map +0 -1
  86. package/lib/commonjs/hooks/useIsHealthDataAvailable.js +0 -27
  87. package/lib/commonjs/hooks/useIsHealthDataAvailable.js.map +0 -1
  88. package/lib/commonjs/hooks/useIsHealthDataAvailable.test.js +0 -41
  89. package/lib/commonjs/hooks/useIsHealthDataAvailable.test.js.map +0 -1
  90. package/lib/commonjs/hooks/useMostRecentCategorySample.js +0 -23
  91. package/lib/commonjs/hooks/useMostRecentCategorySample.js.map +0 -1
  92. package/lib/commonjs/hooks/useMostRecentQuantitySample.js +0 -37
  93. package/lib/commonjs/hooks/useMostRecentQuantitySample.js.map +0 -1
  94. package/lib/commonjs/hooks/useMostRecentWorkout.js +0 -48
  95. package/lib/commonjs/hooks/useMostRecentWorkout.js.map +0 -1
  96. package/lib/commonjs/hooks/useSources.js +0 -22
  97. package/lib/commonjs/hooks/useSources.js.map +0 -1
  98. package/lib/commonjs/hooks/useStatisticsForQuantity.js +0 -28
  99. package/lib/commonjs/hooks/useStatisticsForQuantity.js.map +0 -1
  100. package/lib/commonjs/hooks/useSubscribeToChanges.js +0 -28
  101. package/lib/commonjs/hooks/useSubscribeToChanges.js.map +0 -1
  102. package/lib/commonjs/index.ios.js +0 -531
  103. package/lib/commonjs/index.ios.js.map +0 -1
  104. package/lib/commonjs/index.js +0 -22
  105. package/lib/commonjs/index.js.map +0 -1
  106. package/lib/commonjs/index.native.js +0 -235
  107. package/lib/commonjs/index.native.js.map +0 -1
  108. package/lib/commonjs/index.web.js +0 -22
  109. package/lib/commonjs/index.web.js.map +0 -1
  110. package/lib/commonjs/native-types.js +0 -1575
  111. package/lib/commonjs/native-types.js.map +0 -1
  112. package/lib/commonjs/test-setup.js +0 -57
  113. package/lib/commonjs/test-setup.js.map +0 -1
  114. package/lib/commonjs/test-utils.js +0 -18
  115. package/lib/commonjs/test-utils.js.map +0 -1
  116. package/lib/commonjs/types.js +0 -17
  117. package/lib/commonjs/types.js.map +0 -1
  118. package/lib/commonjs/utils/deleteQuantitySample.js +0 -11
  119. package/lib/commonjs/utils/deleteQuantitySample.js.map +0 -1
  120. package/lib/commonjs/utils/deleteSamples.js +0 -18
  121. package/lib/commonjs/utils/deleteSamples.js.map +0 -1
  122. package/lib/commonjs/utils/deleteWorkoutSample.js +0 -11
  123. package/lib/commonjs/utils/deleteWorkoutSample.js.map +0 -1
  124. package/lib/commonjs/utils/deserializeCategorySample.js +0 -13
  125. package/lib/commonjs/utils/deserializeCategorySample.js.map +0 -1
  126. package/lib/commonjs/utils/deserializeCategorySample.test.js +0 -26
  127. package/lib/commonjs/utils/deserializeCategorySample.test.js.map +0 -1
  128. package/lib/commonjs/utils/deserializeCorrelation.js +0 -26
  129. package/lib/commonjs/utils/deserializeCorrelation.js.map +0 -1
  130. package/lib/commonjs/utils/deserializeHeartbeatSeriesSample.js +0 -15
  131. package/lib/commonjs/utils/deserializeHeartbeatSeriesSample.js.map +0 -1
  132. package/lib/commonjs/utils/deserializeSample.js +0 -15
  133. package/lib/commonjs/utils/deserializeSample.js.map +0 -1
  134. package/lib/commonjs/utils/deserializeWorkout.js +0 -15
  135. package/lib/commonjs/utils/deserializeWorkout.js.map +0 -1
  136. package/lib/commonjs/utils/ensureMetadata.js +0 -11
  137. package/lib/commonjs/utils/ensureMetadata.js.map +0 -1
  138. package/lib/commonjs/utils/ensureTotals.js +0 -11
  139. package/lib/commonjs/utils/ensureTotals.js.map +0 -1
  140. package/lib/commonjs/utils/ensureUnit.js +0 -17
  141. package/lib/commonjs/utils/ensureUnit.js.map +0 -1
  142. package/lib/commonjs/utils/getDateOfBirth.js +0 -14
  143. package/lib/commonjs/utils/getDateOfBirth.js.map +0 -1
  144. package/lib/commonjs/utils/getMostRecentCategorySample.js +0 -17
  145. package/lib/commonjs/utils/getMostRecentCategorySample.js.map +0 -1
  146. package/lib/commonjs/utils/getMostRecentQuantitySample.js +0 -21
  147. package/lib/commonjs/utils/getMostRecentQuantitySample.js.map +0 -1
  148. package/lib/commonjs/utils/getMostRecentWorkout.js +0 -19
  149. package/lib/commonjs/utils/getMostRecentWorkout.js.map +0 -1
  150. package/lib/commonjs/utils/getPreferredUnit.js +0 -14
  151. package/lib/commonjs/utils/getPreferredUnit.js.map +0 -1
  152. package/lib/commonjs/utils/getPreferredUnits.js +0 -14
  153. package/lib/commonjs/utils/getPreferredUnits.js.map +0 -1
  154. package/lib/commonjs/utils/getPreferredUnitsTyped.js +0 -33
  155. package/lib/commonjs/utils/getPreferredUnitsTyped.js.map +0 -1
  156. package/lib/commonjs/utils/getRequestStatusForAuthorization.js +0 -21
  157. package/lib/commonjs/utils/getRequestStatusForAuthorization.js.map +0 -1
  158. package/lib/commonjs/utils/getWorkoutPlanById.js +0 -13
  159. package/lib/commonjs/utils/getWorkoutPlanById.js.map +0 -1
  160. package/lib/commonjs/utils/prepareOptions.js +0 -25
  161. package/lib/commonjs/utils/prepareOptions.js.map +0 -1
  162. package/lib/commonjs/utils/queryCategorySamples.js +0 -17
  163. package/lib/commonjs/utils/queryCategorySamples.js.map +0 -1
  164. package/lib/commonjs/utils/queryCategorySamplesWithAnchor.js +0 -21
  165. package/lib/commonjs/utils/queryCategorySamplesWithAnchor.js.map +0 -1
  166. package/lib/commonjs/utils/queryCorrelationSamples.js +0 -17
  167. package/lib/commonjs/utils/queryCorrelationSamples.js.map +0 -1
  168. package/lib/commonjs/utils/queryHeartbeatSeriesSamples.js +0 -17
  169. package/lib/commonjs/utils/queryHeartbeatSeriesSamples.js.map +0 -1
  170. package/lib/commonjs/utils/queryHeartbeatSeriesSamplesWithAnchor.js +0 -21
  171. package/lib/commonjs/utils/queryHeartbeatSeriesSamplesWithAnchor.js.map +0 -1
  172. package/lib/commonjs/utils/queryQuantitySamples.js +0 -19
  173. package/lib/commonjs/utils/queryQuantitySamples.js.map +0 -1
  174. package/lib/commonjs/utils/queryQuantitySamplesWithAnchor.js +0 -23
  175. package/lib/commonjs/utils/queryQuantitySamplesWithAnchor.js.map +0 -1
  176. package/lib/commonjs/utils/querySources.js +0 -14
  177. package/lib/commonjs/utils/querySources.js.map +0 -1
  178. package/lib/commonjs/utils/queryStateOfMindSamples.js +0 -22
  179. package/lib/commonjs/utils/queryStateOfMindSamples.js.map +0 -1
  180. package/lib/commonjs/utils/queryStatisticsCollectionForQuantity.js +0 -16
  181. package/lib/commonjs/utils/queryStatisticsCollectionForQuantity.js.map +0 -1
  182. package/lib/commonjs/utils/queryStatisticsForQuantity.js +0 -29
  183. package/lib/commonjs/utils/queryStatisticsForQuantity.js.map +0 -1
  184. package/lib/commonjs/utils/queryWorkoutSamplesWithAnchor.js +0 -26
  185. package/lib/commonjs/utils/queryWorkoutSamplesWithAnchor.js.map +0 -1
  186. package/lib/commonjs/utils/queryWorkouts.js +0 -22
  187. package/lib/commonjs/utils/queryWorkouts.js.map +0 -1
  188. package/lib/commonjs/utils/requestAuthorization.js +0 -22
  189. package/lib/commonjs/utils/requestAuthorization.js.map +0 -1
  190. package/lib/commonjs/utils/saveCategorySample.js +0 -20
  191. package/lib/commonjs/utils/saveCategorySample.js.map +0 -1
  192. package/lib/commonjs/utils/saveCorrelationSample.js +0 -35
  193. package/lib/commonjs/utils/saveCorrelationSample.js.map +0 -1
  194. package/lib/commonjs/utils/saveQuantitySample.js +0 -16
  195. package/lib/commonjs/utils/saveQuantitySample.js.map +0 -1
  196. package/lib/commonjs/utils/saveStateOfMindSample.js +0 -17
  197. package/lib/commonjs/utils/saveStateOfMindSample.js.map +0 -1
  198. package/lib/commonjs/utils/saveWorkoutRoute.js +0 -26
  199. package/lib/commonjs/utils/saveWorkoutRoute.js.map +0 -1
  200. package/lib/commonjs/utils/saveWorkoutSample.js +0 -36
  201. package/lib/commonjs/utils/saveWorkoutSample.js.map +0 -1
  202. package/lib/commonjs/utils/serializeDate.js +0 -9
  203. package/lib/commonjs/utils/serializeDate.js.map +0 -1
  204. package/lib/commonjs/utils/serializeDate.test.js +0 -17
  205. package/lib/commonjs/utils/serializeDate.test.js.map +0 -1
  206. package/lib/commonjs/utils/startWatchApp.js +0 -11
  207. package/lib/commonjs/utils/startWatchApp.js.map +0 -1
  208. package/lib/commonjs/utils/subscribeToChanges.js +0 -27
  209. package/lib/commonjs/utils/subscribeToChanges.js.map +0 -1
  210. package/lib/module/hooks/useHealthkitAuthorization.js +0 -32
  211. package/lib/module/hooks/useHealthkitAuthorization.js.map +0 -1
  212. package/lib/module/hooks/useHealthkitAuthorization.test.js +0 -68
  213. package/lib/module/hooks/useHealthkitAuthorization.test.js.map +0 -1
  214. package/lib/module/hooks/useIsHealthDataAvailable.js +0 -21
  215. package/lib/module/hooks/useIsHealthDataAvailable.js.map +0 -1
  216. package/lib/module/hooks/useIsHealthDataAvailable.test.js +0 -37
  217. package/lib/module/hooks/useIsHealthDataAvailable.test.js.map +0 -1
  218. package/lib/module/hooks/useMostRecentCategorySample.js +0 -16
  219. package/lib/module/hooks/useMostRecentCategorySample.js.map +0 -1
  220. package/lib/module/hooks/useMostRecentQuantitySample.js +0 -30
  221. package/lib/module/hooks/useMostRecentQuantitySample.js.map +0 -1
  222. package/lib/module/hooks/useMostRecentWorkout.js +0 -41
  223. package/lib/module/hooks/useMostRecentWorkout.js.map +0 -1
  224. package/lib/module/hooks/useSources.js +0 -15
  225. package/lib/module/hooks/useSources.js.map +0 -1
  226. package/lib/module/hooks/useStatisticsForQuantity.js +0 -21
  227. package/lib/module/hooks/useStatisticsForQuantity.js.map +0 -1
  228. package/lib/module/hooks/useSubscribeToChanges.js +0 -21
  229. package/lib/module/hooks/useSubscribeToChanges.js.map +0 -1
  230. package/lib/module/index.ios.js +0 -209
  231. package/lib/module/index.ios.js.map +0 -1
  232. package/lib/module/index.js +0 -4
  233. package/lib/module/index.js.map +0 -1
  234. package/lib/module/index.native.js +0 -160
  235. package/lib/module/index.native.js.map +0 -1
  236. package/lib/module/index.web.js +0 -4
  237. package/lib/module/index.web.js.map +0 -1
  238. package/lib/module/native-types.js +0 -1606
  239. package/lib/module/native-types.js.map +0 -1
  240. package/lib/module/test-setup.js +0 -54
  241. package/lib/module/test-setup.js.map +0 -1
  242. package/lib/module/test-utils.js +0 -11
  243. package/lib/module/test-utils.js.map +0 -1
  244. package/lib/module/types.js +0 -67
  245. package/lib/module/types.js.map +0 -1
  246. package/lib/module/utils/deleteQuantitySample.js +0 -4
  247. package/lib/module/utils/deleteQuantitySample.js.map +0 -1
  248. package/lib/module/utils/deleteSamples.js +0 -11
  249. package/lib/module/utils/deleteSamples.js.map +0 -1
  250. package/lib/module/utils/deleteWorkoutSample.js +0 -4
  251. package/lib/module/utils/deleteWorkoutSample.js.map +0 -1
  252. package/lib/module/utils/deserializeCategorySample.js +0 -7
  253. package/lib/module/utils/deserializeCategorySample.js.map +0 -1
  254. package/lib/module/utils/deserializeCategorySample.test.js +0 -22
  255. package/lib/module/utils/deserializeCategorySample.test.js.map +0 -1
  256. package/lib/module/utils/deserializeCorrelation.js +0 -19
  257. package/lib/module/utils/deserializeCorrelation.js.map +0 -1
  258. package/lib/module/utils/deserializeHeartbeatSeriesSample.js +0 -9
  259. package/lib/module/utils/deserializeHeartbeatSeriesSample.js.map +0 -1
  260. package/lib/module/utils/deserializeSample.js +0 -9
  261. package/lib/module/utils/deserializeSample.js.map +0 -1
  262. package/lib/module/utils/deserializeWorkout.js +0 -9
  263. package/lib/module/utils/deserializeWorkout.js.map +0 -1
  264. package/lib/module/utils/ensureMetadata.js +0 -5
  265. package/lib/module/utils/ensureMetadata.js.map +0 -1
  266. package/lib/module/utils/ensureTotals.js +0 -5
  267. package/lib/module/utils/ensureTotals.js.map +0 -1
  268. package/lib/module/utils/ensureUnit.js +0 -10
  269. package/lib/module/utils/ensureUnit.js.map +0 -1
  270. package/lib/module/utils/getDateOfBirth.js +0 -7
  271. package/lib/module/utils/getDateOfBirth.js.map +0 -1
  272. package/lib/module/utils/getMostRecentCategorySample.js +0 -10
  273. package/lib/module/utils/getMostRecentCategorySample.js.map +0 -1
  274. package/lib/module/utils/getMostRecentQuantitySample.js +0 -14
  275. package/lib/module/utils/getMostRecentQuantitySample.js.map +0 -1
  276. package/lib/module/utils/getMostRecentWorkout.js +0 -12
  277. package/lib/module/utils/getMostRecentWorkout.js.map +0 -1
  278. package/lib/module/utils/getPreferredUnit.js +0 -7
  279. package/lib/module/utils/getPreferredUnit.js.map +0 -1
  280. package/lib/module/utils/getPreferredUnits.js +0 -7
  281. package/lib/module/utils/getPreferredUnits.js.map +0 -1
  282. package/lib/module/utils/getPreferredUnitsTyped.js +0 -26
  283. package/lib/module/utils/getPreferredUnitsTyped.js.map +0 -1
  284. package/lib/module/utils/getRequestStatusForAuthorization.js +0 -14
  285. package/lib/module/utils/getRequestStatusForAuthorization.js.map +0 -1
  286. package/lib/module/utils/getWorkoutPlanById.js +0 -6
  287. package/lib/module/utils/getWorkoutPlanById.js.map +0 -1
  288. package/lib/module/utils/prepareOptions.js +0 -18
  289. package/lib/module/utils/prepareOptions.js.map +0 -1
  290. package/lib/module/utils/queryCategorySamples.js +0 -10
  291. package/lib/module/utils/queryCategorySamples.js.map +0 -1
  292. package/lib/module/utils/queryCategorySamplesWithAnchor.js +0 -14
  293. package/lib/module/utils/queryCategorySamplesWithAnchor.js.map +0 -1
  294. package/lib/module/utils/queryCorrelationSamples.js +0 -10
  295. package/lib/module/utils/queryCorrelationSamples.js.map +0 -1
  296. package/lib/module/utils/queryHeartbeatSeriesSamples.js +0 -10
  297. package/lib/module/utils/queryHeartbeatSeriesSamples.js.map +0 -1
  298. package/lib/module/utils/queryHeartbeatSeriesSamplesWithAnchor.js +0 -14
  299. package/lib/module/utils/queryHeartbeatSeriesSamplesWithAnchor.js.map +0 -1
  300. package/lib/module/utils/queryQuantitySamples.js +0 -12
  301. package/lib/module/utils/queryQuantitySamples.js.map +0 -1
  302. package/lib/module/utils/queryQuantitySamplesWithAnchor.js +0 -16
  303. package/lib/module/utils/queryQuantitySamplesWithAnchor.js.map +0 -1
  304. package/lib/module/utils/querySources.js +0 -7
  305. package/lib/module/utils/querySources.js.map +0 -1
  306. package/lib/module/utils/queryStateOfMindSamples.js +0 -14
  307. package/lib/module/utils/queryStateOfMindSamples.js.map +0 -1
  308. package/lib/module/utils/queryStatisticsCollectionForQuantity.js +0 -9
  309. package/lib/module/utils/queryStatisticsCollectionForQuantity.js.map +0 -1
  310. package/lib/module/utils/queryStatisticsForQuantity.js +0 -22
  311. package/lib/module/utils/queryStatisticsForQuantity.js.map +0 -1
  312. package/lib/module/utils/queryWorkoutSamplesWithAnchor.js +0 -19
  313. package/lib/module/utils/queryWorkoutSamplesWithAnchor.js.map +0 -1
  314. package/lib/module/utils/queryWorkouts.js +0 -15
  315. package/lib/module/utils/queryWorkouts.js.map +0 -1
  316. package/lib/module/utils/requestAuthorization.js +0 -15
  317. package/lib/module/utils/requestAuthorization.js.map +0 -1
  318. package/lib/module/utils/saveCategorySample.js +0 -13
  319. package/lib/module/utils/saveCategorySample.js.map +0 -1
  320. package/lib/module/utils/saveCorrelationSample.js +0 -28
  321. package/lib/module/utils/saveCorrelationSample.js.map +0 -1
  322. package/lib/module/utils/saveQuantitySample.js +0 -9
  323. package/lib/module/utils/saveQuantitySample.js.map +0 -1
  324. package/lib/module/utils/saveStateOfMindSample.js +0 -10
  325. package/lib/module/utils/saveStateOfMindSample.js.map +0 -1
  326. package/lib/module/utils/saveWorkoutRoute.js +0 -19
  327. package/lib/module/utils/saveWorkoutRoute.js.map +0 -1
  328. package/lib/module/utils/saveWorkoutSample.js +0 -29
  329. package/lib/module/utils/saveWorkoutSample.js.map +0 -1
  330. package/lib/module/utils/serializeDate.js +0 -3
  331. package/lib/module/utils/serializeDate.js.map +0 -1
  332. package/lib/module/utils/serializeDate.test.js +0 -14
  333. package/lib/module/utils/serializeDate.test.js.map +0 -1
  334. package/lib/module/utils/startWatchApp.js +0 -4
  335. package/lib/module/utils/startWatchApp.js.map +0 -1
  336. package/lib/module/utils/subscribeToChanges.js +0 -20
  337. package/lib/module/utils/subscribeToChanges.js.map +0 -1
  338. package/lib/typescript/example-expo/App.d.ts +0 -2
  339. package/lib/typescript/src/hooks/useHealthkitAuthorization.d.ts +0 -8
  340. package/lib/typescript/src/hooks/useHealthkitAuthorization.test.d.ts +0 -1
  341. package/lib/typescript/src/hooks/useIsHealthDataAvailable.d.ts +0 -7
  342. package/lib/typescript/src/hooks/useIsHealthDataAvailable.test.d.ts +0 -1
  343. package/lib/typescript/src/hooks/useMostRecentCategorySample.d.ts +0 -7
  344. package/lib/typescript/src/hooks/useMostRecentQuantitySample.d.ts +0 -7
  345. package/lib/typescript/src/hooks/useMostRecentWorkout.d.ts +0 -10
  346. package/lib/typescript/src/hooks/useSources.d.ts +0 -3
  347. package/lib/typescript/src/hooks/useStatisticsForQuantity.d.ts +0 -4
  348. package/lib/typescript/src/hooks/useSubscribeToChanges.d.ts +0 -3
  349. package/lib/typescript/src/index.d.ts +0 -3
  350. package/lib/typescript/src/index.ios.d.ts +0 -198
  351. package/lib/typescript/src/index.native.d.ts +0 -41
  352. package/lib/typescript/src/index.web.d.ts +0 -3
  353. package/lib/typescript/src/native-types.d.ts +0 -1764
  354. package/lib/typescript/src/test-setup.d.ts +0 -1
  355. package/lib/typescript/src/test-utils.d.ts +0 -2
  356. package/lib/typescript/src/types.d.ts +0 -111
  357. package/lib/typescript/src/utils/deleteQuantitySample.d.ts +0 -4
  358. package/lib/typescript/src/utils/deleteSamples.d.ts +0 -8
  359. package/lib/typescript/src/utils/deleteWorkoutSample.d.ts +0 -3
  360. package/lib/typescript/src/utils/deserializeCategorySample.d.ts +0 -4
  361. package/lib/typescript/src/utils/deserializeCategorySample.test.d.ts +0 -1
  362. package/lib/typescript/src/utils/deserializeCorrelation.d.ts +0 -4
  363. package/lib/typescript/src/utils/deserializeHeartbeatSeriesSample.d.ts +0 -4
  364. package/lib/typescript/src/utils/deserializeSample.d.ts +0 -4
  365. package/lib/typescript/src/utils/deserializeWorkout.d.ts +0 -4
  366. package/lib/typescript/src/utils/ensureMetadata.d.ts +0 -2
  367. package/lib/typescript/src/utils/ensureTotals.d.ts +0 -2
  368. package/lib/typescript/src/utils/ensureUnit.d.ts +0 -3
  369. package/lib/typescript/src/utils/getDateOfBirth.d.ts +0 -2
  370. package/lib/typescript/src/utils/getMostRecentCategorySample.d.ts +0 -4
  371. package/lib/typescript/src/utils/getMostRecentQuantitySample.d.ts +0 -4
  372. package/lib/typescript/src/utils/getMostRecentWorkout.d.ts +0 -5
  373. package/lib/typescript/src/utils/getPreferredUnit.d.ts +0 -4
  374. package/lib/typescript/src/utils/getPreferredUnits.d.ts +0 -4
  375. package/lib/typescript/src/utils/getPreferredUnitsTyped.d.ts +0 -9
  376. package/lib/typescript/src/utils/getRequestStatusForAuthorization.d.ts +0 -3
  377. package/lib/typescript/src/utils/getWorkoutPlanById.d.ts +0 -5
  378. package/lib/typescript/src/utils/prepareOptions.d.ts +0 -9
  379. package/lib/typescript/src/utils/queryCategorySamples.d.ts +0 -5
  380. package/lib/typescript/src/utils/queryCategorySamplesWithAnchor.d.ts +0 -10
  381. package/lib/typescript/src/utils/queryCorrelationSamples.d.ts +0 -5
  382. package/lib/typescript/src/utils/queryHeartbeatSeriesSamples.d.ts +0 -10
  383. package/lib/typescript/src/utils/queryHeartbeatSeriesSamplesWithAnchor.d.ts +0 -10
  384. package/lib/typescript/src/utils/queryQuantitySamples.d.ts +0 -7
  385. package/lib/typescript/src/utils/queryQuantitySamplesWithAnchor.d.ts +0 -12
  386. package/lib/typescript/src/utils/querySources.d.ts +0 -4
  387. package/lib/typescript/src/utils/queryStateOfMindSamples.d.ts +0 -7
  388. package/lib/typescript/src/utils/queryStatisticsCollectionForQuantity.d.ts +0 -3
  389. package/lib/typescript/src/utils/queryStatisticsForQuantity.d.ts +0 -14
  390. package/lib/typescript/src/utils/queryWorkoutSamplesWithAnchor.d.ts +0 -9
  391. package/lib/typescript/src/utils/queryWorkouts.d.ts +0 -4
  392. package/lib/typescript/src/utils/requestAuthorization.d.ts +0 -4
  393. package/lib/typescript/src/utils/saveCategorySample.d.ts +0 -11
  394. package/lib/typescript/src/utils/saveCorrelationSample.d.ts +0 -8
  395. package/lib/typescript/src/utils/saveQuantitySample.d.ts +0 -7
  396. package/lib/typescript/src/utils/saveStateOfMindSample.d.ts +0 -11
  397. package/lib/typescript/src/utils/saveWorkoutRoute.d.ts +0 -3
  398. package/lib/typescript/src/utils/saveWorkoutSample.d.ts +0 -11
  399. package/lib/typescript/src/utils/serializeDate.d.ts +0 -2
  400. package/lib/typescript/src/utils/serializeDate.test.d.ts +0 -1
  401. package/lib/typescript/src/utils/startWatchApp.d.ts +0 -3
  402. package/lib/typescript/src/utils/subscribeToChanges.d.ts +0 -3
  403. package/src/index.ios.tsx +0 -292
  404. package/src/index.native.tsx +0 -233
  405. package/src/index.tsx +0 -5
  406. package/src/index.web.tsx +0 -5
  407. package/src/native-types.ts +0 -2471
  408. package/src/types.ts +0 -156
  409. package/src/utils/deleteQuantitySample.ts +0 -14
  410. package/src/utils/deleteSamples.ts +0 -23
  411. package/src/utils/deleteWorkoutSample.ts +0 -7
  412. package/src/utils/deserializeCategorySample.test.ts +0 -24
  413. package/src/utils/deserializeCategorySample.ts +0 -12
  414. package/src/utils/deserializeCorrelation.ts +0 -28
  415. package/src/utils/deserializeHeartbeatSeriesSample.ts +0 -12
  416. package/src/utils/deserializeSample.ts +0 -17
  417. package/src/utils/deserializeWorkout.ts +0 -14
  418. package/src/utils/ensureMetadata.ts +0 -5
  419. package/src/utils/ensureTotals.ts +0 -5
  420. package/src/utils/ensureUnit.ts +0 -19
  421. package/src/utils/getDateOfBirth.ts +0 -8
  422. package/src/utils/getPreferredUnits.ts +0 -14
  423. package/src/utils/getPreferredUnitsTyped.ts +0 -38
  424. package/src/utils/getRequestStatusForAuthorization.ts +0 -21
  425. package/src/utils/getWorkoutPlanById.ts +0 -7
  426. package/src/utils/prepareOptions.ts +0 -19
  427. package/src/utils/queryCategorySamples.ts +0 -29
  428. package/src/utils/queryCategorySamplesWithAnchor.ts +0 -39
  429. package/src/utils/queryCorrelationSamples.ts +0 -29
  430. package/src/utils/queryHeartbeatSeriesSamples.ts +0 -29
  431. package/src/utils/queryHeartbeatSeriesSamplesWithAnchor.ts +0 -33
  432. package/src/utils/queryQuantitySamples.ts +0 -38
  433. package/src/utils/queryQuantitySamplesWithAnchor.ts +0 -46
  434. package/src/utils/querySources.ts +0 -21
  435. package/src/utils/queryStateOfMindSamples.ts +0 -14
  436. package/src/utils/queryStatisticsCollectionForQuantity.ts +0 -38
  437. package/src/utils/queryStatisticsForQuantity.ts +0 -38
  438. package/src/utils/queryWorkoutSamplesWithAnchor.ts +0 -46
  439. package/src/utils/queryWorkouts.ts +0 -28
  440. package/src/utils/requestAuthorization.ts +0 -19
  441. package/src/utils/saveCategorySample.ts +0 -31
  442. package/src/utils/saveCorrelationSample.ts +0 -43
  443. package/src/utils/saveQuantitySample.ts +0 -29
  444. package/src/utils/saveStateOfMindSample.ts +0 -38
  445. package/src/utils/saveWorkoutRoute.ts +0 -21
  446. package/src/utils/saveWorkoutSample.ts +0 -42
  447. package/src/utils/serializeDate.test.ts +0 -16
  448. package/src/utils/serializeDate.ts +0 -5
  449. package/src/utils/startWatchApp.ts +0 -7
@@ -1,2333 +0,0 @@
1
- import CoreLocation
2
- import HealthKit
3
-
4
- #if canImport(WorkoutKit)
5
- import WorkoutKit
6
- #endif
7
-
8
- @objc(ReactNativeHealthkit)
9
- @available(iOS 10.0, *)
10
- class ReactNativeHealthkit: RCTEventEmitter {
11
- var _store: HKHealthStore?
12
- var _runningQueries: [String: HKQuery]
13
- var _dateFormatter: ISO8601DateFormatter
14
- var _hasListeners = false
15
-
16
- override init() {
17
- self._runningQueries = [String: HKQuery]()
18
- self._dateFormatter = ISO8601DateFormatter()
19
- self._dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
20
- if HKHealthStore.isHealthDataAvailable() {
21
- self._store = HKHealthStore.init()
22
- }
23
- super.init()
24
- }
25
-
26
- deinit {
27
- if let store = _store {
28
- for query in self._runningQueries {
29
- store.stop(query.value)
30
- }
31
- }
32
- }
33
-
34
- override func stopObserving() {
35
- self._hasListeners = false
36
- if let store = _store {
37
- for query in self._runningQueries {
38
- store.stop(query.value)
39
- }
40
- }
41
- }
42
-
43
- override func startObserving() {
44
- self._hasListeners = true
45
- }
46
-
47
- @objc(isProtectedDataAvailable:withRejecter:)
48
- func isProtectedDataAvailable(
49
- resolve: RCTPromiseResolveBlock,
50
- reject: RCTPromiseRejectBlock
51
- ) {
52
- resolve(UIApplication.shared.isProtectedDataAvailable)
53
- }
54
-
55
- @objc(isHealthDataAvailable:withRejecter:)
56
- func isHealthDataAvailable(
57
- resolve: RCTPromiseResolveBlock,
58
- reject: RCTPromiseRejectBlock
59
- ) {
60
- resolve(HKHealthStore.isHealthDataAvailable())
61
- }
62
-
63
- @available(iOS 12.0, *)
64
- @objc(supportsHealthRecords:withRejecter:)
65
- func supportsHealthRecords(
66
- resolve: RCTPromiseResolveBlock,
67
- reject: RCTPromiseRejectBlock
68
- ) {
69
- guard let store = _store else {
70
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
71
- }
72
- resolve(store.supportsHealthRecords())
73
- }
74
-
75
- @available(iOS 12.0, *)
76
- @objc(getRequestStatusForAuthorization:read:resolve:withRejecter:)
77
- func getRequestStatusForAuthorization(
78
- toShare: NSDictionary,
79
- read: NSDictionary,
80
- resolve: @escaping RCTPromiseResolveBlock,
81
- reject: @escaping RCTPromiseRejectBlock
82
- ) {
83
- guard let store = _store else {
84
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
85
- }
86
-
87
- let share = sampleTypesFromDictionary(typeIdentifiers: toShare)
88
- let toRead = objectTypesFromDictionary(typeIdentifiers: read)
89
-
90
- store.getRequestStatusForAuthorization(toShare: share, read: toRead) {
91
- (
92
- status: HKAuthorizationRequestStatus,
93
- error: Error?
94
- ) in
95
- guard let err = error else {
96
- return resolve(status.rawValue)
97
- }
98
- reject(GENERIC_ERROR, err.localizedDescription, err)
99
- }
100
- }
101
-
102
- @objc(getPreferredUnits:resolve:reject:)
103
- func getPreferredUnits(
104
- forIdentifiers: NSArray,
105
- resolve: @escaping RCTPromiseResolveBlock,
106
- reject: @escaping RCTPromiseRejectBlock
107
- ) {
108
- guard let store = _store else {
109
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
110
- }
111
- var quantityTypes = Set<HKQuantityType>()
112
- for identifierString in forIdentifiers {
113
- let identifier = HKQuantityTypeIdentifier.init(rawValue: identifierString as! String)
114
- let type = HKSampleType.quantityType(forIdentifier: identifier)
115
- if type != nil {
116
- quantityTypes.insert(type!)
117
- }
118
- }
119
-
120
- store.preferredUnits(for: quantityTypes) {
121
- (typePerUnits: [HKQuantityType: HKUnit], _: Error?) in
122
- let dic: NSMutableDictionary = NSMutableDictionary()
123
-
124
- for typePerUnit in typePerUnits {
125
- dic.setObject(typePerUnit.value.unitString, forKey: typePerUnit.key.identifier as NSCopying)
126
- }
127
-
128
- resolve(dic)
129
- }
130
- }
131
-
132
- @objc(getBiologicalSex:withRejecter:)
133
- func getBiologicalSex(
134
- resolve: @escaping RCTPromiseResolveBlock,
135
- reject: @escaping RCTPromiseRejectBlock
136
- ) {
137
- guard let store = _store else {
138
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
139
- }
140
-
141
- do {
142
- let bioSex = try store.biologicalSex()
143
- resolve(bioSex.biologicalSex.rawValue)
144
- } catch {
145
- reject(GENERIC_ERROR, error.localizedDescription, error)
146
- }
147
- }
148
-
149
- @objc(getDateOfBirth:withRejecter:)
150
- func getDateOfBirth(
151
- resolve: @escaping RCTPromiseResolveBlock,
152
- reject: @escaping RCTPromiseRejectBlock
153
- ) {
154
- guard let store = _store else {
155
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
156
- }
157
-
158
- do {
159
- let dateOfBirth = try store.dateOfBirthComponents()
160
-
161
- resolve(_dateFormatter.string(from: dateOfBirth.date!))
162
- } catch {
163
- reject(GENERIC_ERROR, error.localizedDescription, error)
164
- }
165
- }
166
-
167
- @objc(getBloodType:withRejecter:)
168
- func getBloodType(
169
- resolve: @escaping RCTPromiseResolveBlock,
170
- reject: @escaping RCTPromiseRejectBlock
171
- ) {
172
- guard let store = _store else {
173
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
174
- }
175
-
176
- do {
177
- let bloodType = try store.bloodType()
178
- resolve(bloodType.bloodType.rawValue)
179
- } catch {
180
- reject(GENERIC_ERROR, error.localizedDescription, error)
181
- }
182
- }
183
-
184
- @objc(getFitzpatrickSkinType:withRejecter:)
185
- func getFitzpatrickSkinType(
186
- resolve: @escaping RCTPromiseResolveBlock,
187
- reject: @escaping RCTPromiseRejectBlock
188
- ) {
189
- guard let store = _store else {
190
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
191
- }
192
-
193
- do {
194
- let fitzpatrickSkinType = try store.fitzpatrickSkinType()
195
- resolve(fitzpatrickSkinType.skinType.rawValue)
196
- } catch {
197
- reject(GENERIC_ERROR, error.localizedDescription, error)
198
- }
199
- }
200
-
201
- @available(iOS 10.0, *)
202
- @objc(getWheelchairUse:withRejecter:)
203
- func getWheelchairUse(
204
- resolve: @escaping RCTPromiseResolveBlock,
205
- reject: @escaping RCTPromiseRejectBlock
206
- ) {
207
- guard let store = _store else {
208
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
209
- }
210
-
211
- do {
212
- let wheelchairUse = try store.wheelchairUse()
213
- resolve(wheelchairUse.wheelchairUse.rawValue)
214
- } catch {
215
- reject(GENERIC_ERROR, error.localizedDescription, error)
216
- }
217
- }
218
-
219
- @objc(authorizationStatusFor:withResolver:withRejecter:)
220
- func authorizationStatusFor(
221
- typeIdentifier: String,
222
- resolve: @escaping RCTPromiseResolveBlock,
223
- reject: @escaping RCTPromiseRejectBlock
224
- ) {
225
- guard let store = _store else {
226
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
227
- }
228
-
229
- guard let objectType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
230
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
231
- }
232
-
233
- let authStatus = store.authorizationStatus(for: objectType)
234
- resolve(authStatus.rawValue)
235
- }
236
-
237
- @objc(saveQuantitySample:unitString:value:start:end:metadata:resolve:reject:)
238
- func saveQuantitySample(
239
- typeIdentifier: String,
240
- unitString: String,
241
- value: Double,
242
- start: Date,
243
- end: Date,
244
- metadata: [String: Any],
245
- resolve: @escaping RCTPromiseResolveBlock,
246
- reject: @escaping RCTPromiseRejectBlock
247
- ) {
248
- guard let store = _store else {
249
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
250
- }
251
-
252
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
253
-
254
- guard let type = HKObjectType.quantityType(forIdentifier: identifier) else {
255
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
256
- }
257
-
258
- let unit = HKUnit.init(from: unitString)
259
- let quantity = HKQuantity.init(unit: unit, doubleValue: value)
260
- let sample = HKQuantitySample.init(
261
- type: type,
262
- quantity: quantity,
263
- start: start,
264
- end: end,
265
- metadata: metadata
266
- )
267
-
268
- store.save(sample) { (success: Bool, error: Error?) in
269
- guard let err = error else {
270
- return resolve(success)
271
- }
272
- reject(GENERIC_ERROR, err.localizedDescription, error)
273
- }
274
- }
275
-
276
- @objc(deleteQuantitySample:uuid:resolve:reject:)
277
- func deleteQuantitySample(
278
- typeIdentifier: String,
279
- uuid: String,
280
- resolve: @escaping RCTPromiseResolveBlock,
281
- reject: @escaping RCTPromiseRejectBlock
282
- ) {
283
- guard let store = _store else {
284
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
285
- }
286
-
287
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
288
- let sampleUuid = UUID.init(uuidString: uuid)!
289
-
290
- guard let sampleType = HKObjectType.quantityType(forIdentifier: identifier) else {
291
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
292
- }
293
-
294
- let samplePredicate = HKQuery.predicateForObject(with: sampleUuid)
295
-
296
- store.deleteObjects(of: sampleType, predicate: samplePredicate) {
297
- (success: Bool, _: Int, error: Error?) in
298
- guard let err = error else {
299
- return resolve(success)
300
- }
301
- reject(GENERIC_ERROR, err.localizedDescription, error)
302
- }
303
- }
304
-
305
- @objc(deleteSamples:start:end:resolve:reject:)
306
- func deleteSamples(
307
- typeIdentifier: String,
308
- start: Date,
309
- end: Date,
310
- resolve: @escaping RCTPromiseResolveBlock,
311
- reject: @escaping RCTPromiseRejectBlock
312
- ) {
313
- guard let store = _store else {
314
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
315
- }
316
-
317
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
318
-
319
- guard let sampleType = HKObjectType.quantityType(forIdentifier: identifier) else {
320
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
321
- }
322
-
323
- let samplePredicate = HKQuery.predicateForSamples(
324
- withStart: start,
325
- end: end,
326
- options: HKQueryOptions.strictStartDate
327
- )
328
-
329
- store.deleteObjects(of: sampleType, predicate: samplePredicate) {
330
- (success: Bool, _: Int, error: Error?) in
331
- guard let err = error else {
332
- return resolve(success)
333
- }
334
- reject(GENERIC_ERROR, err.localizedDescription, error)
335
- }
336
- }
337
-
338
- @objc(deleteWorkoutSample:resolve:reject:)
339
- func deleteWorkoutSample(
340
- uuid: String,
341
- resolve: @escaping RCTPromiseResolveBlock,
342
- reject: @escaping RCTPromiseRejectBlock
343
- ) {
344
- guard let store = _store else {
345
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
346
- }
347
-
348
- guard let workoutUUID = UUID.init(uuidString: uuid) else {
349
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize UUID from string", nil)
350
- }
351
-
352
- let samplePredicate = HKQuery.predicateForObject(with: workoutUUID)
353
-
354
- store.deleteObjects(of: HKObjectType.workoutType(), predicate: samplePredicate) { (success: Bool, _: Int, error: Error?) in
355
- guard let err = error else {
356
- return resolve(success)
357
- }
358
- reject(GENERIC_ERROR, err.localizedDescription, error)
359
- }
360
- }
361
-
362
- @objc(saveCorrelationSample:samples:start:end:metadata:resolve:reject:)
363
- func saveCorrelationSample(
364
- typeIdentifier: String,
365
- samples: [[String: Any]],
366
- start: Date,
367
- end: Date,
368
- metadata: [String: Any],
369
- resolve: @escaping RCTPromiseResolveBlock,
370
- reject: @escaping RCTPromiseRejectBlock
371
- ) {
372
- guard let store = _store else {
373
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
374
- }
375
-
376
- let identifier = HKCorrelationTypeIdentifier.init(rawValue: typeIdentifier)
377
-
378
- guard let type = HKObjectType.correlationType(forIdentifier: identifier) else {
379
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
380
- }
381
-
382
- var initializedSamples = Set<HKSample>()
383
- for sample in samples {
384
- if sample.keys.contains("quantityType") {
385
- let typeId = HKQuantityTypeIdentifier.init(rawValue: sample["quantityType"] as! String)
386
- if let type = HKSampleType.quantityType(forIdentifier: typeId) {
387
- let unitStr = sample["unit"] as! String
388
- let quantityVal = sample["quantity"] as! Double
389
- let metadata = sample["metadata"] as? [String: Any]
390
-
391
- let unit = HKUnit.init(from: unitStr)
392
- let quantity = HKQuantity.init(unit: unit, doubleValue: quantityVal)
393
- let quantitySample = HKQuantitySample.init(
394
- type: type,
395
- quantity: quantity,
396
- start: start,
397
- end: end,
398
- metadata: metadata
399
- )
400
- initializedSamples.insert(quantitySample)
401
- }
402
- } else if sample.keys.contains("categoryType") {
403
- let typeId = HKCategoryTypeIdentifier.init(rawValue: sample["categoryType"] as! String)
404
- if let type = HKSampleType.categoryType(forIdentifier: typeId) {
405
- let value = sample["value"] as! Int
406
- let metadata = sample["metadata"] as? [String: Any]
407
- let categorySample = HKCategorySample.init(
408
- type: type,
409
- value: value,
410
- start: start,
411
- end: end,
412
- metadata: metadata
413
- )
414
- initializedSamples.insert(categorySample)
415
- }
416
- }
417
-
418
- }
419
-
420
- let correlation = HKCorrelation.init(
421
- type: type,
422
- start: start,
423
- end: end,
424
- objects: initializedSamples,
425
- metadata: metadata
426
- )
427
-
428
- store.save(correlation) { (success: Bool, error: Error?) in
429
- guard let err = error else {
430
- return resolve(success)
431
- }
432
- reject(GENERIC_ERROR, err.localizedDescription, error)
433
- }
434
- }
435
-
436
- @objc(saveWorkoutSample:quantities:start:end:totals:metadata:resolve:reject:)
437
- func saveWorkoutSample(
438
- typeIdentifier: UInt,
439
- quantities: [[String: Any]],
440
- start: Date,
441
- end: Date,
442
- totals: [String: Any],
443
- metadata: [String: Any],
444
- resolve: @escaping RCTPromiseResolveBlock,
445
- reject: @escaping RCTPromiseRejectBlock
446
- ) {
447
- guard let store = _store else {
448
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
449
- }
450
-
451
- guard let type = HKWorkoutActivityType.init(rawValue: typeIdentifier) else {
452
- return reject(
453
- TYPE_IDENTIFIER_ERROR,
454
- "Failed to initialize HKWorkoutActivityType " + typeIdentifier.description, nil)
455
- }
456
-
457
- // if start and end both exist, ensure that start date is before end date
458
- if let startDate = start as Date?, let endDate = end as Date? {
459
- if startDate > endDate {
460
- return reject(GENERIC_ERROR, "Start date must be before end date", nil)
461
- }
462
- }
463
-
464
- var initializedSamples = [HKSample]()
465
- var totalEnergyBurned: HKQuantity?
466
- var totalDistance: HKQuantity?
467
- var totalSwimmingStrokeCount: HKQuantity?
468
- var totalFlightsClimbed: HKQuantity?
469
- // generating quantity samples
470
- for quantity in quantities {
471
- let typeId = HKQuantityTypeIdentifier.init(rawValue: quantity["quantityType"] as! String)
472
- if let type = HKSampleType.quantityType(forIdentifier: typeId) {
473
- let unitStr = quantity["unit"] as! String
474
- let quantityVal = quantity["quantity"] as! Double
475
- let metadata = quantity["metadata"] as? [String: Any]
476
- let quantityStart = quantity["startDate"] as? String
477
- let quantityEnd = quantity["endDate"] as? String
478
- let unit = HKUnit.init(from: unitStr)
479
- let quantity = HKQuantity.init(unit: unit, doubleValue: quantityVal)
480
-
481
- if quantity.is(compatibleWith: HKUnit.kilocalorie()) {
482
- totalEnergyBurned = quantity
483
- }
484
- if quantity.is(compatibleWith: HKUnit.meter()) {
485
- totalDistance = quantity
486
- }
487
- if typeId == HKQuantityTypeIdentifier.swimmingStrokeCount {
488
- totalSwimmingStrokeCount = quantity
489
- }
490
- if typeId == HKQuantityTypeIdentifier.flightsClimbed {
491
- totalFlightsClimbed = quantity
492
- }
493
- if let quantityStart, let quantityEnd {
494
- let quantityStartDate = self._dateFormatter.date(from: quantityStart) ?? start
495
- let quantityEndDate = self._dateFormatter.date(from: quantityEnd) ?? end
496
- let quantitySample = HKQuantitySample.init(
497
- type: type,
498
- quantity: quantity,
499
- start: quantityStartDate,
500
- end: quantityEndDate,
501
- metadata: metadata
502
- )
503
- initializedSamples.append(quantitySample)
504
- } else {
505
- // Handle the case where either startDate or endDate is nil
506
- let quantitySample = HKQuantitySample.init(
507
- type: type,
508
- quantity: quantity,
509
- start: start,
510
- end: end,
511
- metadata: metadata
512
- )
513
- initializedSamples.append(quantitySample)
514
- }
515
- }
516
- }
517
-
518
- // if totals are provided override samples
519
- let rawTotalDistance = totals["distance"] as? Double ?? 0.0
520
- let rawTotalEnergy = totals["energyBurned"] as? Double ?? 0.0
521
-
522
- if rawTotalDistance != 0.0 {
523
- totalDistance = HKQuantity(unit: .meter(), doubleValue: rawTotalDistance)
524
- }
525
- if rawTotalEnergy != 0.0 {
526
- totalEnergyBurned = HKQuantity(unit: .kilocalorie(), doubleValue: rawTotalEnergy)
527
- }
528
-
529
- // creating workout
530
- var workout: HKWorkout?
531
-
532
- if totalSwimmingStrokeCount != nil {
533
- workout = HKWorkout.init(
534
- activityType: type,
535
- start: start,
536
- end: end,
537
- workoutEvents: nil,
538
- totalEnergyBurned: totalEnergyBurned,
539
- totalDistance: totalDistance,
540
- totalSwimmingStrokeCount: totalSwimmingStrokeCount,
541
- device: nil,
542
- metadata: metadata
543
- )
544
- } else {
545
- if #available(iOS 11, *) {
546
- if totalFlightsClimbed != nil {
547
- workout = HKWorkout.init(
548
- activityType: type,
549
- start: start,
550
- end: end,
551
- workoutEvents: nil,
552
- totalEnergyBurned: totalEnergyBurned,
553
- totalDistance: totalDistance,
554
- totalFlightsClimbed: totalFlightsClimbed,
555
- device: nil,
556
- metadata: metadata
557
- )
558
- }
559
- }
560
- }
561
-
562
- if workout == nil {
563
- workout = HKWorkout.init(
564
- activityType: type,
565
- start: start,
566
- end: end,
567
- workoutEvents: nil,
568
- totalEnergyBurned: totalEnergyBurned,
569
- totalDistance: totalDistance,
570
- metadata: metadata
571
- )
572
- }
573
-
574
- guard let workout = workout else {
575
- reject(GENERIC_ERROR, "Could not create workout", nil)
576
- return
577
- }
578
-
579
- // saving workout, samples and route
580
- store.save(workout) { (_: Bool, error: Error?) in
581
- guard error == nil else {
582
- reject(GENERIC_ERROR, error!.localizedDescription, error)
583
- return
584
- }
585
-
586
- if initializedSamples.isEmpty {
587
- return resolve(workout.uuid.uuidString)
588
- }
589
-
590
- store.add(initializedSamples, to: workout) { (_, error: Error?) in
591
- guard error == nil else {
592
- reject(GENERIC_ERROR, error!.localizedDescription, error)
593
- return
594
- }
595
- return resolve(workout.uuid.uuidString)
596
- }
597
- }
598
- }
599
-
600
- // function which will take an array of location in string format and create an array of CLLocations
601
- func _createCLLocations(from locations: [[String: Any]]) -> [CLLocation] {
602
- var clLocations: [CLLocation] = []
603
- for location in locations {
604
- guard let latitude = location["latitude"] as? CLLocationDegrees,
605
- let longitude = location["longitude"] as? CLLocationDegrees,
606
- let altitude = location["altitude"] as? CLLocationDistance,
607
- let horizontalAccuracy = location["horizontalAccuracy"] as? CLLocationAccuracy,
608
- let verticalAccuracy = location["verticalAccuracy"] as? CLLocationAccuracy,
609
- let course = location["course"] as? CLLocationDirection,
610
- let speed = location["speed"] as? CLLocationSpeed,
611
- let timestamp = location["timestamp"] as? String
612
- else {
613
- continue
614
- }
615
-
616
- let date = self._dateFormatter.date(from: timestamp) ?? Date()
617
- let clLocation = CLLocation(
618
- coordinate: CLLocationCoordinate2D(
619
- latitude: latitude,
620
- longitude: longitude
621
- ), altitude: altitude,
622
- horizontalAccuracy: horizontalAccuracy,
623
- verticalAccuracy: verticalAccuracy,
624
- course: course,
625
- speed: speed,
626
- timestamp: date
627
- )
628
- clLocations.append(clLocation)
629
- }
630
- return clLocations
631
- }
632
-
633
- @available(iOS 13.0.0, *)
634
- @objc(saveWorkoutRoute:locations:resolve:reject:)
635
- func saveWorkoutRoute(
636
- workoutUUID: String,
637
- locations: [[String: Any]],
638
- resolve: @escaping RCTPromiseResolveBlock,
639
- reject: @escaping RCTPromiseRejectBlock
640
- ) {
641
- guard let store = _store else {
642
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
643
- }
644
-
645
- Task {
646
- if let uuid = UUID(uuidString: workoutUUID) {
647
- do {
648
- let workout = await self.getWorkoutByID(store: store, workoutUUID: uuid)
649
- if let workout {
650
- // create CLLocations and return if locations are empty
651
- let clLocations = self._createCLLocations(from: locations)
652
- if clLocations.isEmpty {
653
- return reject(GENERIC_ERROR, "No locations provided", nil)
654
- }
655
- // create route
656
- let routeBuilder = HKWorkoutRouteBuilder(healthStore: store, device: nil)
657
- try await routeBuilder.insertRouteData(clLocations)
658
- try await routeBuilder.finishRoute(with: workout, metadata: nil)
659
-
660
- return resolve(true)
661
- } else {
662
- return reject(GENERIC_ERROR, "No workout found", nil)
663
- }
664
- } catch {
665
- return reject(GENERIC_ERROR, error.localizedDescription, error)
666
- }
667
- } else {
668
- return reject(GENERIC_ERROR, "Invalid UUID", nil)
669
- }
670
- }
671
- }
672
-
673
- @objc(saveStateOfMindSample:kind:valence:labels:associations:metadata:resolve:reject:)
674
- func saveStateOfMindSample(
675
- _ date: Date,
676
- kind: Int,
677
- valence: Double, // non-integer number, ie 0.5
678
- labels: [Int],
679
- associations: [Int],
680
- metadata: NSDictionary,
681
- resolve: @escaping RCTPromiseResolveBlock,
682
- reject: @escaping RCTPromiseRejectBlock
683
- ) {
684
- guard let store = _store else {
685
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
686
- }
687
-
688
- #if compiler(>=6)
689
- if #available(iOS 18.0, *) {
690
-
691
- // ensures valence does not exceed -1.0 and 1.0
692
- let safeValence = max(-1.0, min(1.0, valence))
693
-
694
- let sample = HKStateOfMind(
695
- date: date,
696
- kind: HKStateOfMind.Kind.convertToStateOfMindKind(int: kind),
697
- valence: safeValence,
698
- labels: HKStateOfMind.Label.convertToStateOfMindLabels(intArray: labels),
699
- associations: HKStateOfMind.Association.convertToStateOfMindAssociations(intArray: associations),
700
- metadata: metadata as? [String: Any]
701
- )
702
-
703
- store.save(sample) { (success: Bool, error: Error?) in
704
- guard let err = error else {
705
- return resolve(success)
706
- }
707
- reject(GENERIC_ERROR, err.localizedDescription, error)
708
- }
709
-
710
- } else {
711
- reject("STATE_OF_MIND_ERROR", "State of Mind features require iOS 18.0 or later", nil)
712
- }
713
- #else
714
- reject(
715
- "STATE_OF_MIND_ERROR", "State of Mind features require Xcode 16 or later to compile", nil)
716
- #endif
717
- }
718
-
719
- @objc(saveCategorySample:value:start:end:metadata:resolve:reject:)
720
- func saveCategorySample(
721
- typeIdentifier: String,
722
- value: Double,
723
- start: Date,
724
- end: Date,
725
- metadata: NSDictionary,
726
- resolve: @escaping RCTPromiseResolveBlock,
727
- reject: @escaping RCTPromiseRejectBlock
728
- ) {
729
- guard let store = _store else {
730
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
731
- }
732
-
733
- let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier)
734
-
735
- guard let type = HKObjectType.categoryType(forIdentifier: identifier) else {
736
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
737
- }
738
-
739
- let sample = HKCategorySample.init(
740
- type: type,
741
- value: Int(value),
742
- start: start,
743
- end: end,
744
- metadata: metadata as? [String: Any]
745
- )
746
-
747
- store.save(sample) { (success: Bool, error: Error?) in
748
- guard let err = error else {
749
- return resolve(success)
750
- }
751
- reject(GENERIC_ERROR, err.localizedDescription, error)
752
- }
753
- }
754
-
755
- override func supportedEvents() -> [String]! {
756
- return [
757
- "onChange",
758
- "onRemoteWorkoutStateChange",
759
- "onRemoteWorkoutError",
760
- "onRemoteWorkoutDataReceived",
761
- "onRemoteWorkoutEventReceived"
762
- ]
763
- }
764
-
765
- @objc(enableBackgroundDelivery:updateFrequency:resolve:reject:)
766
- func enableBackgroundDelivery(
767
- typeIdentifier: String,
768
- updateFrequency: Int,
769
- resolve: @escaping RCTPromiseResolveBlock,
770
- reject: @escaping RCTPromiseRejectBlock
771
- ) {
772
- guard let store = _store else {
773
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
774
- }
775
-
776
- guard let sampleType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
777
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
778
- }
779
-
780
- guard let frequency = HKUpdateFrequency.init(rawValue: updateFrequency) else {
781
- return reject("UpdateFrequency not valid", "UpdateFrequency not valid", nil)
782
- }
783
-
784
- store.enableBackgroundDelivery(for: sampleType, frequency: frequency) { (success, error) in
785
- guard let err = error else {
786
- return resolve(success)
787
- }
788
- reject(GENERIC_ERROR, err.localizedDescription, err)
789
- }
790
- }
791
-
792
- @objc(disableAllBackgroundDelivery:reject:)
793
- func disableAllBackgroundDelivery(
794
- resolve: @escaping RCTPromiseResolveBlock,
795
- reject: @escaping RCTPromiseRejectBlock
796
- ) {
797
- guard let store = _store else {
798
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
799
- }
800
-
801
- store.disableAllBackgroundDelivery(completion: { (success, error) in
802
- guard let err = error else {
803
- return resolve(success)
804
- }
805
- reject(GENERIC_ERROR, err.localizedDescription, err)
806
- })
807
- }
808
-
809
- @objc(disableBackgroundDelivery:resolve:reject:)
810
- func disableBackgroundDelivery(
811
- typeIdentifier: String,
812
- resolve: @escaping RCTPromiseResolveBlock,
813
- reject: @escaping RCTPromiseRejectBlock
814
- ) {
815
- guard let store = _store else {
816
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
817
- }
818
-
819
- guard let sampleType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
820
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
821
- }
822
-
823
- store.disableBackgroundDelivery(for: sampleType) { (success, error) in
824
- guard let err = error else {
825
- return resolve(success)
826
- }
827
- reject(GENERIC_ERROR, err.localizedDescription, err)
828
- }
829
- }
830
-
831
- @objc(subscribeToObserverQuery:resolve:reject:)
832
- func subscribeToObserverQuery(
833
- typeIdentifier: String,
834
- resolve: @escaping RCTPromiseResolveBlock,
835
- reject: @escaping RCTPromiseRejectBlock
836
- ) {
837
- guard let store = _store else {
838
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
839
- }
840
-
841
- guard let sampleType = sampleTypeFromString(typeIdentifier: typeIdentifier) else {
842
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
843
- }
844
-
845
- let predicate = HKQuery.predicateForSamples(
846
- withStart: Date.init(),
847
- end: nil,
848
- options: HKQueryOptions.strictStartDate
849
- )
850
-
851
- let queryId = UUID().uuidString
852
-
853
- func responder(
854
- query: HKObserverQuery, handler: @escaping HKObserverQueryCompletionHandler, error: Error?
855
- ) {
856
- if error == nil {
857
- DispatchQueue.main.async {
858
- if self._hasListeners {
859
- self.sendEvent(
860
- withName: "onChange",
861
- body: [
862
- "typeIdentifier": typeIdentifier
863
- ])
864
- }
865
-
866
- }
867
- handler()
868
- }
869
- }
870
-
871
- let query = HKObserverQuery(
872
- sampleType: sampleType,
873
- predicate: predicate
874
- ) {
875
- (query: HKObserverQuery, handler: @escaping HKObserverQueryCompletionHandler, error: Error?)
876
- in
877
- guard let err = error else {
878
- return responder(query: query, handler: handler, error: error)
879
- }
880
- reject(GENERIC_ERROR, err.localizedDescription, err)
881
- }
882
-
883
- store.execute(query)
884
-
885
- self._runningQueries.updateValue(query, forKey: queryId)
886
-
887
- resolve(queryId)
888
- }
889
-
890
- @objc(unsubscribeQuery:resolve:reject:)
891
- func unsubscribeQuery(
892
- queryId: String,
893
- resolve: @escaping RCTPromiseResolveBlock,
894
- reject: @escaping RCTPromiseRejectBlock
895
- ) {
896
- guard let store = _store else {
897
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
898
- }
899
-
900
- guard let query = self._runningQueries[queryId] else {
901
- reject("Error", "Query with id " + queryId + " not found", nil)
902
- return
903
- }
904
-
905
- store.stop(query)
906
-
907
- self._runningQueries.removeValue(forKey: queryId)
908
-
909
- resolve(true)
910
- }
911
-
912
- static override func requiresMainQueueSetup() -> Bool {
913
- return true
914
- }
915
-
916
- @objc(queryStatisticsForQuantity:unitString:from:to:options:resolve:reject:)
917
- func queryStatisticsForQuantity(
918
- typeIdentifier: String,
919
- unitString: String,
920
- from: Date,
921
- to: Date,
922
- options: NSArray,
923
- resolve: @escaping RCTPromiseResolveBlock,
924
- reject: @escaping RCTPromiseRejectBlock
925
- ) {
926
- guard let store = _store else {
927
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
928
- }
929
-
930
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
931
- guard let quantityType = HKObjectType.quantityType(forIdentifier: identifier) else {
932
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
933
- }
934
-
935
- let predicate = HKQuery.predicateForSamples(
936
- withStart: from,
937
- end: to,
938
- options: HKQueryOptions.strictEndDate
939
- )
940
-
941
- var opts = HKStatisticsOptions.init()
942
-
943
- for o in options {
944
- let str = o as! String
945
- if str == "cumulativeSum" {
946
- opts.insert(HKStatisticsOptions.cumulativeSum)
947
- } else if str == "discreteAverage" {
948
- opts.insert(HKStatisticsOptions.discreteAverage)
949
- } else if str == "discreteMax" {
950
- opts.insert(HKStatisticsOptions.discreteMax)
951
- } else if str == "discreteMin" {
952
- opts.insert(HKStatisticsOptions.discreteMin)
953
- }
954
- if #available(iOS 12, *) {
955
- if str == "discreteMostRecent" {
956
- opts.insert(HKStatisticsOptions.discreteMostRecent)
957
- }
958
- }
959
- if #available(iOS 13, *) {
960
- if str == "duration" {
961
- opts.insert(HKStatisticsOptions.duration)
962
- }
963
- if str == "mostRecent" {
964
- opts.insert(HKStatisticsOptions.mostRecent)
965
- }
966
- }
967
-
968
- if str == "separateBySource" {
969
- opts.insert(HKStatisticsOptions.separateBySource)
970
- }
971
- }
972
-
973
- let query = HKStatisticsQuery.init(
974
- quantityType: quantityType,
975
- quantitySamplePredicate: predicate,
976
- options: opts
977
- ) { (_, stats: HKStatistics?, _: Error?) in
978
- var dic = [String: [String: Any]?]()
979
-
980
- guard let gottenStats = stats else {
981
- return resolve(dic)
982
- }
983
-
984
- let unit = HKUnit.init(from: unitString)
985
- if let averageQuantity = gottenStats.averageQuantity() {
986
- dic.updateValue(
987
- serializeQuantity(unit: unit, quantity: averageQuantity), forKey: "averageQuantity")
988
- }
989
- if let maximumQuantity = gottenStats.maximumQuantity() {
990
- dic.updateValue(
991
- serializeQuantity(unit: unit, quantity: maximumQuantity), forKey: "maximumQuantity")
992
- }
993
- if let minimumQuantity = gottenStats.minimumQuantity() {
994
- dic.updateValue(
995
- serializeQuantity(unit: unit, quantity: minimumQuantity), forKey: "minimumQuantity")
996
- }
997
- if let sumQuantity = gottenStats.sumQuantity() {
998
- dic.updateValue(serializeQuantity(unit: unit, quantity: sumQuantity), forKey: "sumQuantity")
999
- }
1000
- if #available(iOS 12, *) {
1001
- if let mostRecent = gottenStats.mostRecentQuantity() {
1002
- dic.updateValue(
1003
- serializeQuantity(unit: unit, quantity: mostRecent), forKey: "mostRecentQuantity")
1004
- }
1005
-
1006
- if let mostRecentDateInterval = gottenStats.mostRecentQuantityDateInterval() {
1007
- dic.updateValue(
1008
- [
1009
- "start": self._dateFormatter.string(from: mostRecentDateInterval.start),
1010
- "end": self._dateFormatter.string(from: mostRecentDateInterval.end)
1011
- ], forKey: "mostRecentQuantityDateInterval")
1012
- }
1013
- }
1014
- if #available(iOS 13, *) {
1015
- let durationUnit = HKUnit.second()
1016
- dic["duration"] = serializeQuantityIfExists(
1017
- unit: durationUnit, quantity: gottenStats.duration())
1018
- }
1019
-
1020
- resolve(dic)
1021
- }
1022
-
1023
- store.execute(query)
1024
- }
1025
-
1026
- @objc(
1027
- queryStatisticsCollectionForQuantity:unitString:options:anchorDate:interval:startDate:endDate:
1028
- resolve:reject:
1029
- )
1030
- func queryStatisticsCollectionForQuantity(
1031
- typeIdentifier: String,
1032
- unitString: String,
1033
- options: NSArray,
1034
- anchorDate: Date,
1035
- interval: NSDictionary,
1036
- startDate: Date,
1037
- endDate: Date,
1038
- resolve: @escaping RCTPromiseResolveBlock,
1039
- reject: @escaping RCTPromiseRejectBlock
1040
- ) {
1041
- guard let store = _store else {
1042
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1043
- }
1044
-
1045
- guard
1046
- let quantityType = HKObjectType.quantityType(
1047
- forIdentifier: HKQuantityTypeIdentifier(rawValue: typeIdentifier))
1048
- else {
1049
- return reject(
1050
- TYPE_IDENTIFIER_ERROR,
1051
- "Failed to initialize quantity type for identifier: \(typeIdentifier)", nil)
1052
- }
1053
-
1054
- let opts = hkStatisticsOptionsFromOptions(options)
1055
- let intervalComponents = componentsFromInterval(interval)
1056
-
1057
- let query = HKStatisticsCollectionQuery(
1058
- quantityType: quantityType,
1059
- quantitySamplePredicate: nil, // We will use enumerateStatistics to filter with date
1060
- options: opts,
1061
- anchorDate: anchorDate,
1062
- intervalComponents: intervalComponents
1063
- )
1064
-
1065
- query.initialResultsHandler = { _, statsCollection, error in
1066
- if let error = error {
1067
- return reject(QUERY_ERROR, error.localizedDescription, error)
1068
- }
1069
-
1070
- var results = [[String: [String: Any]?]]()
1071
-
1072
- guard let collection = statsCollection else {
1073
- return resolve(results)
1074
- }
1075
-
1076
- let unit = HKUnit(from: unitString)
1077
-
1078
- collection.enumerateStatistics(from: startDate, to: endDate) { stats, _ in
1079
- var dic = [String: [String: Any]?]()
1080
-
1081
- let startDate = self._dateFormatter.string(from: stats.startDate)
1082
- let endDate = self._dateFormatter.string(from: stats.endDate)
1083
-
1084
- dic["averageQuantity"] = serializeQuantityIfExists(
1085
- unit: unit, quantity: stats.averageQuantity())
1086
- dic["maximumQuantity"] = serializeQuantityIfExists(
1087
- unit: unit, quantity: stats.maximumQuantity())
1088
- dic["minimumQuantity"] = serializeQuantityIfExists(
1089
- unit: unit, quantity: stats.minimumQuantity())
1090
- dic["sumQuantity"] = serializeStatisticIfExists(
1091
- unit: unit, quantity: stats.sumQuantity(), stats: stats)
1092
-
1093
- if #available(iOS 12, *) {
1094
- dic["mostRecentQuantity"] = serializeQuantityIfExists(
1095
- unit: unit, quantity: stats.mostRecentQuantity())
1096
- if let mostRecentDateInterval = stats.mostRecentQuantityDateInterval() {
1097
- dic["mostRecentQuantityDateInterval"] = [
1098
- "start": self._dateFormatter.string(from: mostRecentDateInterval.start),
1099
- "end": self._dateFormatter.string(from: mostRecentDateInterval.end)
1100
- ]
1101
- }
1102
- }
1103
-
1104
- if #available(iOS 13, *) {
1105
- let durationUnit = HKUnit.second()
1106
- dic["duration"] = serializeQuantityIfExists(
1107
- unit: durationUnit, quantity: stats.duration())
1108
- }
1109
-
1110
- results.append(dic)
1111
- }
1112
-
1113
- resolve(results)
1114
- }
1115
-
1116
- store.execute(query)
1117
- }
1118
-
1119
- func mapWorkout(
1120
- workout: HKWorkout,
1121
- distanceUnit: HKUnit,
1122
- energyUnit: HKUnit
1123
- ) -> NSMutableDictionary {
1124
- let endDate = self._dateFormatter.string(from: workout.endDate)
1125
- let startDate = self._dateFormatter.string(from: workout.startDate)
1126
-
1127
- let dict: NSMutableDictionary = [
1128
- "uuid": workout.uuid.uuidString,
1129
- "device": serializeDevice(_device: workout.device) as Any,
1130
- "duration": workout.duration,
1131
- "totalDistance": serializeQuantity(unit: distanceUnit, quantity: workout.totalDistance)
1132
- as Any,
1133
- "totalEnergyBurned": serializeQuantity(unit: energyUnit, quantity: workout.totalEnergyBurned)
1134
- as Any,
1135
- "totalSwimmingStrokeCount": serializeQuantity(
1136
- unit: HKUnit.count(), quantity: workout.totalSwimmingStrokeCount) as Any,
1137
- "workoutActivityType": workout.workoutActivityType.rawValue,
1138
- "startDate": startDate,
1139
- "endDate": endDate,
1140
- "metadata": serializeMetadata(metadata: workout.metadata),
1141
- "sourceRevision": serializeSourceRevision(_sourceRevision: workout.sourceRevision) as Any
1142
- ]
1143
-
1144
- // this is used for our laps functionality to get markers
1145
- // https://developer.apple.com/documentation/healthkit/hkworkoutevent
1146
- var eventArray: [[String: Any]] = []
1147
- if let events = workout.workoutEvents {
1148
- for event in events {
1149
- let eventStartDate = self._dateFormatter.string(from: event.dateInterval.start)
1150
- let eventEndDate = self._dateFormatter.string(from: event.dateInterval.end)
1151
- let eventDict: [String: Any] = [
1152
- "type": event.type.rawValue, // https://developer.apple.com/documentation/healthkit/hkworkouteventtype
1153
- "startDate": eventStartDate,
1154
- "endDate": eventEndDate
1155
- ]
1156
- eventArray.append(eventDict)
1157
- }
1158
- }
1159
- dict["events"] = eventArray
1160
-
1161
- // also used for our laps functionality to get activities for custom workouts defined by the user
1162
- // https://developer.apple.com/documentation/healthkit/hkworkout/1615340-init
1163
- // it seems this might be depricated in the latest beta so this might need updating!
1164
- var activitiesArray: [[String: Any]] = []
1165
- if #available(iOS 16.0, *) {
1166
- let activities: [HKWorkoutActivity] = workout.workoutActivities
1167
-
1168
- if !activities.isEmpty {
1169
- for activity in activities {
1170
- var activityStartDate = ""
1171
- var activityEndDate = ""
1172
- if let start = activity.startDate as Date? {
1173
- activityStartDate = self._dateFormatter.string(from: start)
1174
- }
1175
- if let end = activity.endDate as Date? {
1176
- activityEndDate = self._dateFormatter.string(from: end)
1177
- }
1178
- let activityDict: [String: Any] = [
1179
- "startDate": activityStartDate,
1180
- "endDate": activityEndDate,
1181
- "uuid": activity.uuid.uuidString,
1182
- "duration": activity.duration
1183
- ]
1184
- activitiesArray.append(activityDict)
1185
- }
1186
- }
1187
- }
1188
- dict["activities"] = activitiesArray
1189
-
1190
- if #available(iOS 11, *) {
1191
- dict.setValue(
1192
- serializeQuantity(unit: HKUnit.count(), quantity: workout.totalFlightsClimbed),
1193
- forKey: "totalFlightsClimbed")
1194
- }
1195
- return dict
1196
- }
1197
-
1198
- @objc(queryWorkoutSamples:distanceUnitString:from:to:limit:ascending:resolve:reject:)
1199
- func queryWorkoutSamples(
1200
- energyUnitString: String,
1201
- distanceUnitString: String,
1202
- from: Date,
1203
- to: Date,
1204
- limit: Int,
1205
- ascending: Bool,
1206
- resolve: @escaping RCTPromiseResolveBlock,
1207
- reject: @escaping RCTPromiseRejectBlock
1208
- ) {
1209
- guard let store = _store else {
1210
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1211
- }
1212
-
1213
- let from = dateOrNilIfZero(date: from)
1214
- let to = dateOrNilIfZero(date: to)
1215
-
1216
- let predicate = createPredicate(from: from, to: to)
1217
-
1218
- let limit = limitOrNilIfZero(limit: limit)
1219
-
1220
- let energyUnit = HKUnit.init(from: energyUnitString)
1221
- let distanceUnit = HKUnit.init(from: distanceUnitString)
1222
-
1223
- let q = HKSampleQuery(
1224
- sampleType: .workoutType(),
1225
- predicate: predicate,
1226
- limit: limit,
1227
- sortDescriptors: getSortDescriptors(ascending: ascending)
1228
- ) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
1229
- guard let err = error else {
1230
- guard let samples = sample else {
1231
- return resolve([])
1232
- }
1233
- let arr: NSMutableArray = []
1234
-
1235
- for s in samples {
1236
- if let workout = s as? HKWorkout {
1237
- let dict = self.mapWorkout(
1238
- workout: workout,
1239
- distanceUnit: distanceUnit,
1240
- energyUnit: energyUnit
1241
- )
1242
-
1243
- arr.add(dict)
1244
- }
1245
- }
1246
-
1247
- return resolve(arr)
1248
- }
1249
- reject(GENERIC_ERROR, err.localizedDescription, err)
1250
- }
1251
-
1252
- store.execute(q)
1253
- }
1254
-
1255
- @objc(queryWorkoutSamplesWithAnchor:distanceUnitString:from:to:limit:anchor:resolve:reject:)
1256
- func queryWorkoutSamplesWithAnchor(
1257
- energyUnitString: String,
1258
- distanceUnitString: String,
1259
- from: Date,
1260
- to: Date,
1261
- limit: Int,
1262
- anchor: String,
1263
- resolve: @escaping RCTPromiseResolveBlock,
1264
- reject: @escaping RCTPromiseRejectBlock
1265
- ) {
1266
- guard let store = _store else {
1267
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1268
- }
1269
-
1270
- let from = dateOrNilIfZero(date: from)
1271
- let to = dateOrNilIfZero(date: to)
1272
-
1273
- let predicate = createPredicate(from: from, to: to)
1274
-
1275
- let limit = limitOrNilIfZero(limit: limit)
1276
-
1277
- let energyUnit = HKUnit.init(from: energyUnitString)
1278
- let distanceUnit = HKUnit.init(from: distanceUnitString)
1279
-
1280
- let actualAnchor = deserializeHKQueryAnchor(anchor: anchor)
1281
-
1282
- let q = HKAnchoredObjectQuery(
1283
- type: .workoutType(), predicate: predicate, anchor: actualAnchor, limit: limit
1284
- ) {
1285
- (
1286
- _: HKAnchoredObjectQuery,
1287
- s: [HKSample]?,
1288
- deletedSamples: [HKDeletedObject]?,
1289
- newAnchor: HKQueryAnchor?,
1290
- error: Error?
1291
- ) in
1292
- guard let err = error else {
1293
- guard let samples = s else {
1294
- return resolve([])
1295
- }
1296
-
1297
- let arr: NSMutableArray = []
1298
-
1299
- for s in samples {
1300
- if let workout = s as? HKWorkout {
1301
- let dict = self.mapWorkout(
1302
- workout: workout,
1303
- distanceUnit: distanceUnit,
1304
- energyUnit: energyUnit
1305
- )
1306
-
1307
- arr.add(dict)
1308
- }
1309
- }
1310
-
1311
- return resolve([
1312
- "samples": arr as Any,
1313
- "deletedSamples": deletedSamples?.map({ sample in
1314
- return serializeDeletedSample(sample: sample)
1315
- }) as Any,
1316
- "newAnchor": serializeAnchor(anchor: newAnchor) as Any
1317
- ])
1318
- }
1319
- reject(GENERIC_ERROR, err.localizedDescription, err)
1320
- }
1321
-
1322
- store.execute(q)
1323
- }
1324
-
1325
- @objc(queryQuantitySamples:unitString:from:to:limit:ascending:resolve:reject:)
1326
- func queryQuantitySamples(
1327
- typeIdentifier: String,
1328
- unitString: String,
1329
- from: Date,
1330
- to: Date,
1331
- limit: Int,
1332
- ascending: Bool,
1333
- resolve: @escaping RCTPromiseResolveBlock,
1334
- reject: @escaping RCTPromiseRejectBlock
1335
- ) {
1336
- guard let store = _store else {
1337
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1338
- }
1339
-
1340
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
1341
- guard let sampleType = HKSampleType.quantityType(forIdentifier: identifier) else {
1342
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1343
- }
1344
-
1345
- let from = dateOrNilIfZero(date: from)
1346
- let to = dateOrNilIfZero(date: to)
1347
- let predicate = createPredicate(from: from, to: to)
1348
- let limit = limitOrNilIfZero(limit: limit)
1349
-
1350
- let q = HKSampleQuery(
1351
- sampleType: sampleType,
1352
- predicate: predicate,
1353
- limit: limit,
1354
- sortDescriptors: getSortDescriptors(ascending: ascending)
1355
- ) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
1356
- guard let err = error else {
1357
- guard let samples = sample else {
1358
- return resolve([])
1359
- }
1360
- let arr: NSMutableArray = []
1361
-
1362
- for s in samples {
1363
- if let sample = s as? HKQuantitySample {
1364
- let serialized = serializeQuantitySample(
1365
- sample: sample, unit: HKUnit.init(from: unitString))
1366
-
1367
- arr.add(serialized)
1368
- }
1369
- }
1370
-
1371
- return resolve(arr)
1372
- }
1373
- reject(GENERIC_ERROR, err.localizedDescription, err)
1374
- }
1375
-
1376
- store.execute(q)
1377
- }
1378
-
1379
- @objc(queryCorrelationSamples:from:to:resolve:reject:)
1380
- func queryCorrelationSamples(
1381
- typeIdentifier: String,
1382
- from: Date,
1383
- to: Date,
1384
- resolve: @escaping RCTPromiseResolveBlock,
1385
- reject: @escaping RCTPromiseRejectBlock
1386
- ) {
1387
- guard let store = _store else {
1388
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1389
- }
1390
-
1391
- let identifier = HKCorrelationTypeIdentifier.init(rawValue: typeIdentifier)
1392
- guard let sampleType = HKSampleType.correlationType(forIdentifier: identifier) else {
1393
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1394
- }
1395
-
1396
- let from = from.timeIntervalSince1970 >= 0 ? from : nil
1397
- let to = to.timeIntervalSince1970 >= 0 ? to : nil
1398
-
1399
- let predicate = createPredicate(from: from, to: to)
1400
-
1401
- let q = HKCorrelationQuery(
1402
- type: sampleType,
1403
- predicate: predicate,
1404
- samplePredicates: nil
1405
- ) { (_: HKCorrelationQuery, _correlations: [HKCorrelation]?, error: Error?) in
1406
- guard let err = error else {
1407
- guard let correlations = _correlations else {
1408
- return resolve([])
1409
- }
1410
-
1411
- var qts = Set<HKQuantityType>()
1412
- for c in correlations {
1413
- for object in c.objects {
1414
- if let quantitySample = object as? HKQuantitySample {
1415
- qts.insert(quantitySample.quantityType)
1416
- }
1417
- }
1418
- }
1419
- return store.preferredUnits(for: qts) { (map: [HKQuantityType: HKUnit], error: Error?) in
1420
- guard let e = error else {
1421
- let collerationsToReturn: NSMutableArray = []
1422
- for c in correlations {
1423
- let objects = NSMutableArray()
1424
- for o in c.objects {
1425
- if let quantitySample = o as? HKQuantitySample {
1426
- objects.add(
1427
- serializeQuantitySample(
1428
- sample: quantitySample,
1429
- unit: map[quantitySample.quantityType]!
1430
- )
1431
- )
1432
- }
1433
- if let categorySample = o as? HKCategorySample {
1434
- objects.add(serializeCategorySample(sample: categorySample))
1435
- }
1436
- }
1437
-
1438
- collerationsToReturn.add([
1439
- "uuid": c.uuid.uuidString,
1440
- "device": serializeDevice(_device: c.device) as Any,
1441
- "correlationType": c.correlationType.identifier,
1442
- "objects": objects,
1443
- "metadata": serializeMetadata(metadata: c.metadata),
1444
- "startDate": self._dateFormatter.string(from: c.startDate),
1445
- "endDate": self._dateFormatter.string(from: c.endDate)
1446
- ])
1447
- }
1448
-
1449
- return resolve(collerationsToReturn)
1450
- }
1451
- reject(GENERIC_ERROR, e.localizedDescription, e)
1452
- }
1453
- }
1454
- reject(GENERIC_ERROR, err.localizedDescription, err)
1455
- }
1456
-
1457
- store.execute(q)
1458
- }
1459
-
1460
- @objc(queryCategorySamples:from:to:limit:ascending:resolve:reject:)
1461
- func queryCategorySamples(
1462
- typeIdentifier: String,
1463
- from: Date,
1464
- to: Date,
1465
- limit: Int,
1466
- ascending: Bool,
1467
- resolve: @escaping RCTPromiseResolveBlock,
1468
- reject: @escaping RCTPromiseRejectBlock
1469
- ) {
1470
- guard let store = _store else {
1471
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1472
- }
1473
-
1474
- let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier)
1475
- guard let sampleType = HKSampleType.categoryType(forIdentifier: identifier) else {
1476
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1477
- }
1478
-
1479
- let from = dateOrNilIfZero(date: from)
1480
- let to = dateOrNilIfZero(date: to)
1481
- let predicate = createPredicate(from: from, to: to)
1482
- let limit = limitOrNilIfZero(limit: limit)
1483
-
1484
- let q = HKSampleQuery(
1485
- sampleType: sampleType, predicate: predicate, limit: limit,
1486
- sortDescriptors: getSortDescriptors(ascending: ascending)
1487
- ) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
1488
- guard let err = error else {
1489
- guard let samples = sample else {
1490
- return resolve([])
1491
- }
1492
- let arr: NSMutableArray = []
1493
-
1494
- for s in samples {
1495
- if let sample = s as? HKCategorySample {
1496
- let serialized = serializeCategorySample(sample: sample)
1497
-
1498
- arr.add(serialized)
1499
- }
1500
- }
1501
- return resolve(arr)
1502
- }
1503
-
1504
- reject(GENERIC_ERROR, err.localizedDescription, err)
1505
- }
1506
-
1507
- store.execute(q)
1508
- }
1509
-
1510
- @objc(queryQuantitySamplesWithAnchor:unitString:from:to:limit:anchor:resolve:reject:)
1511
- func queryQuantitySamplesWithAnchor(
1512
- typeIdentifier: String,
1513
- unitString: String,
1514
- from: Date,
1515
- to: Date,
1516
- limit: Int,
1517
- anchor: String,
1518
- resolve: @escaping RCTPromiseResolveBlock,
1519
- reject: @escaping RCTPromiseRejectBlock
1520
- ) {
1521
- guard let store = _store else {
1522
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1523
- }
1524
-
1525
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
1526
- guard let sampleType = HKSampleType.quantityType(forIdentifier: identifier) else {
1527
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1528
- }
1529
-
1530
- let from = dateOrNilIfZero(date: from)
1531
- let to = dateOrNilIfZero(date: to)
1532
- let predicate = createPredicate(from: from, to: to)
1533
- let limit = limitOrNilIfZero(limit: limit)
1534
-
1535
- let actualAnchor = deserializeHKQueryAnchor(anchor: anchor)
1536
-
1537
- let q = HKAnchoredObjectQuery(
1538
- type: sampleType,
1539
- predicate: predicate,
1540
- anchor: actualAnchor,
1541
- limit: limit
1542
- ) {
1543
- (
1544
- _: HKAnchoredObjectQuery,
1545
- s: [HKSample]?,
1546
- deletedSamples: [HKDeletedObject]?,
1547
- newAnchor: HKQueryAnchor?,
1548
- error: Error?
1549
- ) in
1550
- guard let err = error else {
1551
- guard let samples = s else {
1552
- return resolve([])
1553
- }
1554
-
1555
- return resolve([
1556
- "samples": samples.map({ sample in
1557
- let serialized = serializeQuantitySample(
1558
- sample: sample as! HKQuantitySample,
1559
- unit: HKUnit.init(from: unitString)
1560
- )
1561
-
1562
- return serialized
1563
- }) as Any,
1564
- "deletedSamples": deletedSamples?.map({ sample in
1565
- return serializeDeletedSample(sample: sample)
1566
- }) as Any,
1567
- "newAnchor": serializeAnchor(anchor: newAnchor) as Any
1568
- ])
1569
- }
1570
- reject(GENERIC_ERROR, err.localizedDescription, err)
1571
- }
1572
-
1573
- store.execute(q)
1574
- }
1575
-
1576
- @objc(queryCategorySamplesWithAnchor:from:to:limit:anchor:resolve:reject:)
1577
- func queryCategorySamplesWithAnchor(
1578
- typeIdentifier: String,
1579
- from: Date,
1580
- to: Date,
1581
- limit: Int,
1582
- anchor: String,
1583
- resolve: @escaping RCTPromiseResolveBlock,
1584
- reject: @escaping RCTPromiseRejectBlock
1585
- ) {
1586
- guard let store = _store else {
1587
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1588
- }
1589
-
1590
- let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier)
1591
- guard let sampleType = HKSampleType.categoryType(forIdentifier: identifier) else {
1592
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1593
- }
1594
-
1595
- let from = dateOrNilIfZero(date: from)
1596
- let to = dateOrNilIfZero(date: to)
1597
- let predicate = createPredicate(from: from, to: to)
1598
- let limit = limitOrNilIfZero(limit: limit)
1599
-
1600
- let q = HKAnchoredObjectQuery(
1601
- type: sampleType,
1602
- predicate: predicate,
1603
- anchor: anchor != "" ? base64StringToHKQueryAnchor(base64String: anchor) : nil,
1604
- limit: limit
1605
- ) {
1606
- (
1607
- _: HKAnchoredObjectQuery,
1608
- s: [HKSample]?,
1609
- deletedSamples: [HKDeletedObject]?,
1610
- newAnchor: HKQueryAnchor?,
1611
- error: Error?
1612
- ) in
1613
- guard let err = error else {
1614
- guard let samples = s else {
1615
- return resolve([])
1616
- }
1617
-
1618
- let arr: NSMutableArray = []
1619
-
1620
- for s in samples {
1621
- if let sample = s as? HKCategorySample {
1622
- let serialized = serializeCategorySample(sample: sample)
1623
-
1624
- arr.add(serialized)
1625
- }
1626
- }
1627
-
1628
- return resolve([
1629
- "samples": arr,
1630
- "deletedSamples": deletedSamples?.map({ sample in
1631
- return serializeDeletedSample(sample: sample)
1632
- }) as Any,
1633
- "newAnchor": serializeAnchor(anchor: newAnchor) as Any
1634
- ])
1635
- }
1636
- reject(GENERIC_ERROR, err.localizedDescription, err)
1637
- }
1638
-
1639
- store.execute(q)
1640
- }
1641
-
1642
- @objc(querySources:resolve:reject:)
1643
- func querySources(
1644
- typeIdentifier: String,
1645
- resolve: @escaping RCTPromiseResolveBlock,
1646
- reject: @escaping RCTPromiseRejectBlock
1647
- ) {
1648
-
1649
- guard let store = _store else {
1650
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1651
- }
1652
-
1653
- guard let type = objectTypeFromString(typeIdentifier: typeIdentifier) else {
1654
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1655
- }
1656
-
1657
- let query = HKSourceQuery(
1658
- sampleType: type as! HKSampleType,
1659
- samplePredicate: nil
1660
- ) {
1661
- (
1662
- _: HKSourceQuery,
1663
- source: Set<HKSource>?,
1664
- error: Error?
1665
- ) in
1666
- guard let err = error else {
1667
- guard let sources = source else {
1668
- return resolve([])
1669
- }
1670
- let arr: NSMutableArray = []
1671
-
1672
- for s in sources {
1673
- let serialized = serializeSource(source: s)
1674
-
1675
- arr.add(serialized)
1676
- }
1677
-
1678
- return resolve(arr)
1679
- }
1680
- reject(GENERIC_ERROR, err.localizedDescription, err)
1681
-
1682
- }
1683
-
1684
- store.execute(query)
1685
- }
1686
-
1687
- @objc(requestAuthorization:read:resolve:withRejecter:)
1688
- func requestAuthorization(
1689
- toShare: NSDictionary,
1690
- read: NSDictionary,
1691
- resolve: @escaping RCTPromiseResolveBlock,
1692
- reject: @escaping RCTPromiseRejectBlock
1693
- ) {
1694
- guard let store = _store else {
1695
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1696
- }
1697
-
1698
- let share = sampleTypesFromDictionary(typeIdentifiers: toShare)
1699
- let toRead = objectTypesFromDictionary(typeIdentifiers: read)
1700
-
1701
- store.requestAuthorization(toShare: share, read: toRead) { (success: Bool, error: Error?) in
1702
- guard let err = error else {
1703
- return resolve(success)
1704
- }
1705
- reject(GENERIC_ERROR, err.localizedDescription, err)
1706
- }
1707
- }
1708
-
1709
- @available(iOS 13.0.0, *)
1710
- func getWorkoutByID(
1711
- store: HKHealthStore,
1712
- workoutUUID: UUID
1713
- ) async -> HKWorkout? {
1714
- let workoutPredicate = HKQuery.predicateForObject(with: workoutUUID)
1715
-
1716
- let samples = try! await withCheckedThrowingContinuation {
1717
- (continuation: CheckedContinuation<[HKSample], Error>) in
1718
- let query = HKSampleQuery(
1719
- sampleType: HKObjectType.workoutType(),
1720
- predicate: workoutPredicate,
1721
- limit: 1,
1722
- sortDescriptors: nil
1723
- ) { (_, results, error) in
1724
-
1725
- if let hasError = error {
1726
- continuation.resume(throwing: hasError)
1727
- return
1728
- }
1729
-
1730
- guard let samples = results else {
1731
- fatalError("workout samples unexpectedly nil")
1732
- }
1733
-
1734
- continuation.resume(returning: samples)
1735
- }
1736
- store.execute(query)
1737
- }
1738
-
1739
- guard let workouts = samples as? [HKWorkout] else {
1740
- return nil
1741
- }
1742
-
1743
- return workouts.first ?? nil
1744
- }
1745
-
1746
- @available(iOS 13.0.0, *)
1747
- func _getWorkoutRoutes(
1748
- store: HKHealthStore,
1749
- workoutUUID: UUID
1750
- ) async -> [HKWorkoutRoute]? {
1751
- guard let workout = await getWorkoutByID(store: store, workoutUUID: workoutUUID) else {
1752
- return nil
1753
- }
1754
-
1755
- let workoutPredicate = HKQuery.predicateForObjects(from: workout)
1756
- let samples = try! await withCheckedThrowingContinuation {
1757
- (continuation: CheckedContinuation<[HKSample], Error>) in
1758
- let query = HKAnchoredObjectQuery(
1759
- type: HKSeriesType.workoutRoute(),
1760
- predicate: workoutPredicate,
1761
- anchor: nil,
1762
- limit: HKObjectQueryNoLimit
1763
- ) {
1764
- (_, samples, _, _, error) in
1765
-
1766
- if let hasError = error {
1767
- continuation.resume(throwing: hasError)
1768
- return
1769
- }
1770
-
1771
- guard let samples = samples else {
1772
- fatalError("workoutRoute samples unexpectedly nil")
1773
- }
1774
-
1775
- continuation.resume(returning: samples)
1776
- }
1777
- store.execute(query)
1778
- }
1779
-
1780
- guard let routes = samples as? [HKWorkoutRoute] else {
1781
- return nil
1782
- }
1783
-
1784
- return routes
1785
- }
1786
-
1787
- @available(iOS 13.0.0, *)
1788
- func getRouteLocations(
1789
- store: HKHealthStore,
1790
- route: HKWorkoutRoute
1791
- ) async -> [CLLocation] {
1792
- let locations = try! await withCheckedThrowingContinuation {
1793
- (continuation: CheckedContinuation<[CLLocation], Error>) in
1794
- var allLocations: [CLLocation] = []
1795
-
1796
- let query = HKWorkoutRouteQuery(route: route) {
1797
- (_, locationsOrNil, done, errorOrNil) in
1798
-
1799
- if let error = errorOrNil {
1800
- continuation.resume(throwing: error)
1801
- return
1802
- }
1803
-
1804
- guard let currentLocationBatch = locationsOrNil else {
1805
- fatalError("routeLocations unexpectedly nil")
1806
- }
1807
-
1808
- allLocations.append(contentsOf: currentLocationBatch)
1809
-
1810
- if done {
1811
- continuation.resume(returning: allLocations)
1812
- }
1813
- }
1814
-
1815
- store.execute(query)
1816
- }
1817
-
1818
- return locations
1819
- }
1820
-
1821
- @available(iOS 13.0.0, *)
1822
- func getSerializedWorkoutLocations(
1823
- store: HKHealthStore,
1824
- workoutUUID: UUID
1825
- ) async -> [[String: Any]]? {
1826
- let routes = await _getWorkoutRoutes(
1827
- store: store,
1828
- workoutUUID: workoutUUID
1829
- )
1830
-
1831
- var allRoutes: [[String: Any]] = []
1832
- guard let _routes = routes else {
1833
- return nil
1834
- }
1835
- for route in _routes {
1836
- let routeMetadata =
1837
- serializeMetadata(
1838
- metadata: route.metadata
1839
- ) as! [String: Any]
1840
- let routeCLLocations = await getRouteLocations(
1841
- store: store,
1842
- route: route
1843
- )
1844
- let routeLocations = routeCLLocations.enumerated().map {
1845
- (i, loc) in
1846
- serializeLocation(
1847
- location: loc,
1848
- previousLocation: i == 0 ? nil : routeCLLocations[i - 1]
1849
- )
1850
- }
1851
- let routeInfos: [String: Any] = ["locations": routeLocations]
1852
-
1853
- allRoutes.append(routeInfos.merging(routeMetadata) { (current, _) in current })
1854
- }
1855
- return allRoutes
1856
- }
1857
-
1858
- @available(iOS 17.0.0, *)
1859
- func getWorkoutPlan(workout: HKWorkout) async -> [String: Any]? {
1860
- #if canImport(WorkoutKit)
1861
- do {
1862
- let workoutPlan = try await workout.workoutPlan
1863
-
1864
- var dict = [String: Any]()
1865
- if (workoutPlan?.id) != nil {
1866
- dict["id"] = workoutPlan?.id.uuidString
1867
-
1868
- }
1869
- if (workoutPlan?.workout.activity) != nil {
1870
- dict["activityType"] = workoutPlan?.workout.activity.rawValue
1871
- }
1872
-
1873
- if dict.isEmpty {
1874
- return nil
1875
- }
1876
- return dict
1877
- } catch {
1878
- return nil
1879
- }
1880
- #else
1881
- return nil
1882
- #endif
1883
- }
1884
-
1885
- @objc(getWorkoutPlanById:resolve:reject:)
1886
- func getWorkoutPlanById(
1887
- workoutUUID: String,
1888
- resolve: @escaping RCTPromiseResolveBlock,
1889
- reject: @escaping RCTPromiseRejectBlock
1890
- ) {
1891
- if #available(iOS 17.0, *) {
1892
- #if canImport(WorkoutKit)
1893
- guard let store = _store else {
1894
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1895
- }
1896
-
1897
- Task {
1898
- if let uuid = UUID(uuidString: workoutUUID) {
1899
- let workout = await self.getWorkoutByID(store: store, workoutUUID: uuid)
1900
- if let workout {
1901
- let workoutPlan = await self.getWorkoutPlan(workout: workout)
1902
-
1903
- return resolve(workoutPlan)
1904
- } else {
1905
- return reject(GENERIC_ERROR, "No workout found", nil)
1906
- }
1907
- } else {
1908
- return reject(GENERIC_ERROR, "Invalid UUID", nil)
1909
- }
1910
- }
1911
- #else
1912
- return resolve(nil)
1913
- #endif
1914
- } else {
1915
- return resolve(nil)
1916
- }
1917
- }
1918
-
1919
- func serializeLocation(location: CLLocation, previousLocation: CLLocation?) -> [String: Any] {
1920
- var distance: CLLocationDistance?
1921
- if let previousLocation = previousLocation {
1922
- distance = location.distance(from: previousLocation)
1923
- } else {
1924
- distance = nil
1925
- }
1926
- return [
1927
- "longitude": location.coordinate.longitude,
1928
- "latitude": location.coordinate.latitude,
1929
- "altitude": location.altitude,
1930
- "speed": location.speed,
1931
- "timestamp": location.timestamp.timeIntervalSince1970,
1932
- "horizontalAccuracy": location.horizontalAccuracy,
1933
- "speedAccuracy": location.speedAccuracy,
1934
- "verticalAccuracy": location.verticalAccuracy,
1935
- "distance": distance as Any
1936
- ]
1937
- }
1938
-
1939
- @available(iOS 13.0.0, *)
1940
- @objc(getWorkoutRoutes:resolve:reject:)
1941
- func getWorkoutRoutes(
1942
- workoutUUID: String,
1943
- resolve: @escaping RCTPromiseResolveBlock,
1944
- reject: @escaping RCTPromiseRejectBlock
1945
- ) {
1946
-
1947
- guard let store = _store else {
1948
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1949
- }
1950
-
1951
- guard let _workoutUUID = UUID(uuidString: workoutUUID) else {
1952
- return reject("INVALID_UUID_ERROR", "Invalid UUID received", nil)
1953
- }
1954
-
1955
- Task {
1956
- do {
1957
- let locations = await getSerializedWorkoutLocations(store: store, workoutUUID: _workoutUUID)
1958
- resolve(locations)
1959
- } catch {
1960
- reject("WORKOUT_LOCATION_ERROR", "Failed to retrieve workout locations", nil)
1961
- }
1962
- }
1963
- }
1964
-
1965
- typealias HKAnchoredObjectQueryResult = (
1966
- samples: [HKSample],
1967
- deletedSamples: [HKDeletedObject]?,
1968
- newAnchor: HKQueryAnchor?
1969
- )
1970
-
1971
- @available(iOS 13.0.0, *)
1972
- func _queryHeartbeatSeriesSamplesWithAnchor(
1973
- store: HKHealthStore,
1974
- predicate: NSPredicate?,
1975
- limit: Int,
1976
- anchor: HKQueryAnchor?
1977
- ) async throws -> HKAnchoredObjectQueryResult {
1978
- let queryResult = try await withCheckedThrowingContinuation {
1979
- (continuation: CheckedContinuation<HKAnchoredObjectQueryResult, Error>) in
1980
- let query = HKAnchoredObjectQuery(
1981
- type: HKSeriesType.heartbeat(),
1982
- predicate: predicate,
1983
- anchor: anchor,
1984
- limit: limit
1985
- ) {
1986
- (
1987
- _: HKAnchoredObjectQuery,
1988
- s: [HKSample]?,
1989
- deletedSamples: [HKDeletedObject]?,
1990
- newAnchor: HKQueryAnchor?,
1991
- error: Error?
1992
- ) in
1993
- if let err = error {
1994
- return continuation.resume(throwing: err)
1995
- } else {
1996
- guard let samples = s else {
1997
- fatalError("heartbeatSeries unexpectedly nil")
1998
- }
1999
-
2000
- continuation.resume(
2001
- returning: HKAnchoredObjectQueryResult(
2002
- samples: samples,
2003
- deletedSamples: deletedSamples,
2004
- newAnchor: newAnchor)
2005
- )
2006
- }
2007
- }
2008
-
2009
- store.execute(query)
2010
- }
2011
-
2012
- return queryResult
2013
- }
2014
-
2015
- @available(iOS 13.0.0, *)
2016
- func getHeartbeatSeriesHeartbeats(
2017
- store: HKHealthStore,
2018
- sample: HKHeartbeatSeriesSample
2019
- ) async throws -> [[String: Any]] {
2020
- let beatTimes = try await withCheckedThrowingContinuation {
2021
- (continuation: CheckedContinuation<[[String: Any]], Error>) in
2022
- var allBeats: [[String: Any]] = []
2023
-
2024
- let query = HKHeartbeatSeriesQuery(heartbeatSeries: sample) {
2025
- (
2026
- _: HKHeartbeatSeriesQuery,
2027
- timeSinceSeriesStart: TimeInterval,
2028
- precededByGap: Bool,
2029
- done: Bool,
2030
- error: Error?
2031
- ) in
2032
- if let err = error {
2033
- return continuation.resume(throwing: err)
2034
- } else {
2035
- let timeDict: [String: Any] = [
2036
- "timeSinceSeriesStart": timeSinceSeriesStart,
2037
- "precededByGap": precededByGap
2038
- ]
2039
-
2040
- allBeats.append(timeDict)
2041
-
2042
- if done {
2043
- continuation.resume(returning: allBeats)
2044
- }
2045
- }
2046
- }
2047
-
2048
- store.execute(query)
2049
- }
2050
-
2051
- return beatTimes
2052
- }
2053
-
2054
- @available(iOS 13.0.0, *)
2055
- func getSerializedHeartbeatSeriesSample(
2056
- store: HKHealthStore,
2057
- sample: HKHeartbeatSeriesSample
2058
- ) async throws -> [String: Any] {
2059
- let sampleMetadata = serializeMetadata(metadata: sample.metadata) as! [String: Any]
2060
- let sampleHeartbeats = try await getHeartbeatSeriesHeartbeats(
2061
- store: store,
2062
- sample: sample
2063
- )
2064
-
2065
- return [
2066
- "uuid": sample.uuid.uuidString,
2067
- "device": serializeDevice(_device: sample.device) as Any,
2068
- "startDate": self._dateFormatter.string(from: sample.startDate),
2069
- "endDate": self._dateFormatter.string(from: sample.endDate),
2070
- "heartbeats": sampleHeartbeats as Any,
2071
- "metadata": serializeMetadata(metadata: sample.metadata),
2072
- "sourceRevision": serializeSourceRevision(_sourceRevision: sample.sourceRevision) as Any
2073
- ]
2074
- }
2075
-
2076
- @available(iOS 13.0.0, *)
2077
- @objc(queryHeartbeatSeriesSamplesWithAnchor:to:limit:anchor:resolve:reject:)
2078
- func queryHeartbeatSeriesSamplesWithAnchor(
2079
- from: Date,
2080
- to: Date,
2081
- limit: Int,
2082
- anchor: String,
2083
- resolve: @escaping RCTPromiseResolveBlock,
2084
- reject: @escaping RCTPromiseRejectBlock
2085
- ) {
2086
- guard let store = _store else {
2087
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
2088
- }
2089
-
2090
- Task {
2091
- do {
2092
- let from = dateOrNilIfZero(date: from)
2093
- let to = dateOrNilIfZero(date: to)
2094
-
2095
- let predicate = createPredicate(from: from, to: to)
2096
-
2097
- let limit = limitOrNilIfZero(limit: limit)
2098
-
2099
- let actualAnchor = deserializeHKQueryAnchor(anchor: anchor)
2100
-
2101
- let queryResult = try await _queryHeartbeatSeriesSamplesWithAnchor(
2102
- store: store,
2103
- predicate: predicate,
2104
- limit: limit,
2105
- anchor: actualAnchor
2106
- )
2107
-
2108
- var allHeartbeatSamples: [[String: Any]] = []
2109
- for sample in queryResult.samples as! [HKHeartbeatSeriesSample] {
2110
- allHeartbeatSamples.append(
2111
- try await getSerializedHeartbeatSeriesSample(
2112
- store: store,
2113
- sample: sample
2114
- )
2115
- )
2116
- }
2117
-
2118
- resolve([
2119
- "samples": allHeartbeatSamples as Any,
2120
- "deletedSamples": queryResult.deletedSamples?.map({ sample in
2121
- return serializeDeletedSample(sample: sample)
2122
- }) as Any,
2123
- "newAnchor": serializeAnchor(anchor: queryResult.newAnchor) as Any
2124
- ])
2125
- } catch {
2126
- reject(GENERIC_ERROR, error.localizedDescription, error)
2127
- }
2128
- }
2129
- }
2130
-
2131
- @available(iOS 13.0.0, *)
2132
- func _queryHeartbeatSeriesSamples(
2133
- store: HKHealthStore,
2134
- predicate: NSPredicate?,
2135
- limit: Int,
2136
- ascending: Bool
2137
- ) async throws -> [HKSample] {
2138
- let samples = try await withCheckedThrowingContinuation {
2139
- (continuation: CheckedContinuation<[HKSample], Error>) in
2140
-
2141
- let query = HKSampleQuery(
2142
- sampleType: HKSeriesType.heartbeat(),
2143
- predicate: predicate,
2144
- limit: limit,
2145
- sortDescriptors: [
2146
- NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: ascending)
2147
- ]
2148
- ) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
2149
- if let err = error {
2150
- continuation.resume(throwing: err)
2151
- } else {
2152
- guard let actualSamples = sample else {
2153
- fatalError("heartbeatSeries samples unexpectedly nil")
2154
- }
2155
- continuation.resume(returning: actualSamples)
2156
- }
2157
- }
2158
-
2159
- store.execute(query)
2160
- }
2161
-
2162
- return samples
2163
- }
2164
-
2165
- @available(iOS 13.0.0, *)
2166
- @objc(queryHeartbeatSeriesSamples:to:limit:ascending:resolve:reject:)
2167
- func queryHeartbeatSeriesSamples(
2168
- from: Date,
2169
- to: Date,
2170
- limit: Int,
2171
- ascending: Bool,
2172
- resolve: @escaping RCTPromiseResolveBlock,
2173
- reject: @escaping RCTPromiseRejectBlock
2174
- ) {
2175
- guard let store = _store else {
2176
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
2177
- }
2178
-
2179
- Task {
2180
- do {
2181
- let from = dateOrNilIfZero(date: from)
2182
- let to = dateOrNilIfZero(date: to)
2183
-
2184
- let predicate = createPredicate(
2185
- from: from,
2186
- to: to
2187
- )
2188
-
2189
- let limit = limitOrNilIfZero(
2190
- limit: limit
2191
- )
2192
-
2193
- let samples = try await _queryHeartbeatSeriesSamples(
2194
- store: store,
2195
- predicate: predicate,
2196
- limit: limit,
2197
- ascending: ascending
2198
- )
2199
-
2200
- var allHeartbeatSamples: [[String: Any]] = []
2201
- for sample in samples as! [HKHeartbeatSeriesSample] {
2202
- allHeartbeatSamples.append(
2203
- try await getSerializedHeartbeatSeriesSample(
2204
- store: store,
2205
- sample: sample
2206
- )
2207
- )
2208
- }
2209
-
2210
- resolve(allHeartbeatSamples as Any)
2211
- } catch {
2212
- reject(GENERIC_ERROR, error.localizedDescription, error)
2213
- }
2214
- }
2215
- }
2216
-
2217
- @objc(queryStateOfMindSamples:to:limit:ascending:resolve:reject:)
2218
- func queryStateOfMindSamples(
2219
- from: Date,
2220
- to: Date,
2221
- limit: Int,
2222
- ascending: Bool,
2223
- resolve: @escaping RCTPromiseResolveBlock,
2224
- reject: @escaping RCTPromiseRejectBlock
2225
- ) {
2226
- #if compiler(>=6)
2227
- if #available(iOS 18.0, *) {
2228
- guard let store = _store else {
2229
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
2230
- }
2231
-
2232
- Task {
2233
- let from = dateOrNilIfZero(date: from)
2234
- let to = dateOrNilIfZero(date: to)
2235
-
2236
- let predicate = createPredicate(
2237
- from: from,
2238
- to: to
2239
- )
2240
-
2241
- let limit = limitOrNilIfZero(
2242
- limit: limit
2243
- )
2244
-
2245
- let type = HKStateOfMindType.stateOfMindType()
2246
-
2247
- let query = HKSampleQuery(
2248
- sampleType: type,
2249
- predicate: predicate,
2250
- limit: limit,
2251
- sortDescriptors: [
2252
- NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: ascending)
2253
- ]
2254
- ) { (_, samples, error) in
2255
- if let error = error {
2256
- reject(GENERIC_ERROR, error.localizedDescription, error)
2257
- return
2258
- }
2259
-
2260
- guard let samples = samples as? [HKStateOfMind] else {
2261
- resolve([])
2262
- return
2263
- }
2264
-
2265
- let serializedSamples = samples.map { sample in
2266
- var associations: [Int] = []
2267
- if #available(iOS 18.0, *) {
2268
- associations = sample.associations.map({ association in
2269
- return association.rawValue
2270
- })
2271
- }
2272
-
2273
- var labels: [Int] = []
2274
- if #available(iOS 18.0, *) {
2275
- labels = sample.labels.map({ label in
2276
- return label.rawValue
2277
- })
2278
- }
2279
-
2280
- return [
2281
- "uuid": sample.uuid.uuidString,
2282
- "device": serializeDevice(_device: sample.device) as Any,
2283
- "startDate": self._dateFormatter.string(from: sample.startDate),
2284
- "endDate": self._dateFormatter.string(from: sample.endDate),
2285
- "valence": sample.valence,
2286
- "kind": sample.kind.rawValue,
2287
- "valenceClassification": sample.valenceClassification.rawValue,
2288
- "associations": associations,
2289
- "labels": labels,
2290
- "metadata": serializeMetadata(metadata: sample.metadata),
2291
- "sourceRevision": serializeSourceRevision(_sourceRevision: sample.sourceRevision)
2292
- as Any
2293
- ]
2294
- }
2295
-
2296
- resolve(serializedSamples)
2297
- }
2298
-
2299
- store.execute(query)
2300
- }
2301
- } else {
2302
- reject("STATE_OF_MIND_ERROR", "State of Mind features require iOS 18.0 or later", nil)
2303
- }
2304
- #else
2305
- reject(
2306
- "STATE_OF_MIND_ERROR", "State of Mind features require Xcode 16 or later to compile", nil)
2307
- #endif
2308
- }
2309
-
2310
- @available(iOS 17.0.0, *)
2311
- @objc(startWatchAppWithWorkoutConfiguration:resolve:reject:)
2312
- func startWatchAppWithWorkoutConfiguration(
2313
- _ workoutConfiguration: NSDictionary,
2314
- resolve: @escaping RCTPromiseResolveBlock,
2315
- reject: @escaping RCTPromiseRejectBlock
2316
- ) {
2317
- guard let store = _store else {
2318
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
2319
- }
2320
-
2321
- let configuration = parseWorkoutConfiguration(workoutConfiguration)
2322
-
2323
- store.startWatchApp(with: configuration) { success, error in
2324
- if let error {
2325
- reject(GENERIC_ERROR, error.localizedDescription, error)
2326
- return
2327
- }
2328
-
2329
- resolve(success)
2330
- }
2331
- }
2332
- }
2333
-