2015-09-29 17 views
0

我很高興知道蘋果在iOS 9中添加了HealthKit中刪除的跟蹤。因此,我設置了一個測試項目來試用它。不幸的是,雖然我可以很好地獲取新數據,但我的回調中沒有找到任何已刪除的對象。健康包iOS 9中的HKAnchoredObjectQuery不返回HKDeletedObject

我有一個可以跟蹤HKQueryAnchor的HKAnchoredObjectQuery,並且每當我通過Health應用程序向HealthKit添加BloodGlucose數量時,都會爲我提供新的HKSamples。但是,當我刪除相同的數量並重新運行應用程序時,HKDeletedObject始終爲空。即使我在同一時間添加和刪除。看起來不管我做什麼,HKDeletedObject數組總是空的。但補充工作正常(只從上次錨點獲取添加的樣本)。

這是我的代碼。它只是2個文件。要重新創建項目,只需創建一個新的swift項目,爲自己提供HealthKit權利,並將其複製到其中(注意:運行它時,每次運行只會得到一個更新,因此如果您在HealthKit中進行更改,則必須停止並重新啓動應用程序來測試回調)

這是我HealthKit客戶端:

// 
// HKClient.swift 
// HKTest 

import UIKit 
import HealthKit 

class HKClient : NSObject { 

    var isSharingEnabled: Bool = false 
    let healthKitStore:HKHealthStore? = HKHealthStore() 
    let glucoseType : HKObjectType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)! 

    override init(){ 
     super.init() 
    } 

    func requestGlucosePermissions(authorizationCompleted: (success: Bool, error: NSError?)->Void) { 

     let dataTypesToRead : Set<HKObjectType> = [ glucoseType ] 

     if(!HKHealthStore.isHealthDataAvailable()) 
     { 
      // let error = NSError(domain: "com.test.healthkit", code: 2, userInfo: [NSLocalizedDescriptionKey: "Healthkit is not available on this device"]) 
      self.isSharingEnabled = false 
      return 
     } 

     self.healthKitStore?.requestAuthorizationToShareTypes(nil, readTypes: dataTypesToRead){(success, error) -> Void in 
      self.isSharingEnabled = true 
      authorizationCompleted(success: success, error: error) 
     } 
    } 

    func getGlucoseSinceAnchor(anchor:HKQueryAnchor?, maxResults:uint, callback: ((source: HKClient, added: [String]?, deleted: [String]?, newAnchor: HKQueryAnchor?, error: NSError?)->Void)!){ 
     let queryEndDate = NSDate(timeIntervalSinceNow: NSTimeInterval(60.0 * 60.0 * 24)) 
     let queryStartDate = NSDate.distantPast() 
     let sampleType: HKSampleType = glucoseType as! HKSampleType 
     let predicate: NSPredicate = HKAnchoredObjectQuery.predicateForSamplesWithStartDate(queryStartDate, endDate: queryEndDate, options: HKQueryOptions.None) 
     var hkAnchor: HKQueryAnchor; 

     if(anchor != nil){ 
      hkAnchor = anchor! 
     } else { 
      hkAnchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor)) 
     } 

     let onAnchorQueryResults : ((HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, NSError?) -> Void)! = { 
      (query:HKAnchoredObjectQuery, addedObjects:[HKSample]?, deletedObjects:[HKDeletedObject]?, newAnchor:HKQueryAnchor?, nsError:NSError?) -> Void in 

      var added = [String]() 
      var deleted = [String]() 

      if (addedObjects?.count > 0){ 
       for obj in addedObjects! { 
        let quant = obj as? HKQuantitySample 
        if(quant?.UUID.UUIDString != nil){ 
         let val = Double((quant?.quantity.doubleValueForUnit(HKUnit(fromString: "mg/dL")))!) 
         let msg : String = (quant?.UUID.UUIDString)! + " " + String(val) 
         added.append(msg) 
        } 
       } 
      } 

      if (deletedObjects?.count > 0){ 
       for del in deletedObjects! { 
        let value : String = del.UUID.UUIDString 
        deleted.append(value) 
       } 
      } 

      if(callback != nil){ 
       callback(source:self, added: added, deleted: deleted, newAnchor: newAnchor, error: nsError) 
      } 
     } 

     let anchoredQuery = HKAnchoredObjectQuery(type: sampleType, predicate: predicate, anchor: hkAnchor, limit: Int(maxResults), resultsHandler: onAnchorQueryResults) 
     healthKitStore?.executeQuery(anchoredQuery) 
    } 

    let AnchorKey = "HKClientAnchorKey" 
    func getAnchor() -> HKQueryAnchor? { 
     let encoded = NSUserDefaults.standardUserDefaults().dataForKey(AnchorKey) 
     if(encoded == nil){ 
      return nil 
     } 
     let anchor = NSKeyedUnarchiver.unarchiveObjectWithData(encoded!) as? HKQueryAnchor 
     return anchor 
    } 

    func saveAnchor(anchor : HKQueryAnchor) { 
     let encoded = NSKeyedArchiver.archivedDataWithRootObject(anchor) 
     NSUserDefaults.standardUserDefaults().setValue(encoded, forKey: AnchorKey) 
     NSUserDefaults.standardUserDefaults().synchronize() 
    } 
} 

