1- import HealthKit
21import Flutter
2+ import HealthKit
33
44/// Class for managing health data permissions and deletion operations
55class 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}
0 commit comments