我很高興知道蘋果在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。
謝謝。這工作,但非常傷心,我不能指定一個謂詞,如果我想回調刪除對象。如果我只給它一個開始日期和結束日期,它甚至會失敗。相反,我必須使用一個無謂詞,並從所有時間獲得所有結果的所有結果,然後再解析事實。如果謂詞只適用於添加的條目,這似乎打破了我。 – thedigitalsean
經過進一步調查,來源DOES的謂詞適用於刪除。我的理論是這樣的: getPredicateForSamples按日期查詢HKSamples。 HKDeletedObjects繼承自HKObject,而不是HKSample。所以,我懷疑由於getPredicateForSamples()在日期範圍內爲HKSamples過濾,所以它會跳過HKDeletedObjects。 現在,如果使用getPredicateForObjects()過濾源代碼,它將過濾HKObjects,而不是HKSamples。因此它會返回HKSample和HKDeletedObject,因爲這兩個類都繼承自HKObject。我不喜歡它,但這似乎正在發生。 – thedigitalsean
當我看到您的評論時,我正深入挖掘這一點。你的理論是有道理的。 – steve1951