這是我的看法:

// 
// ViewController.swift 
// HKTest 

import UIKit 
import HealthKit 

class ViewController: UIViewController { 
    let debugLabel = UILabel(frame: CGRect(x: 10,y: 20,width: 350,height: 600)) 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     self.view = UIView(); 
     self.view.backgroundColor = UIColor.whiteColor() 


     debugLabel.textAlignment = NSTextAlignment.Center 
     debugLabel.textColor = UIColor.blackColor() 
     debugLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping 
     debugLabel.numberOfLines = 0 
     self.view.addSubview(debugLabel) 

     let hk = HKClient() 
     hk.requestGlucosePermissions(){ 
      (success, error) -> Void in 

      if(success){ 
       let anchor = hk.getAnchor() 

       hk.getGlucoseSinceAnchor(anchor, maxResults: 0) 
        { (source, added, deleted, newAnchor, error) -> Void in 
         var msg : String = String() 

         if(deleted?.count > 0){ 
          msg += "Deleted: \n" + (deleted?[0])! 
          for s in deleted!{ 
           msg += s + "\n" 
          } 
         } 

         if (added?.count > 0) { 
          msg += "Added: " 
          for s in added!{ 
           msg += s + "\n" 
          } 
         } 

         if(error != nil) { 
          msg = "Error = " + (error?.description)! 
         } 

         if(msg.isEmpty) 
         { 
          msg = "No changes" 
         } 
         debugPrint(msg) 

         if(newAnchor != nil && newAnchor != anchor){ 
          hk.saveAnchor(newAnchor!) 
         } 

         dispatch_async(dispatch_get_main_queue(), {() -> Void in 
          self.debugLabel.text = msg 
         }) 
        } 
      } 
     } 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 
} 

注:我知道蘋果建議此設置使用HKObserverQuery。我原本是在Xamarin項目中這樣做的,行爲是一樣的(沒有HKDeletedObjects被髮送)。所以當用swift試用時,爲了簡單起見,我忽略了HKObserverQuery。

回答

2

當您實例化查詢時刪除謂詞(謂詞:nil),您將看到更多結果,包括已刪除的結果。第一次運行這個(在保存HKQueryAnchor之前)你會得到所有的結果,所以你可能想要做一些事情來過濾這些結果;但後續執行的查詢將使用您保存的錨點,以便您只能看到自保存的錨點以來發生的更改。

您可能還想在執行前在查詢中設置updateHandler屬性。這會將查詢設置爲在後臺調用更新處理程序時連續運行(添加或刪除)。附近getGlucoseSinceAnchor結束(...)的代碼看起來像

... 
let anchoredQuery = HKAnchoredObjectQuery(type: sampleType, predicate: nil, anchor: hkAnchor, limit: Int(maxResults), resultsHandler: onAnchorQueryResults) 
anchoredQuery.updateHandler = onAnchorQueryResults 
healthKitStore?.executeQuery(anchoredQuery) 
... 
+0

謝謝。這工作,但非常傷心,我不能指定一個謂詞,如果我想回調刪除對象。如果我只給它一個開始日期和結束日期,它甚至會失敗。相反,我必須使用一個無謂詞,並從所有時間獲得所有結果的所有結果,然後再解析事實。如果謂詞只適用於添加的條目,這似乎打破了我。 – thedigitalsean

+1

經過進一步調查,來源DOES的謂詞適用於刪除。我的理論是這樣的: getPredicateForSamples按日期查詢HKSamples。 HKDeletedObjects繼承自HKObject,而不是HKSample。所以,我懷疑由於getPredicateForSamples()在日期範圍內爲HKSamples過濾,所以它會跳過HKDeletedObjects。 現在,如果使用getPredicateForObjects()過濾源代碼,它將過濾HKObjects,而不是HKSamples。因此它會返回HKSample和HKDeletedObject,因爲這兩個類都繼承自HKObject。我不喜歡它,但這似乎正在發生。 – thedigitalsean

+0

當我看到您的評論時,我正深入挖掘這一點。你的理論是有道理的。 – steve1951