简体   繁体   中英

HealthKit Error: “Failed to find some objects for deletion” when attempting to delete HKQuantitySample

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 (eg 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)
            
            
        
        
    }

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:

  1. Add Health entitlement in your app.
  2. Add Usage description texts in your Info.plist
  3. Request authorization
  4. Grant the authorization
  5. Save some valid sample data
  6. Query the data
  7. Check if the result is not empty and is valid
  8. Delete the resulting data

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM