Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/health/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 12.1.0

* Add delete record by UUID method. See function `deleteByUUID(required String uuid, HealthDataType? type)`
* iOS: Parse metadata to remove unsupported types - PR [#1120](https://github.com/cph-cachet/flutter-plugins/pull/1120)
* iOS: Add UV Index Types
* Android: Add request access to historic data [#1126](https://github.com/cph-cachet/flutter-plugins/issues/1126) - PR [#1127](https://github.com/cph-cachet/flutter-plugins/pull/1127)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
"writeBloodOxygen" -> writeBloodOxygen(call, result)
"writeMenstruationFlow" -> writeMenstruationFlow(call, result)
"writeMeal" -> writeMeal(call, result)
"deleteByUUID" -> deleteByUUID(call, result)
else -> result.notImplemented()
}
}
Expand Down Expand Up @@ -2395,6 +2396,43 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
}
}

/** Delete a specific record by UUID and type */
private fun deleteByUUID(call: MethodCall, result: Result) {
val arguments = call.arguments as? HashMap<*, *>
val dataTypeKey = (arguments?.get("dataTypeKey") as? String)!!
val uuid = (arguments?.get("uuid") as? String)!!

if (!mapToType.containsKey(dataTypeKey)) {
Log.w("FLUTTER_HEALTH::ERROR", "Datatype $dataTypeKey not found in HC")
result.success(false)
return
}

val classType = mapToType[dataTypeKey]!!

scope.launch {
try {
healthConnectClient.deleteRecords(
recordType = classType,
recordIdsList = listOf(uuid),
clientRecordIdsList = emptyList()
)
result.success(true)
Log.i(
"FLUTTER_HEALTH::SUCCESS",
"[Health Connect] Record with UUID $uuid was successfully deleted!"
)
} catch (e: Exception) {
Log.e("FLUTTER_HEALTH::ERROR", "Error deleting record with UUID: $uuid")
Log.e("FLUTTER_HEALTH::ERROR", e.message ?: "unknown error")
Log.e("FLUTTER_HEALTH::ERROR", e.stackTraceToString())
result.success(false)
}
}
}



private val mapSleepStageToType =
hashMapOf(
0 to SLEEP_UNKNOWN,
Expand Down
20 changes: 20 additions & 0 deletions packages/health/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,26 @@ class HealthAppState extends State<HealthApp> {
);
}

// To delete a record by UUID - call the `health.deleteByUUID` method:
/**
List<HealthDataPoint> healthData = await health.getHealthDataFromTypes(
types: [HealthDataType.STEPS],
startTime: startDate,
endTime: endDate,
);

if (healthData.isNotEmpty) {
print("DELETING: ${healthData.first.toJson()}");
String uuid = healthData.first.uuid;

success &= await health.deleteByUUID(
type: HealthDataType.STEPS,
uuid: uuid,
);

}
*/

setState(() {
_state = success ? AppState.DATA_DELETED : AppState.DATA_NOT_DELETED;
});
Expand Down
44 changes: 44 additions & 0 deletions packages/health/ios/Classes/SwiftHealthPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin {
else if call.method.elementsEqual("delete") {
try! delete(call: call, result: result)
}

/// Handle deleteByUUID data
else if call.method.elementsEqual("deleteByUUID") {
try! deleteByUUID(call: call, result: result)
}
}

func checkIfHealthDataAvailable(call: FlutterMethodCall, result: @escaping FlutterResult) {
Expand Down Expand Up @@ -766,6 +771,45 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin {

HKHealthStore().execute(deleteQuery)
}

func deleteByUUID(call: FlutterMethodCall, result: @escaping FlutterResult) throws {
guard let arguments = call.arguments as? NSDictionary,
let uuidarg = arguments["uuid"] as? String,
let dataTypeKey = arguments["dataTypeKey"] as? String else {
throw PluginError(message: "Invalid Arguments - UUID or DataTypeKey invalid")
}
let dataTypeToRemove = dataTypeLookUp(key: dataTypeKey)
guard let uuid = UUID(uuidString: uuidarg) else {
result(false)
return
}
let predicate = HKQuery.predicateForObjects(with: [uuid])

let query = HKSampleQuery(
sampleType: dataTypeToRemove,
predicate: predicate,
limit: 1,
sortDescriptors: nil
) { query, samplesOrNil, error in
guard let samples = samplesOrNil, !samples.isEmpty else {
DispatchQueue.main.async {
result(false)
}
return
}

self.healthStore.delete(samples) { success, error in
if let error = error {
print("Error deleting sample with UUID \(uuid): \(error.localizedDescription)")
}
DispatchQueue.main.async {
result(success)
}
}
}

healthStore.execute(query)
}

func getData(call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
Expand Down
32 changes: 32 additions & 0 deletions packages/health/lib/src/health_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,38 @@ class Health {
return success ?? false;
}

/// Deletes a specific health record by its UUID.
///
/// Returns true if successful, false otherwise.
///
/// Parameters:
/// * [uuid] - The UUID of the health record to delete.
/// * [type] - The health data type of the record. Required on iOS.
///
/// On Android, only the UUID is required. On iOS, both UUID and type are required.
Future<bool> deleteByUUID({
required String uuid,
HealthDataType? type,
}) async {
await _checkIfHealthConnectAvailableOnAndroid();

if (uuid.isEmpty || uuid == "") {
throw ArgumentError("UUID must not be empty.");
}

if (Platform.isIOS && type == null) {
throw ArgumentError("On iOS, both UUID and type are required to delete a record.");
}

Map<String, dynamic> args = {
'uuid': uuid,
'dataTypeKey': type?.name,
};

bool? success = await _channel.invokeMethod('deleteByUUID', args);
return success ?? false;
}

/// Saves a blood pressure record.
///
/// Returns true if successful, false otherwise.
Expand Down