Skip to content

Commit 5cbe7fb

Browse files
authored
Merge pull request #1253 from cph-cachet/health13/1250
Health 13.1.3
2 parents 69cc770 + b59ee61 commit 5cbe7fb

File tree

8 files changed

+412
-239
lines changed

8 files changed

+412
-239
lines changed

packages/health/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 13.1.3
2+
3+
* Fix permissions issues with iOS
4+
* Fix [#1231](https://github.com/cph-cachet/flutter-plugins/issues/1231)
5+
16
## 13.1.2
27

38
* Fix [#1250](https://github.com/cph-cachet/flutter-plugins/issues/1250)

packages/health/android/src/main/kotlin/cachet/plugins/health/HealthDataOperations.kt

Lines changed: 97 additions & 98 deletions
Large diffs are not rendered by default.

packages/health/example/lib/main.dart

Lines changed: 196 additions & 63 deletions
Large diffs are not rendered by default.
Lines changed: 91 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,87 @@
1-
import HealthKit
21
import Flutter
2+
import HealthKit
33

44
/// Class for managing health data permissions and deletion operations
55
class HealthDataOperations {
66
let healthStore: HKHealthStore
77
let dataTypesDict: [String: HKSampleType]
88
let characteristicsTypesDict: [String: HKCharacteristicType]
99
let nutritionList: [String]
10-
10+
1111
/// - Parameters:
1212
/// - healthStore: The HealthKit store
1313
/// - dataTypesDict: Dictionary of data types
1414
/// - characteristicsTypesDict: Dictionary of characteristic types
1515
/// - nutritionList: List of nutrition data types
16-
init(healthStore: HKHealthStore,
17-
dataTypesDict: [String: HKSampleType],
18-
characteristicsTypesDict: [String: HKCharacteristicType],
19-
nutritionList: [String]) {
16+
init(
17+
healthStore: HKHealthStore,
18+
dataTypesDict: [String: HKSampleType],
19+
characteristicsTypesDict: [String: HKCharacteristicType],
20+
nutritionList: [String]
21+
) {
2022
self.healthStore = healthStore
2123
self.dataTypesDict = dataTypesDict
2224
self.characteristicsTypesDict = characteristicsTypesDict
2325
self.nutritionList = nutritionList
2426
}
25-
27+
2628
/// Check if HealthKit is available on the device
2729
/// - Parameters:
2830
/// - call: Flutter method call
2931
/// - result: Flutter result callback
3032
func checkIfHealthDataAvailable(call: FlutterMethodCall, result: @escaping FlutterResult) {
3133
result(HKHealthStore.isHealthDataAvailable())
3234
}
33-
35+
3436
/// Check if we have required permissions
3537
/// - Parameters:
3638
/// - call: Flutter method call
3739
/// - result: Flutter result callback
3840
func hasPermissions(call: FlutterMethodCall, result: @escaping FlutterResult) throws {
3941
let arguments = call.arguments as? NSDictionary
4042
guard var types = arguments?["types"] as? [String],
41-
var permissions = arguments?["permissions"] as? [Int],
42-
types.count == permissions.count
43+
var permissions = arguments?["permissions"] as? [Int],
44+
types.count == permissions.count
4345
else {
4446
throw PluginError(message: "Invalid Arguments!")
4547
}
46-
48+
4749
if let nutritionIndex = types.firstIndex(of: HealthConstants.NUTRITION) {
4850
types.remove(at: nutritionIndex)
4951
let nutritionPermission = permissions[nutritionIndex]
5052
permissions.remove(at: nutritionIndex)
51-
53+
5254
for nutritionType in nutritionList {
5355
types.append(nutritionType)
5456
permissions.append(nutritionPermission)
5557
}
5658
}
57-
59+
5860
for (index, type) in types.enumerated() {
5961
guard let sampleType = dataTypesDict[type] else {
6062
print("Warning: Health data type '\(type)' not found in dataTypesDict")
6163
result(false)
6264
return
6365
}
64-
66+
6567
let success = hasPermission(type: sampleType, access: permissions[index])
6668
if success == nil || success == false {
6769
result(success)
6870
return
6971
}
7072
if let characteristicType = characteristicsTypesDict[type] {
71-
let characteristicSuccess = hasPermission(type: characteristicType, access: permissions[index])
72-
if (characteristicSuccess == nil || characteristicSuccess == false) {
73+
let characteristicSuccess = hasPermission(
74+
type: characteristicType, access: permissions[index])
75+
if characteristicSuccess == nil || characteristicSuccess == false {
7376
result(characteristicSuccess)
7477
return
7578
}
7679
}
7780
}
78-
81+
7982
result(true)
8083
}
81-
84+
8285
/// Check if we have permission for a specific type
8386
/// - Parameters:
8487
/// - type: The object type to check
@@ -99,35 +102,47 @@ class HealthDataOperations {
99102
return nil
100103
}
101104
}
102-
105+
103106
/// Request authorization for health data
104107
/// - Parameters:
105108
/// - call: Flutter method call
106109
/// - result: Flutter result callback
107110
func requestAuthorization(call: FlutterMethodCall, result: @escaping FlutterResult) throws {
108111
guard let arguments = call.arguments as? NSDictionary,
109-
let types = arguments["types"] as? [String],
110-
let permissions = arguments["permissions"] as? [Int],
111-
permissions.count == types.count
112+
let types = arguments["types"] as? [String],
113+
let permissions = arguments["permissions"] as? [Int],
114+
permissions.count == types.count
112115
else {
113116
throw PluginError(message: "Invalid Arguments!")
114117
}
115-
118+
116119
var typesToRead = Set<HKObjectType>()
117120
var typesToWrite = Set<HKSampleType>()
118-
121+
119122
for (index, key) in types.enumerated() {
120-
if (key == HealthConstants.NUTRITION) {
123+
if key == HealthConstants.NUTRITION {
121124
for nutritionType in nutritionList {
122125
guard let nutritionData = dataTypesDict[nutritionType] else {
123-
print("Warning: Nutrition data type '\(nutritionType)' not found in dataTypesDict")
126+
print(
127+
"Warning: Nutrition data type '\(nutritionType)' not found in dataTypesDict"
128+
)
124129
continue
125130
}
126-
typesToWrite.insert(nutritionData)
131+
let access = permissions[index]
132+
switch access {
133+
case 0:
134+
typesToRead.insert(nutritionData)
135+
case 1:
136+
typesToWrite.insert(nutritionData)
137+
default:
138+
typesToRead.insert(nutritionData)
139+
typesToWrite.insert(nutritionData)
140+
}
141+
127142
}
128143
} else {
129144
let access = permissions[index]
130-
145+
131146
if let dataType = dataTypesDict[key] {
132147
switch access {
133148
case 0:
@@ -139,90 +154,98 @@ class HealthDataOperations {
139154
typesToWrite.insert(dataType)
140155
}
141156
}
142-
157+
143158
if let characteristicsType = characteristicsTypesDict[key] {
144159
switch access {
145160
case 0:
146161
typesToRead.insert(characteristicsType)
147162
case 1:
148-
throw PluginError(message: "Cannot request write permission for characteristic type \(characteristicsType)")
163+
throw PluginError(
164+
message:
165+
"Cannot request write permission for characteristic type \(characteristicsType)"
166+
)
149167
default:
150168
typesToRead.insert(characteristicsType)
151169
}
152170
}
153-
171+
154172
if dataTypesDict[key] == nil && characteristicsTypesDict[key] == nil {
155-
print("Warning: Health data type '\(key)' not found in dataTypesDict or characteristicsTypesDict")
173+
print(
174+
"Warning: Health data type '\(key)' not found in dataTypesDict or characteristicsTypesDict"
175+
)
156176
}
157-
177+
158178
}
159179
}
160-
161-
if #available(iOS 13.0, *) {
162-
healthStore.requestAuthorization(toShare: typesToWrite, read: typesToRead) {
163-
(success, error) in
164-
DispatchQueue.main.async {
165-
result(success)
166-
}
180+
181+
healthStore.requestAuthorization(toShare: typesToWrite, read: typesToRead) {
182+
(success, error) in
183+
DispatchQueue.main.async {
184+
result(success)
167185
}
168-
} else {
169-
// TODO: Add proper error handling
170-
result(false)
171186
}
187+
172188
}
173-
189+
174190
/// Delete health data by date range
175191
/// - Parameters:
176192
/// - call: Flutter method call
177193
/// - result: Flutter result callback
178194
func delete(call: FlutterMethodCall, result: @escaping FlutterResult) {
179195
guard let arguments = call.arguments as? NSDictionary,
180-
let dataTypeKey = arguments["dataTypeKey"] as? String else {
196+
let dataTypeKey = arguments["dataTypeKey"] as? String
197+
else {
181198
print("Error: Missing dataTypeKey in arguments")
182199
result(false)
183200
return
184201
}
185-
202+
186203
// Check if it's a characteristic type - these cannot be deleted
187204
if characteristicsTypesDict[dataTypeKey] != nil {
188-
print("Info: Cannot delete characteristic type '\(dataTypeKey)' - these are read-only system values")
205+
print(
206+
"Info: Cannot delete characteristic type '\(dataTypeKey)' - these are read-only system values"
207+
)
189208
result(false)
190209
return
191210
}
192-
211+
193212
let startTime = (arguments["startTime"] as? NSNumber) ?? 0
194213
let endTime = (arguments["endTime"] as? NSNumber) ?? 0
195-
214+
196215
let dateFrom = HealthUtilities.dateFromMilliseconds(startTime.doubleValue)
197216
let dateTo = HealthUtilities.dateFromMilliseconds(endTime.doubleValue)
198-
217+
199218
guard let dataType = dataTypesDict[dataTypeKey] else {
200219
print("Warning: Health data type '\(dataTypeKey)' not found in dataTypesDict")
201220
result(false)
202221
return
203222
}
204-
223+
205224
let samplePredicate = HKQuery.predicateForSamples(
206225
withStart: dateFrom, end: dateTo, options: .strictStartDate)
207226
let ownerPredicate = HKQuery.predicateForObjects(from: HKSource.default())
208227
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
209-
228+
210229
let deleteQuery = HKSampleQuery(
211230
sampleType: dataType,
212-
predicate: NSCompoundPredicate(andPredicateWithSubpredicates: [samplePredicate, ownerPredicate]),
231+
predicate: NSCompoundPredicate(andPredicateWithSubpredicates: [
232+
samplePredicate, ownerPredicate,
233+
]),
213234
limit: HKObjectQueryNoLimit,
214235
sortDescriptors: [sortDescriptor]
215236
) { [weak self] x, samplesOrNil, error in
216237
guard let self = self else { return }
217-
238+
218239
guard let samplesOrNil = samplesOrNil, error == nil else {
219-
print("Error querying \(dataType) samples: \(error?.localizedDescription ?? "Unknown error")")
240+
print(
241+
"Error querying \(dataType) samples: \(error?.localizedDescription ?? "Unknown error")"
242+
)
220243
DispatchQueue.main.async {
221244
result(false)
222245
}
223246
return
224247
}
225-
248+
226249
// Chcek if there are any samples to delete
227250
if samplesOrNil.isEmpty {
228251
print("Info: No \(dataType) samples found in the specified date range.")
@@ -231,7 +254,7 @@ class HealthDataOperations {
231254
}
232255
return
233256
}
234-
257+
235258
// Delete the retrieved objects from the HealthKit store
236259
self.healthStore.delete(samplesOrNil) { (success, error) in
237260
if let err = error {
@@ -242,48 +265,49 @@ class HealthDataOperations {
242265
}
243266
}
244267
}
245-
268+
246269
healthStore.execute(deleteQuery)
247270
}
248-
271+
249272
/// Delete health data by UUID
250273
/// - Parameters:
251274
/// - call: Flutter method call
252275
/// - result: Flutter result callback
253276
func deleteByUUID(call: FlutterMethodCall, result: @escaping FlutterResult) throws {
254277
guard let arguments = call.arguments as? NSDictionary,
255-
let uuidarg = arguments["uuid"] as? String,
256-
let dataTypeKey = arguments["dataTypeKey"] as? String else {
278+
let uuidarg = arguments["uuid"] as? String,
279+
let dataTypeKey = arguments["dataTypeKey"] as? String
280+
else {
257281
throw PluginError(message: "Invalid Arguments - UUID or DataTypeKey invalid")
258282
}
259-
283+
260284
guard let dataTypeToRemove = dataTypesDict[dataTypeKey] else {
261285
print("Warning: Health data type '\(dataTypeKey)' not found in dataTypesDict")
262286
result(false)
263287
return
264288
}
265-
289+
266290
guard let uuid = UUID(uuidString: uuidarg) else {
267291
result(false)
268292
return
269293
}
270294
let predicate = HKQuery.predicateForObjects(with: [uuid])
271-
295+
272296
let query = HKSampleQuery(
273297
sampleType: dataTypeToRemove,
274298
predicate: predicate,
275299
limit: 1,
276300
sortDescriptors: nil
277301
) { [weak self] query, samplesOrNil, error in
278302
guard let self = self else { return }
279-
303+
280304
guard let samples = samplesOrNil, !samples.isEmpty else {
281305
DispatchQueue.main.async {
282306
result(false)
283307
}
284308
return
285309
}
286-
310+
287311
self.healthStore.delete(samples) { success, error in
288312
if let error = error {
289313
print("Error deleting sample with UUID \(uuid): \(error.localizedDescription)")
@@ -293,7 +317,7 @@ class HealthDataOperations {
293317
}
294318
}
295319
}
296-
320+
297321
healthStore.execute(query)
298322
}
299323
}

packages/health/ios/health.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44
Pod::Spec.new do |s|
55
s.name = 'health'
6-
s.version = '13.1.2'
6+
s.version = '13.1.3'
77
s.summary = 'Wrapper for Apple\'s HealthKit on iOS and Google\'s Health Connect on Android.'
88
s.description = <<-DESC
99
Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android.

0 commit comments

Comments
 (0)