Reputation: 6710
I have a workout Apple Watch app that starts an HKWorkout
and saves HKQuantitySamples
(DistanceWalkingRunning samples) into Apple Health using the original Healthkit API (not the newer workout builder). I then want to give the user the ability to delete these on the companion iPhone app (e.g. they forgot to end the workout on the watch). No matter which of the 3 methods of deleting the samples I use below I always get this error. I know that an app can only delete samples that were saved by the same app but is the fact that the HKSource
of the samples is the watch app preventing me from deleting on the iPhone? I wouldn't think that would be intended. Is there any other reason I am unable to delete these samples?
private func deleteRunningAndWalkingSamplesFrom(startDate: Date, endDate: Date) {
let healthStore = HKHealthStore()
guard let distanceWalkingAndRunningType:HKQuantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWalkingRunning) else {
return
}
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate)
let distanceWalkingAndRunningQuery = HKSampleQuery(sampleType: distanceWalkingAndRunningType,
predicate: predicate,
limit: (HKObjectQueryNoLimit),
sortDescriptors: nil)
{ (query:HKSampleQuery, results:[HKSample]?, error:Error?) -> Void in
if let unwrappedError = error {
print("Error trying to delete distance samples = \(unwrappedError)")
return
}
guard let unwrappedResults = results as? [HKQuantitySample] else {
print("Couldn't cast results as [HKQuantitySample] in deleteRunninAndWalkingSamples")
return
}
//(1) Look through samples and try to delete
// for sample in unwrappedResults {
// healthStore.delete(sample) { success, error in
// if let error = error {
// print("error attempting to delete runningAndWalkinGSample with startDate of \(sample.startDate) error = \(error)")
// }
//
// if success {
// print("successfully delete sample with startDate of \(sample.startDate)")
// }
// }
// }
//(2) Delete objects with predicate
// healthStore.deleteObjects(of: distanceWalkingAndRunningType, predicate: predicate) { success, deleteCount, error in
// if let error = error {
// print("error attempting to delete runningAndWalkinGSamplesFrom shift = \(error)")
// }
//
// if success {
// print("successfully deleted \(deleteCount) samples")
// }
// }
//(3) Delete all samples
healthStore.delete(unwrappedResults) { success, error in
if let error = error {
print("error attempting to delete runningAndWalkinGSamplesFrom shift = \(error)")
}
if success {
print("success deleting runningAndWalkingSamples ")
}
}
}
healthStore.execute(distanceWalkingAndRunningQuery)
}
Upvotes: 2
Views: 325
Reputation: 1575
I've created a sample app, and couldn't reproduce the issue:
import SwiftUI
import HealthKit
final class HealthManager: ObservableObject {
let quantityType = HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning)!
let healthStore = HKHealthStore()
func requestAuthorization() {
healthStore.requestAuthorization(toShare: [quantityType], read: [quantityType]) { [weak self] success, error in
print(success, error?.localizedDescription ?? "")
self?.createMockData { [weak self] success, error in
print(success, error?.localizedDescription ?? "")
self?.deleteMockData { success, error in
print(success, error?.localizedDescription ?? "")
}
}
}
}
func createMockData(completion: @escaping (Bool, Error?) -> ()) {
let sample = HKQuantitySample(type: quantityType,
quantity: .init(unit: .inch(), doubleValue: 1000),
start: Date().addingTimeInterval(-1000),
end: Date().addingTimeInterval(-10))
healthStore.save(sample, withCompletion: completion)
}
func deleteMockData(completion: @escaping (Bool, Error?) -> ()) {
let predicate = HKQuery.predicateForSamples(withStart: .distantPast, end: .distantFuture)
let distanceWalkingAndRunningQuery = HKSampleQuery(sampleType: quantityType,
predicate: predicate,
limit: HKObjectQueryNoLimit,
sortDescriptors: nil) { [weak self] (query, results, error) in
if let unwrappedError = error {
completion(false, unwrappedError)
return
}
guard let unwrappedResults = results as? [HKQuantitySample] else {
completion(false, NSError(domain: "dom", code: 1, userInfo: [
NSLocalizedDescriptionKey: "Couldn't cast results as [HKQuantitySample] in deleteRunninAndWalkingSamples"
]))
return
}
guard !unwrappedResults.isEmpty else {
completion(false, NSError(domain: "dom", code: 2, userInfo: [
NSLocalizedDescriptionKey: "Nothing to delete"
]))
return
}
self?.healthStore.delete(unwrappedResults, withCompletion: completion)
}
healthStore.execute(distanceWalkingAndRunningQuery)
}
}
@main
struct TestApp: App {
@StateObject var healthManager = HealthManager()
@SceneBuilder var body: some Scene {
WindowGroup {
NavigationView {
ContentView()
.onAppear {
healthManager.requestAuthorization()
}
}
}
WKNotificationScene(controller: NotificationController.self, category: "myCategory")
}
}
Please make sure that you have performed all the following steps:
Health entitlement
in your app.Usage description
texts in your Info.plist
Upvotes: 1