@kingstinct/react-native-healthkit 12.1.2 → 12.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/CategoryTypeModule.swift +49 -97
- package/ios/CharacteristicTypeModule.swift +77 -63
- package/ios/CoreModule.swift +324 -280
- package/ios/CorrelationTypeModule.swift +192 -144
- package/ios/ElectrocardiogramModule.swift +185 -194
- package/ios/HeartbeatSeriesModule.swift +123 -171
- package/ios/Helpers.swift +312 -571
- package/ios/MedicationModule.swift +259 -0
- package/ios/PredicateHelpers.swift +334 -0
- package/ios/QuantityTypeModule.swift +297 -378
- package/ios/Serializers.swift +273 -210
- package/ios/SourceProxy.swift +2 -2
- package/ios/StateOfMindModule.swift +179 -125
- package/ios/WorkoutProxy.swift +235 -112
- package/ios/WorkoutsModule.swift +214 -262
- package/lib/commonjs/healthkit.ios.js +22 -2
- package/lib/commonjs/healthkit.js +35 -5
- package/lib/commonjs/hooks/useStatisticsForQuantity.js +1 -1
- package/lib/commonjs/hooks/useSubscribeToCategorySamples.js +20 -0
- package/lib/commonjs/modules.js +2 -1
- package/lib/commonjs/specs/MedicationModule.nitro.js +27 -0
- package/lib/commonjs/types/Constants.js +2 -1
- package/lib/commonjs/types/QuantityType.js +8 -1
- package/lib/commonjs/types/QueryOptions.js +18 -0
- package/lib/commonjs/types/WeatherCondition.js +32 -32
- package/lib/commonjs/types/Workouts.js +1 -50
- package/lib/commonjs/utils/getCategorySampleById.js +1 -1
- package/lib/commonjs/utils/getQuantitySampleById.js +1 -1
- package/lib/commonjs/utils/getWorkoutById.js +1 -1
- package/lib/commonjs/utils/subscribeToCategorySamples.js +29 -0
- package/lib/commonjs/utils/subscribeToQuantitySamples.js +8 -25
- package/lib/module/healthkit.ios.js +20 -2
- package/lib/module/healthkit.js +32 -2
- package/lib/module/hooks/useStatisticsForQuantity.js +1 -1
- package/lib/module/hooks/useSubscribeToCategorySamples.js +17 -0
- package/lib/module/modules.js +1 -0
- package/lib/module/specs/MedicationModule.nitro.js +26 -0
- package/lib/module/types/Constants.js +1 -0
- package/lib/module/types/QuantityType.js +7 -0
- package/lib/module/types/QueryOptions.js +17 -1
- package/lib/module/types/WeatherCondition.js +31 -31
- package/lib/module/types/Workouts.js +0 -49
- package/lib/module/utils/getCategorySampleById.js +1 -1
- package/lib/module/utils/getQuantitySampleById.js +1 -1
- package/lib/module/utils/getWorkoutById.js +1 -1
- package/lib/module/utils/subscribeToCategorySamples.js +26 -0
- package/lib/module/utils/subscribeToQuantitySamples.js +8 -25
- package/lib/typescript/healthkit.d.ts +18 -9
- package/lib/typescript/healthkit.ios.d.ts +33 -15
- package/lib/typescript/hooks/useSubscribeToCategorySamples.d.ts +3 -0
- package/lib/typescript/modules.d.ts +2 -0
- package/lib/typescript/specs/CategoryTypeModule.nitro.d.ts +2 -2
- package/lib/typescript/specs/CoreModule.nitro.d.ts +2 -1
- package/lib/typescript/specs/CorrelationTypeModule.nitro.d.ts +4 -2
- package/lib/typescript/specs/ElectrocardiogramModule.nitro.d.ts +1 -1
- package/lib/typescript/specs/HeartbeatSeriesModule.nitro.d.ts +1 -1
- package/lib/typescript/specs/MedicationModule.nitro.d.ts +56 -0
- package/lib/typescript/specs/QuantityTypeModule.nitro.d.ts +4 -4
- package/lib/typescript/specs/StateOfMindModule.nitro.d.ts +4 -3
- package/lib/typescript/types/CategoryType.d.ts +10 -20
- package/lib/typescript/types/Constants.d.ts +1 -0
- package/lib/typescript/types/CorrelationType.d.ts +8 -10
- package/lib/typescript/types/ElectrocardiogramSample.d.ts +2 -12
- package/lib/typescript/types/HeartbeatSeries.d.ts +2 -14
- package/lib/typescript/types/QuantitySample.d.ts +2 -8
- package/lib/typescript/types/QuantityType.d.ts +7 -8
- package/lib/typescript/types/QuantityTypeIdentifier.d.ts +23 -23
- package/lib/typescript/types/QueryOptions.d.ts +43 -28
- package/lib/typescript/types/Shared.d.ts +52 -7
- package/lib/typescript/types/StateOfMind.d.ts +7 -10
- package/lib/typescript/types/Subscriptions.d.ts +12 -3
- package/lib/typescript/types/WeatherCondition.d.ts +1 -1
- package/lib/typescript/types/Workouts.d.ts +28 -81
- package/lib/typescript/utils/subscribeToCategorySamples.d.ts +5 -0
- package/nitrogen/generated/ios/ReactNativeHealthkit-Swift-Cxx-Bridge.cpp +57 -0
- package/nitrogen/generated/ios/ReactNativeHealthkit-Swift-Cxx-Bridge.hpp +592 -389
- package/nitrogen/generated/ios/ReactNativeHealthkit-Swift-Cxx-Umbrella.hpp +65 -30
- package/nitrogen/generated/ios/ReactNativeHealthkitAutolinking.mm +8 -0
- package/nitrogen/generated/ios/ReactNativeHealthkitAutolinking.swift +15 -0
- package/nitrogen/generated/ios/c++/HybridCategoryTypeModuleSpecSwift.hpp +32 -26
- package/nitrogen/generated/ios/c++/HybridCoreModuleSpecSwift.hpp +36 -37
- package/nitrogen/generated/ios/c++/HybridCorrelationTypeModuleSpecSwift.hpp +55 -2
- package/nitrogen/generated/ios/c++/HybridElectrocardiogramModuleSpecSwift.hpp +36 -30
- package/nitrogen/generated/ios/c++/HybridHeartbeatSeriesModuleSpecSwift.hpp +35 -29
- package/nitrogen/generated/ios/c++/HybridMedicationModuleSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridMedicationModuleSpecSwift.hpp +181 -0
- package/nitrogen/generated/ios/c++/HybridQuantityTypeModuleSpecSwift.hpp +48 -42
- package/nitrogen/generated/ios/c++/HybridStateOfMindModuleSpecSwift.hpp +59 -36
- package/nitrogen/generated/ios/c++/HybridWorkoutProxySpecSwift.hpp +150 -29
- package/nitrogen/generated/ios/c++/HybridWorkoutsModuleSpecSwift.hpp +13 -28
- package/nitrogen/generated/ios/swift/AggregationStyle.swift +48 -0
- package/nitrogen/generated/ios/swift/CategorySample.swift +571 -24
- package/nitrogen/generated/ios/swift/ComparisonPredicateOperator.swift +2 -2
- package/nitrogen/generated/ios/swift/CorrelationSample.swift +640 -17
- package/nitrogen/generated/ios/swift/{PredicateWithStartAndEnd.swift → DateFilter.swift} +5 -5
- package/nitrogen/generated/ios/swift/ECGQueryOptionsWithAnchor.swift +11 -91
- package/nitrogen/generated/ios/swift/ECGQueryOptionsWithSortOrder.swift +11 -91
- package/nitrogen/generated/ios/swift/ElectrocardiogramSample.swift +570 -72
- package/nitrogen/generated/ios/swift/FilterForSamples.swift +349 -12
- package/nitrogen/generated/ios/swift/FilterForSamplesBase.swift +234 -0
- package/nitrogen/generated/ios/swift/FilterForWorkouts.swift +366 -0
- package/nitrogen/generated/ios/swift/FilterForWorkoutsBase.swift +240 -0
- package/nitrogen/generated/ios/swift/Func_void_MedicationDoseEventsWithAnchorResponse.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_QueryCorrelationSamplesWithAnchorResponse.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_StateOfMindSamplesWithAnchorResponse.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_MedicationDoseEvent_.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_UserAnnotatedMedication_.swift +47 -0
- package/nitrogen/generated/ios/swift/GeneralForm.swift +104 -0
- package/nitrogen/generated/ios/swift/HeartRateMotionContext.swift +44 -0
- package/nitrogen/generated/ios/swift/HeartbeatSeriesSample.swift +565 -37
- package/nitrogen/generated/ios/swift/HybridCategoryTypeModuleSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridCategoryTypeModuleSpec_cxx.swift +2 -2
- package/nitrogen/generated/ios/swift/HybridCoreModuleSpec.swift +2 -1
- package/nitrogen/generated/ios/swift/HybridCoreModuleSpec_cxx.swift +19 -31
- package/nitrogen/generated/ios/swift/HybridCorrelationTypeModuleSpec.swift +2 -1
- package/nitrogen/generated/ios/swift/HybridCorrelationTypeModuleSpec_cxx.swift +21 -2
- package/nitrogen/generated/ios/swift/HybridElectrocardiogramModuleSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridElectrocardiogramModuleSpec_cxx.swift +2 -2
- package/nitrogen/generated/ios/swift/HybridHeartbeatSeriesModuleSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridHeartbeatSeriesModuleSpec_cxx.swift +2 -2
- package/nitrogen/generated/ios/swift/HybridMedicationModuleSpec.swift +60 -0
- package/nitrogen/generated/ios/swift/HybridMedicationModuleSpec_cxx.swift +208 -0
- package/nitrogen/generated/ios/swift/HybridQuantityTypeModuleSpec.swift +2 -2
- package/nitrogen/generated/ios/swift/HybridQuantityTypeModuleSpec_cxx.swift +9 -43
- package/nitrogen/generated/ios/swift/HybridStateOfMindModuleSpec.swift +2 -1
- package/nitrogen/generated/ios/swift/HybridStateOfMindModuleSpec_cxx.swift +21 -2
- package/nitrogen/generated/ios/swift/HybridWorkoutProxySpec.swift +34 -6
- package/nitrogen/generated/ios/swift/HybridWorkoutProxySpec_cxx.swift +376 -36
- package/nitrogen/generated/ios/swift/InsulinDeliveryReason.swift +40 -0
- package/nitrogen/generated/ios/swift/MedicationConcept.swift +80 -0
- package/nitrogen/generated/ios/swift/MedicationDoseEvent.swift +781 -0
- package/nitrogen/generated/ios/swift/MedicationDoseEventLogStatus.swift +56 -0
- package/nitrogen/generated/ios/swift/MedicationDoseEventScheduleType.swift +40 -0
- package/nitrogen/generated/ios/swift/MedicationDoseEventsWithAnchorResponse.swift +81 -0
- package/nitrogen/generated/ios/swift/ObjectTypeIdentifier.swift +16 -16
- package/nitrogen/generated/ios/swift/PredicateWithMetadataKey.swift +7 -7
- package/nitrogen/generated/ios/swift/QuantitySample.swift +574 -27
- package/nitrogen/generated/ios/swift/QuantityTypeIdentifier.swift +16 -16
- package/nitrogen/generated/ios/swift/QueryCorrelationSamplesWithAnchorResponse.swift +81 -0
- package/nitrogen/generated/ios/swift/QueryOptionsWithAnchor.swift +11 -91
- package/nitrogen/generated/ios/swift/QueryOptionsWithAnchorAndUnit.swift +11 -91
- package/nitrogen/generated/ios/swift/QueryOptionsWithSortOrder.swift +11 -91
- package/nitrogen/generated/ios/swift/QueryOptionsWithSortOrderAndUnit.swift +11 -91
- package/nitrogen/generated/ios/swift/RelatedCoding.swift +76 -0
- package/nitrogen/generated/ios/swift/SampleType.swift +68 -0
- package/nitrogen/generated/ios/swift/SampleTypeIdentifier.swift +16 -16
- package/nitrogen/generated/ios/swift/SampleTypeIdentifierWriteable.swift +0 -16
- package/nitrogen/generated/ios/swift/StateOfMindSample.swift +586 -58
- package/nitrogen/generated/ios/swift/StateOfMindSamplesWithAnchorResponse.swift +81 -0
- package/nitrogen/generated/ios/swift/StatisticsQueryOptions.swift +7 -75
- package/nitrogen/generated/ios/swift/UserAnnotatedMedication.swift +87 -0
- package/nitrogen/generated/ios/swift/WeatherCondition.swift +144 -0
- package/nitrogen/generated/ios/swift/WorkoutQueryOptions.swift +11 -105
- package/nitrogen/generated/ios/swift/WorkoutQueryOptionsWithAnchor.swift +11 -105
- package/nitrogen/generated/ios/swift/WorkoutSample.swift +751 -78
- package/nitrogen/generated/shared/c++/AggregationStyle.hpp +64 -0
- package/nitrogen/generated/shared/c++/CategorySample.hpp +126 -23
- package/nitrogen/generated/shared/c++/CorrelationSample.hpp +134 -12
- package/nitrogen/generated/shared/c++/{PredicateWithStartAndEnd.hpp → DateFilter.hpp} +10 -10
- package/nitrogen/generated/shared/c++/ECGQueryOptionsWithAnchor.hpp +12 -31
- package/nitrogen/generated/shared/c++/ECGQueryOptionsWithSortOrder.hpp +12 -31
- package/nitrogen/generated/shared/c++/ElectrocardiogramSample.hpp +135 -36
- package/nitrogen/generated/shared/c++/FilterForSamples.hpp +124 -0
- package/nitrogen/generated/shared/c++/FilterForSamplesBase.hpp +109 -0
- package/nitrogen/generated/shared/c++/FilterForWorkouts.hpp +131 -0
- package/nitrogen/generated/shared/c++/FilterForWorkoutsBase.hpp +116 -0
- package/nitrogen/generated/shared/c++/GeneralForm.hpp +140 -0
- package/nitrogen/generated/shared/c++/HeartRateMotionContext.hpp +67 -0
- package/nitrogen/generated/shared/c++/HeartbeatSeriesSample.hpp +131 -28
- package/nitrogen/generated/shared/c++/HybridCategoryTypeModuleSpec.hpp +1 -2
- package/nitrogen/generated/shared/c++/HybridCoreModuleSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridCoreModuleSpec.hpp +10 -28
- package/nitrogen/generated/shared/c++/HybridCorrelationTypeModuleSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridCorrelationTypeModuleSpec.hpp +11 -1
- package/nitrogen/generated/shared/c++/HybridElectrocardiogramModuleSpec.hpp +1 -2
- package/nitrogen/generated/shared/c++/HybridHeartbeatSeriesModuleSpec.hpp +1 -2
- package/nitrogen/generated/shared/c++/HybridMedicationModuleSpec.cpp +24 -0
- package/nitrogen/generated/shared/c++/HybridMedicationModuleSpec.hpp +80 -0
- package/nitrogen/generated/shared/c++/HybridQuantityTypeModuleSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridQuantityTypeModuleSpec.hpp +6 -25
- package/nitrogen/generated/shared/c++/HybridStateOfMindModuleSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridStateOfMindModuleSpec.hpp +9 -2
- package/nitrogen/generated/shared/c++/HybridWorkoutProxySpec.cpp +33 -5
- package/nitrogen/generated/shared/c++/HybridWorkoutProxySpec.hpp +56 -16
- package/nitrogen/generated/shared/c++/InsulinDeliveryReason.hpp +62 -0
- package/nitrogen/generated/shared/c++/MedicationConcept.hpp +93 -0
- package/nitrogen/generated/shared/c++/MedicationDoseEvent.hpp +240 -0
- package/nitrogen/generated/shared/c++/MedicationDoseEventLogStatus.hpp +66 -0
- package/nitrogen/generated/shared/c++/MedicationDoseEventScheduleType.hpp +62 -0
- package/nitrogen/generated/shared/c++/MedicationDoseEventsWithAnchorResponse.hpp +89 -0
- package/nitrogen/generated/shared/c++/ObjectTypeIdentifier.hpp +105 -105
- package/nitrogen/generated/shared/c++/PredicateWithMetadataKey.hpp +8 -8
- package/nitrogen/generated/shared/c++/QuantitySample.hpp +130 -27
- package/nitrogen/generated/shared/c++/QuantityTypeIdentifier.hpp +105 -105
- package/nitrogen/generated/shared/c++/QueryCorrelationSamplesWithAnchorResponse.hpp +89 -0
- package/nitrogen/generated/shared/c++/QueryOptionsWithAnchor.hpp +12 -31
- package/nitrogen/generated/shared/c++/QueryOptionsWithAnchorAndUnit.hpp +12 -31
- package/nitrogen/generated/shared/c++/QueryOptionsWithSortOrder.hpp +12 -31
- package/nitrogen/generated/shared/c++/QueryOptionsWithSortOrderAndUnit.hpp +12 -31
- package/nitrogen/generated/shared/c++/RelatedCoding.hpp +84 -0
- package/nitrogen/generated/shared/c++/SampleType.hpp +87 -0
- package/nitrogen/generated/shared/c++/SampleTypeIdentifier.hpp +105 -105
- package/nitrogen/generated/shared/c++/SampleTypeIdentifierWriteable.hpp +105 -121
- package/nitrogen/generated/shared/c++/StateOfMindSample.hpp +139 -36
- package/nitrogen/generated/shared/c++/StateOfMindSamplesWithAnchorResponse.hpp +89 -0
- package/nitrogen/generated/shared/c++/StatisticsQueryOptions.hpp +8 -27
- package/nitrogen/generated/shared/c++/UserAnnotatedMedication.hpp +90 -0
- package/nitrogen/generated/shared/c++/WeatherCondition.hpp +88 -0
- package/nitrogen/generated/shared/c++/WorkoutQueryOptions.hpp +12 -37
- package/nitrogen/generated/shared/c++/WorkoutQueryOptionsWithAnchor.hpp +12 -37
- package/nitrogen/generated/shared/c++/WorkoutSample.hpp +159 -35
- package/package.json +1 -1
- package/src/healthkit.ios.ts +30 -0
- package/src/healthkit.ts +68 -3
- package/src/hooks/useStatisticsForQuantity.ts +1 -1
- package/src/hooks/useSubscribeToCategorySamples.ts +31 -0
- package/src/modules.ts +4 -0
- package/src/specs/CategoryTypeModule.nitro.ts +2 -2
- package/src/specs/CoreModule.nitro.ts +3 -0
- package/src/specs/CorrelationTypeModule.nitro.ts +11 -3
- package/src/specs/ElectrocardiogramModule.nitro.ts +1 -1
- package/src/specs/HeartbeatSeriesModule.nitro.ts +1 -1
- package/src/specs/MedicationModule.nitro.ts +140 -0
- package/src/specs/QuantityTypeModule.nitro.ts +4 -7
- package/src/specs/StateOfMindModule.nitro.ts +10 -2
- package/src/types/CategoryType.ts +15 -22
- package/src/types/Constants.ts +3 -0
- package/src/types/CorrelationType.ts +10 -15
- package/src/types/ElectrocardiogramSample.ts +2 -14
- package/src/types/HeartbeatSeries.ts +2 -15
- package/src/types/QuantitySample.ts +2 -8
- package/src/types/QuantityType.ts +8 -17
- package/src/types/QuantityTypeIdentifier.ts +25 -25
- package/src/types/QueryOptions.ts +54 -43
- package/src/types/Shared.ts +74 -17
- package/src/types/StateOfMind.ts +8 -10
- package/src/types/Subscriptions.ts +19 -3
- package/src/types/WeatherCondition.ts +1 -1
- package/src/types/Workouts.ts +28 -91
- package/src/utils/getCategorySampleById.ts +1 -1
- package/src/utils/getQuantitySampleById.ts +1 -1
- package/src/utils/getWorkoutById.ts +1 -2
- package/src/utils/subscribeToCategorySamples.ts +38 -0
- package/src/utils/subscribeToQuantitySamples.ts +12 -37
- package/nitrogen/generated/ios/swift/FilterForSamplesAnd.swift +0 -94
- package/nitrogen/generated/ios/swift/FilterForSamplesOr.swift +0 -94
- package/nitrogen/generated/ios/swift/PredicateForSamples.swift +0 -21
- package/nitrogen/generated/ios/swift/PredicateForWorkouts.swift +0 -23
- package/nitrogen/generated/ios/swift/PredicateForWorkoutsAnd.swift +0 -108
- package/nitrogen/generated/ios/swift/PredicateForWorkoutsOr.swift +0 -108
- package/nitrogen/generated/ios/swift/PredicateFromWorkout.swift +0 -45
- package/nitrogen/generated/ios/swift/PredicateWithMetadataOperator.swift +0 -48
- package/nitrogen/generated/ios/swift/PredicateWithUUID.swift +0 -35
- package/nitrogen/generated/ios/swift/PredicateWithUUIDs.swift +0 -47
- package/nitrogen/generated/ios/swift/Variant_PredicateWithUUID_PredicateWithUUIDs_PredicateWithMetadataKey_PredicateWithStartAndEnd_PredicateFromWorkout_FilterForSamplesAnd_FilterForSamplesOr.swift +0 -23
- package/nitrogen/generated/ios/swift/Variant_PredicateWithUUID_PredicateWithUUIDs_PredicateWithMetadataKey_PredicateWithStartAndEnd_PredicateFromWorkout_WorkoutActivityTypePredicate_WorkoutDurationPredicate_PredicateForWorkoutsOr_PredicateForWorkoutsAnd.swift +0 -25
- package/nitrogen/generated/ios/swift/WorkoutActivityTypePredicate.swift +0 -35
- package/nitrogen/generated/shared/c++/FilterForSamplesAnd.hpp +0 -90
- package/nitrogen/generated/shared/c++/FilterForSamplesOr.hpp +0 -90
- package/nitrogen/generated/shared/c++/PredicateForWorkoutsAnd.hpp +0 -96
- package/nitrogen/generated/shared/c++/PredicateForWorkoutsOr.hpp +0 -96
- package/nitrogen/generated/shared/c++/PredicateFromWorkout.hpp +0 -77
- package/nitrogen/generated/shared/c++/PredicateWithMetadataOperator.hpp +0 -84
- package/nitrogen/generated/shared/c++/PredicateWithUUID.hpp +0 -75
- package/nitrogen/generated/shared/c++/PredicateWithUUIDs.hpp +0 -76
- package/nitrogen/generated/shared/c++/WorkoutActivityTypePredicate.hpp +0 -76
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
//
|
|
2
|
+
// MedicationModule.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Robert Herber on 2025-12-10.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import HealthKit
|
|
9
|
+
import NitroModules
|
|
10
|
+
|
|
11
|
+
#if compiler(>=6.2)
|
|
12
|
+
@available(iOS 26.0, *)
|
|
13
|
+
func deserializeHKMedicationConceptIdentifier(base64String: String?) throws
|
|
14
|
+
-> HKHealthConceptIdentifier? {
|
|
15
|
+
return try fromBase64(base64String: base64String) as? HKHealthConceptIdentifier
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@available(iOS 26.0, *)
|
|
19
|
+
func getLogStatus(_ logStatus: HKMedicationDoseEvent.LogStatus) throws
|
|
20
|
+
-> MedicationDoseEventLogStatus {
|
|
21
|
+
if let rawValue = Int32(exactly: logStatus.rawValue) {
|
|
22
|
+
if let logStatus = MedicationDoseEventLogStatus.init(rawValue: rawValue) {
|
|
23
|
+
return logStatus
|
|
24
|
+
}
|
|
25
|
+
throw runtimeErrorWithPrefix(
|
|
26
|
+
"Unable to convert HKMedicationDoseEvent.LogStatus with \(rawValue) to MedicationDoseEventLogStatus"
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
throw runtimeErrorWithPrefix(
|
|
30
|
+
"Unable to convert HKMedicationDoseEvent.LogStatus to MedicationDoseEventLogStatus")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@available(iOS 26.0, *)
|
|
34
|
+
func getScheduleType(_ scheduleType: HKMedicationDoseEvent.ScheduleType) throws
|
|
35
|
+
-> MedicationDoseEventScheduleType {
|
|
36
|
+
if let rawValue = Int32(exactly: scheduleType.rawValue) {
|
|
37
|
+
if let scheduleType = MedicationDoseEventScheduleType.init(rawValue: rawValue) {
|
|
38
|
+
return scheduleType
|
|
39
|
+
}
|
|
40
|
+
throw runtimeErrorWithPrefix(
|
|
41
|
+
"Unable to convert HKMedicationDoseEvent.ScheduleType with \(rawValue) to MedicationDoseEventScheduleType"
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
throw runtimeErrorWithPrefix(
|
|
45
|
+
"Unable to convert HKMedicationDoseEvent.ScheduleType to MedicationDoseEventScheduleType")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@available(iOS 26.0, *)
|
|
49
|
+
func serializeMedicationEvent(sample: HKMedicationDoseEvent) throws -> MedicationDoseEvent {
|
|
50
|
+
return MedicationDoseEvent(
|
|
51
|
+
scheduleType: try getScheduleType(sample.scheduleType),
|
|
52
|
+
medicationConceptIdentifier: try serializeMedicationConceptIdentifier(
|
|
53
|
+
sample.medicationConceptIdentifier),
|
|
54
|
+
scheduledDate: sample.scheduledDate,
|
|
55
|
+
scheduledDoseQuantity: sample.scheduledDoseQuantity,
|
|
56
|
+
doseQuantity: sample.doseQuantity,
|
|
57
|
+
logStatus: try getLogStatus(sample.logStatus),
|
|
58
|
+
unit: sample.unit.unitString,
|
|
59
|
+
sampleType: serializeSampleType(sample.sampleType),
|
|
60
|
+
startDate: sample.startDate,
|
|
61
|
+
endDate: sample.endDate,
|
|
62
|
+
hasUndeterminedDuration: sample.hasUndeterminedDuration,
|
|
63
|
+
|
|
64
|
+
metadataWeatherCondition: serializeWeatherCondition(
|
|
65
|
+
sample.metadata?[HKMetadataKeyWeatherCondition] as? HKWeatherCondition),
|
|
66
|
+
metadataWeatherHumidity: serializeUnknownQuantityTyped(
|
|
67
|
+
quantity: sample.metadata?[HKMetadataKeyWeatherHumidity] as? HKQuantity),
|
|
68
|
+
metadataWeatherTemperature: serializeUnknownQuantityTyped(
|
|
69
|
+
quantity: sample.metadata?[HKMetadataKeyWeatherTemperature] as? HKQuantity),
|
|
70
|
+
metadataInsulinDeliveryReason: serializeInsulinDeliveryReason(
|
|
71
|
+
sample.metadata?[HKMetadataKeyInsulinDeliveryReason] as? HKInsulinDeliveryReason),
|
|
72
|
+
metadataHeartRateMotionContext: serializeHeartRateMotionContext(
|
|
73
|
+
sample.metadata?[HKMetadataKeyHeartRateMotionContext] as? HKHeartRateMotionContext),
|
|
74
|
+
|
|
75
|
+
uuid: sample.uuid.uuidString,
|
|
76
|
+
sourceRevision: serializeSourceRevision(sample.sourceRevision),
|
|
77
|
+
device: serializeDevice(hkDevice: sample.device),
|
|
78
|
+
metadata: serializeMetadata(sample.metadata),
|
|
79
|
+
|
|
80
|
+
metadataExternalUUID: sample.metadata?[HKMetadataKeyExternalUUID] as? String,
|
|
81
|
+
metadataTimeZone: sample.metadata?[HKMetadataKeyTimeZone] as? String,
|
|
82
|
+
metadataWasUserEntered: sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool,
|
|
83
|
+
metadataDeviceSerialNumber: sample.metadata?[HKMetadataKeyDeviceSerialNumber] as? String,
|
|
84
|
+
metadataUdiDeviceIdentifier: sample.metadata?[HKMetadataKeyUDIDeviceIdentifier] as? String,
|
|
85
|
+
metadataUdiProductionIdentifier: sample.metadata?[HKMetadataKeyUDIProductionIdentifier]
|
|
86
|
+
as? String,
|
|
87
|
+
metadataDigitalSignature: sample.metadata?[HKMetadataKeyDigitalSignature] as? String,
|
|
88
|
+
metadataDeviceName: sample.metadata?[HKMetadataKeyDeviceName] as? String,
|
|
89
|
+
metadataDeviceManufacturerName: sample.metadata?[HKMetadataKeyDeviceManufacturerName]
|
|
90
|
+
as? String,
|
|
91
|
+
metadataSyncIdentifier: sample.metadata?[HKMetadataKeySyncIdentifier] as? String,
|
|
92
|
+
metadataSyncVersion: sample.metadata?[HKMetadataKeySyncVersion] as? Double,
|
|
93
|
+
metadataWasTakenInLab: sample.metadata?[HKMetadataKeyWasTakenInLab] as? Bool,
|
|
94
|
+
metadataReferenceRangeLowerLimit: sample.metadata?[HKMetadataKeyReferenceRangeLowerLimit]
|
|
95
|
+
as? Double,
|
|
96
|
+
metadataReferenceRangeUpperLimit: sample.metadata?[HKMetadataKeyReferenceRangeUpperLimit]
|
|
97
|
+
as? Double,
|
|
98
|
+
metadataAlgorithmVersion: sample.metadata?[HKMetadataKeyAlgorithmVersion] as? Double
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@available(iOS 26.0, *)
|
|
103
|
+
func serializeMedicationConceptIdentifier(_ identifier: HKHealthConceptIdentifier) throws
|
|
104
|
+
-> String {
|
|
105
|
+
if let base64String = toBase64(identifier) {
|
|
106
|
+
return base64String
|
|
107
|
+
}
|
|
108
|
+
throw runtimeErrorWithPrefix("Unable to serialize HKHealthConceptIdentifier")
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@available(iOS 26.0, *)
|
|
112
|
+
func getGeneralForm(hkGeneralForm: HKMedicationGeneralForm) throws -> GeneralForm {
|
|
113
|
+
if let generalForm = GeneralForm.init(fromString: hkGeneralForm.rawValue) {
|
|
114
|
+
return generalForm
|
|
115
|
+
}
|
|
116
|
+
throw runtimeErrorWithPrefix(
|
|
117
|
+
"Unable to convert HKMedicationGeneralForm with \(hkGeneralForm.rawValue) to GeneralForm, no matching value found"
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@available(iOS 26.0, *)
|
|
122
|
+
func serializeMedication(medication: HKUserAnnotatedMedication) throws -> UserAnnotatedMedication {
|
|
123
|
+
return UserAnnotatedMedication(
|
|
124
|
+
isArchived: medication.isArchived,
|
|
125
|
+
hasSchedule: medication.hasSchedule,
|
|
126
|
+
nickname: medication.nickname,
|
|
127
|
+
medication: MedicationConcept(
|
|
128
|
+
identifier: try serializeMedicationConceptIdentifier(medication.medication.identifier),
|
|
129
|
+
displayText: medication.medication.displayText,
|
|
130
|
+
generalForm: try getGeneralForm(hkGeneralForm: medication.medication.generalForm),
|
|
131
|
+
relatedCodings: medication.medication.relatedCodings.map({ c in
|
|
132
|
+
return RelatedCoding(system: c.system, code: c.code, version: c.version)
|
|
133
|
+
})
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class MedicationModule: HybridMedicationModuleSpec {
|
|
139
|
+
func queryMedications() -> Promise<[UserAnnotatedMedication]> {
|
|
140
|
+
return Promise.async {
|
|
141
|
+
if #available(iOS 26.0, *) {
|
|
142
|
+
let q = HKUserAnnotatedMedicationQueryDescriptor()
|
|
143
|
+
|
|
144
|
+
let medications = try await q.result(for: store)
|
|
145
|
+
|
|
146
|
+
return try medications.compactMap({ medication in
|
|
147
|
+
return try serializeMedication(medication: medication)
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
warnWithPrefix(
|
|
151
|
+
"queryMedications is only available on iOS 26 and above")
|
|
152
|
+
return []
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
func queryMedicationEvents(options: QueryOptionsWithSortOrder) -> Promise<[MedicationDoseEvent]> {
|
|
157
|
+
return Promise.async {
|
|
158
|
+
if #available(iOS 26.0, *) {
|
|
159
|
+
let predicate = createPredicateForSamples(options.filter)
|
|
160
|
+
|
|
161
|
+
let doseEvents = try await sampleQueryAsync(
|
|
162
|
+
sampleType: HKSeriesType.medicationDoseEventType(),
|
|
163
|
+
limit: options.limit,
|
|
164
|
+
predicate: predicate,
|
|
165
|
+
sortDescriptors: getSortDescriptors(ascending: options.ascending)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
return try doseEvents.compactMap({ sample in
|
|
169
|
+
if let event = sample as? HKMedicationDoseEvent {
|
|
170
|
+
return try serializeMedicationEvent(sample: event)
|
|
171
|
+
}
|
|
172
|
+
return nil
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
warnWithPrefix("queryMedicationEvents is only available on iOS 26 and above")
|
|
176
|
+
return []
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
func requestMedicationsAuthorization() -> Promise<Bool> {
|
|
181
|
+
return Promise.async {
|
|
182
|
+
if #available(iOS 26.0, *) {
|
|
183
|
+
try await store.requestPerObjectReadAuthorization(
|
|
184
|
+
for: .userAnnotatedMedicationType(), predicate: nil)
|
|
185
|
+
return true
|
|
186
|
+
} else {
|
|
187
|
+
warnWithPrefix(
|
|
188
|
+
"requestMedicationsAuthorization is only available on iOS 26 and above"
|
|
189
|
+
)
|
|
190
|
+
return false
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
func queryMedicationEventsWithAnchor(options: QueryOptionsWithAnchor) -> Promise<
|
|
196
|
+
MedicationDoseEventsWithAnchorResponse
|
|
197
|
+
> {
|
|
198
|
+
return Promise.async {
|
|
199
|
+
if #available(iOS 26.0, *) {
|
|
200
|
+
let predicate = createPredicateForSamples(options.filter)
|
|
201
|
+
|
|
202
|
+
let response = try await sampleAnchoredQueryAsync(
|
|
203
|
+
sampleType: HKSeriesType.medicationDoseEventType(),
|
|
204
|
+
limit: options.limit,
|
|
205
|
+
queryAnchor: options.anchor,
|
|
206
|
+
predicate: predicate
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
let serializedSamples = try response.samples.compactMap { s in
|
|
210
|
+
if let sample = s as? HKMedicationDoseEvent {
|
|
211
|
+
return try serializeMedicationEvent(sample: sample)
|
|
212
|
+
}
|
|
213
|
+
return nil
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return MedicationDoseEventsWithAnchorResponse(
|
|
217
|
+
samples: serializedSamples,
|
|
218
|
+
deletedSamples: response.deletedSamples,
|
|
219
|
+
newAnchor: response.newAnchor
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
throw runtimeErrorWithPrefix("queryMedicationEventsWithAnchor requires iOS 26.0 or later")
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
#else
|
|
228
|
+
class MedicationModule: HybridMedicationModuleSpec {
|
|
229
|
+
func queryMedications() -> Promise<[UserAnnotatedMedication]> {
|
|
230
|
+
return Promise.async {
|
|
231
|
+
throw runtimeErrorWithPrefix("queryMedications needs to be built with XCode 26.0 or later")
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
func queryMedicationEvents(options: QueryOptionsWithSortOrder) -> Promise<[MedicationDoseEvent]> {
|
|
236
|
+
return Promise.async {
|
|
237
|
+
throw runtimeErrorWithPrefix(
|
|
238
|
+
"queryMedicationEvents needs to be built with XCode 26.0 or later")
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
func requestMedicationsAuthorization() -> Promise<Bool> {
|
|
243
|
+
return Promise.async {
|
|
244
|
+
throw runtimeErrorWithPrefix(
|
|
245
|
+
"requestMedicationsAuthorization needs to be built with XCode 26.0 or later")
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
func queryMedicationEventsWithAnchor(options: QueryOptionsWithAnchor)
|
|
250
|
+
-> Promise<MedicationDoseEventsWithAnchorResponse> {
|
|
251
|
+
return Promise.async {
|
|
252
|
+
throw runtimeErrorWithPrefix(
|
|
253
|
+
"queryMedicationEventsWithAnchor needs to be built with XCode 26.0 or later")
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
#endif
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PredicateHelpers.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Robert Herber on 2025-12-10.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import HealthKit
|
|
9
|
+
|
|
10
|
+
func createSourcePredicate(_ sources: [HybridSourceProxySpec]?) -> NSPredicate? {
|
|
11
|
+
if let sources = sources {
|
|
12
|
+
|
|
13
|
+
let sourceSet = Set(
|
|
14
|
+
sources.compactMap({ source in
|
|
15
|
+
return (source as? SourceProxy)?.source
|
|
16
|
+
}))
|
|
17
|
+
if sourceSet.count > 0 {
|
|
18
|
+
return HKQuery.predicateForObjects(from: sourceSet)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return nil
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func createDurationFilter(_ duration: WorkoutDurationPredicate?) -> NSPredicate? {
|
|
25
|
+
if let duration = duration {
|
|
26
|
+
if let predicateOperator = NSComparisonPredicate.Operator(
|
|
27
|
+
rawValue: UInt(duration.predicateOperator.rawValue)) {
|
|
28
|
+
return HKQuery.predicateForWorkouts(
|
|
29
|
+
with: predicateOperator, duration: duration.durationInSeconds)
|
|
30
|
+
} else {
|
|
31
|
+
warnWithPrefix(
|
|
32
|
+
"Unsupported operator in duration filter: \(duration.predicateOperator.rawValue)")
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return nil
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
func createWorkoutActivityTypeFilter(_ workoutActivityType: WorkoutActivityType?) -> NSPredicate? {
|
|
39
|
+
if let workoutActivityType = workoutActivityType {
|
|
40
|
+
if let workoutActivityType = HKWorkoutActivityType.init(
|
|
41
|
+
rawValue: UInt(workoutActivityType.rawValue)) {
|
|
42
|
+
return HKQuery.predicateForWorkouts(with: workoutActivityType)
|
|
43
|
+
} else {
|
|
44
|
+
warnWithPrefix("Unsupported workout activity type in filter: \(workoutActivityType.rawValue)")
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return nil
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func createPredicateForWorkoutBase(_ filter: FilterForWorkoutsBase?) -> NSPredicate? {
|
|
51
|
+
if let filter = filter {
|
|
52
|
+
let allFilters = [
|
|
53
|
+
createMetadataPredicate(filter.metadata),
|
|
54
|
+
createUUIDPredicate(filter.uuid),
|
|
55
|
+
createUUIDsPredicate(uuids: filter.uuids),
|
|
56
|
+
createDatePredicate(filter.date),
|
|
57
|
+
createWorkoutActivityTypeFilter(filter.workoutActivityType),
|
|
58
|
+
createDurationFilter(filter.duration),
|
|
59
|
+
createSourcePredicate(filter.sources),
|
|
60
|
+
].compactMap { $0 }
|
|
61
|
+
|
|
62
|
+
return allFilters.count > 1
|
|
63
|
+
? NSCompoundPredicate.init(andPredicateWithSubpredicates: allFilters)
|
|
64
|
+
: allFilters.first
|
|
65
|
+
}
|
|
66
|
+
return nil
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func getPredicateForWorkoutBase(_ filter: FilterForWorkouts?) -> FilterForWorkoutsBase? {
|
|
70
|
+
if let filter = filter {
|
|
71
|
+
return FilterForWorkoutsBase(
|
|
72
|
+
workoutActivityType: filter.workoutActivityType,
|
|
73
|
+
duration: filter.duration,
|
|
74
|
+
uuid: filter.uuid,
|
|
75
|
+
uuids: filter.uuids,
|
|
76
|
+
metadata: filter.metadata,
|
|
77
|
+
date: filter.date,
|
|
78
|
+
sources: filter.sources
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
return nil
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
func createCompoundOrPredicateForWorkout(OR: [FilterForWorkoutsBase]?) -> NSPredicate? {
|
|
85
|
+
if let OR = OR {
|
|
86
|
+
let orPredicates = OR.compactMap({ filter in
|
|
87
|
+
return createPredicateForWorkoutBase(filter)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
let compoundOr =
|
|
91
|
+
orPredicates.count > 1
|
|
92
|
+
? NSCompoundPredicate.init(orPredicateWithSubpredicates: orPredicates)
|
|
93
|
+
: orPredicates.first
|
|
94
|
+
|
|
95
|
+
if orPredicates.count < 2 {
|
|
96
|
+
warnWithPrefix("Workout filter OR clause contains less than 2 valid predicates.")
|
|
97
|
+
}
|
|
98
|
+
return compoundOr
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return nil
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
func createNotPredicateForWorkout(NOT: [FilterForWorkoutsBase]?) -> NSPredicate? {
|
|
105
|
+
if let NOT = NOT {
|
|
106
|
+
let notPredicates = NOT.compactMap({ filter in
|
|
107
|
+
if let pred = createPredicateForWorkoutBase(filter) {
|
|
108
|
+
return NSCompoundPredicate.init(notPredicateWithSubpredicate: pred)
|
|
109
|
+
}
|
|
110
|
+
return nil
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
return notPredicates.count > 1
|
|
114
|
+
? NSCompoundPredicate.init(andPredicateWithSubpredicates: notPredicates)
|
|
115
|
+
: notPredicates.first
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return nil
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
func createAndPredicateForWorkout(AND: [FilterForWorkoutsBase]?) -> NSPredicate? {
|
|
122
|
+
if let AND = AND {
|
|
123
|
+
let andPredicates = AND.compactMap({ filter in
|
|
124
|
+
return createPredicateForWorkoutBase(filter)
|
|
125
|
+
})
|
|
126
|
+
return andPredicates.count > 1
|
|
127
|
+
? NSCompoundPredicate.init(andPredicateWithSubpredicates: andPredicates)
|
|
128
|
+
: andPredicates.first
|
|
129
|
+
}
|
|
130
|
+
return nil
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
func createPredicateForWorkout(_ filter: FilterForWorkouts?) -> NSPredicate? {
|
|
134
|
+
if let filter = filter {
|
|
135
|
+
let allPredicates = [
|
|
136
|
+
createPredicateForWorkoutBase(
|
|
137
|
+
getPredicateForWorkoutBase(filter)
|
|
138
|
+
),
|
|
139
|
+
createCompoundOrPredicateForWorkout(OR: filter.OR),
|
|
140
|
+
createNotPredicateForWorkout(NOT: filter.NOT),
|
|
141
|
+
createAndPredicateForWorkout(AND: filter.AND),
|
|
142
|
+
].compactMap { $0 }
|
|
143
|
+
|
|
144
|
+
return allPredicates.count > 1
|
|
145
|
+
? NSCompoundPredicate.init(andPredicateWithSubpredicates: allPredicates) : allPredicates.first
|
|
146
|
+
}
|
|
147
|
+
return nil
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
func createDatePredicate(_ dateFilter: DateFilter?) -> NSPredicate? {
|
|
151
|
+
if let dateFilter = dateFilter {
|
|
152
|
+
let strictStartDate = dateFilter.strictStartDate ?? false
|
|
153
|
+
let strictEndDate = dateFilter.strictEndDate ?? false
|
|
154
|
+
|
|
155
|
+
let options: HKQueryOptions =
|
|
156
|
+
strictStartDate && strictEndDate
|
|
157
|
+
? [.strictStartDate, .strictEndDate]
|
|
158
|
+
: strictEndDate
|
|
159
|
+
? .strictEndDate
|
|
160
|
+
: strictStartDate
|
|
161
|
+
? .strictStartDate
|
|
162
|
+
: []
|
|
163
|
+
|
|
164
|
+
return HKQuery.predicateForSamples(
|
|
165
|
+
withStart: dateFilter.startDate,
|
|
166
|
+
end: dateFilter.endDate,
|
|
167
|
+
options: options
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
return nil
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
func createUUIDsPredicate(uuids: [String]?) -> NSPredicate? {
|
|
174
|
+
if let uuids = uuids {
|
|
175
|
+
let uuids = uuids.compactMap { uuidStr -> UUID? in
|
|
176
|
+
do {
|
|
177
|
+
let uuid = try initializeUUID(uuidStr)
|
|
178
|
+
return uuid
|
|
179
|
+
} catch {
|
|
180
|
+
warnWithPrefix(error.localizedDescription)
|
|
181
|
+
return nil
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return HKQuery.predicateForObjects(with: Set(uuids))
|
|
185
|
+
}
|
|
186
|
+
return nil
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
func createUUIDPredicate(_ uuid: String?) -> NSPredicate? {
|
|
190
|
+
if let uuidStr = uuid {
|
|
191
|
+
do {
|
|
192
|
+
let uuid = try initializeUUID(uuidStr)
|
|
193
|
+
return HKQuery.predicateForObject(with: uuid)
|
|
194
|
+
} catch {
|
|
195
|
+
warnWithPrefix("createUUIDPredicate: \(error.localizedDescription)")
|
|
196
|
+
return nil
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return nil
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
func getComparisonPredicateOperator(_ op: ComparisonPredicateOperator?) -> NSComparisonPredicate
|
|
203
|
+
.Operator? {
|
|
204
|
+
if let rawValue = op?.rawValue {
|
|
205
|
+
if let op = NSComparisonPredicate.Operator.init(rawValue: UInt(rawValue)) {
|
|
206
|
+
return op
|
|
207
|
+
} else {
|
|
208
|
+
warnWithPrefix(
|
|
209
|
+
"getComparisonPredicateOperator: Unsupported operator in metadata filter: \(rawValue)")
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return nil
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
func createMetadataPredicate(_ metadata: PredicateWithMetadataKey?) -> NSPredicate? {
|
|
216
|
+
if let metadata = metadata {
|
|
217
|
+
|
|
218
|
+
guard let valueVariant = metadata.value else {
|
|
219
|
+
return HKQuery.predicateForObjects(withMetadataKey: metadata.withMetadataKey)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let actualValue: Any
|
|
223
|
+
|
|
224
|
+
switch valueVariant {
|
|
225
|
+
case .first(let boolValue):
|
|
226
|
+
actualValue = NSNumber(value: boolValue ? 1 : 0)
|
|
227
|
+
case .second(let stringValue):
|
|
228
|
+
actualValue = stringValue
|
|
229
|
+
case .third(let doubleValue):
|
|
230
|
+
actualValue = NSNumber(value: doubleValue)
|
|
231
|
+
case .fourth(let dateValue):
|
|
232
|
+
actualValue = dateValue
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if let operatorType = metadata.operatorType != nil
|
|
236
|
+
? getComparisonPredicateOperator(metadata.operatorType)
|
|
237
|
+
: .equalTo {
|
|
238
|
+
return HKQuery.predicateForObjects(
|
|
239
|
+
withMetadataKey: metadata.withMetadataKey,
|
|
240
|
+
operatorType: operatorType,
|
|
241
|
+
value: actualValue
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
}
|
|
246
|
+
return nil
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
func createPredicateForSamplesBase(_ filter: FilterForSamplesBase?) -> NSPredicate? {
|
|
250
|
+
if let filter = filter {
|
|
251
|
+
let w = filter.workout as? WorkoutProxy
|
|
252
|
+
|
|
253
|
+
let allFilters = [
|
|
254
|
+
createUUIDPredicate(filter.uuid),
|
|
255
|
+
createUUIDsPredicate(uuids: filter.uuids),
|
|
256
|
+
createDatePredicate(filter.date),
|
|
257
|
+
w?.workoutPredicate,
|
|
258
|
+
createMetadataPredicate(filter.metadata),
|
|
259
|
+
createSourcePredicate(filter.sources),
|
|
260
|
+
].compactMap { $0 }
|
|
261
|
+
|
|
262
|
+
return allFilters.count > 1
|
|
263
|
+
? NSCompoundPredicate.init(andPredicateWithSubpredicates: allFilters)
|
|
264
|
+
: allFilters.first
|
|
265
|
+
}
|
|
266
|
+
return nil
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
func createAndPredicateForSamples(_ AND: [FilterForSamplesBase]?) -> NSPredicate? {
|
|
270
|
+
if let filter = AND {
|
|
271
|
+
let allFilters = filter.compactMap { createPredicateForSamplesBase($0) }
|
|
272
|
+
|
|
273
|
+
return allFilters.count > 1
|
|
274
|
+
? NSCompoundPredicate.init(andPredicateWithSubpredicates: allFilters)
|
|
275
|
+
: allFilters.first
|
|
276
|
+
}
|
|
277
|
+
return nil
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
func createNotPredicateForSamples(NOT: [FilterForSamplesBase]?) -> NSPredicate? {
|
|
281
|
+
if let filter = NOT {
|
|
282
|
+
if let allFilters = createAndPredicateForSamples(filter) {
|
|
283
|
+
return NSCompoundPredicate.init(notPredicateWithSubpredicate: allFilters)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return nil
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
func createOrPredicateForSamples(OR: [FilterForSamplesBase]?) -> NSPredicate? {
|
|
290
|
+
if let filter = OR {
|
|
291
|
+
let allFilters = filter.compactMap({ createPredicateForSamplesBase($0) })
|
|
292
|
+
|
|
293
|
+
if allFilters.count < 2 {
|
|
294
|
+
warnWithPrefix(
|
|
295
|
+
"Sample filter OR clause contains less than 2 valid predicates, which makes it redundant.")
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return allFilters.count > 1
|
|
299
|
+
? NSCompoundPredicate.init(orPredicateWithSubpredicates: allFilters)
|
|
300
|
+
: allFilters.first
|
|
301
|
+
}
|
|
302
|
+
return nil
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
func getPredicateForSamplesBase(_ filter: FilterForSamples?) -> FilterForSamplesBase? {
|
|
306
|
+
if let filter = filter {
|
|
307
|
+
return FilterForSamplesBase(
|
|
308
|
+
uuid: filter.uuid,
|
|
309
|
+
uuids: filter.uuids,
|
|
310
|
+
metadata: filter.metadata,
|
|
311
|
+
date: filter.date,
|
|
312
|
+
workout: filter.workout,
|
|
313
|
+
sources: filter.sources
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
return nil
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
func createPredicateForSamples(_ filter: FilterForSamples?) -> NSPredicate? {
|
|
320
|
+
if let filter = filter {
|
|
321
|
+
let allPredicates = [
|
|
322
|
+
createPredicateForSamplesBase(
|
|
323
|
+
getPredicateForSamplesBase(filter)
|
|
324
|
+
),
|
|
325
|
+
createOrPredicateForSamples(OR: filter.OR),
|
|
326
|
+
createAndPredicateForSamples(filter.AND),
|
|
327
|
+
createNotPredicateForSamples(NOT: filter.NOT),
|
|
328
|
+
].compactMap { $0 }
|
|
329
|
+
|
|
330
|
+
return allPredicates.count > 1
|
|
331
|
+
? NSCompoundPredicate.init(andPredicateWithSubpredicates: allPredicates) : allPredicates.first
|
|
332
|
+
}
|
|
333
|
+
return nil
|
|
334
|
+
}
|