CloudKit提取操作返回旧数据集

时间:2018-05-25 16:36:21

标签: ios swift cloudkit ckmodifyrecordsoperation

我正在尝试在CloudKit和本地数据存储之间创建一个非常简单的sycn。 在我的函数中,我获取当前数据并将其与本地数据进行比较。新的脱机记录进入一个阵列,脱机删除进入另一个阵列。这些使用CKModifyRecordsOperation保存到CloudKit。在完成处理程序中,然后我调用执行操作来获取更新的数据。

不幸的是,我没有在此获取中获取新记录。删除不包括在内但我只是没有从CloudKit收到新记录。以下是我职能的基本内容。

static func sync(
        completion: @escaping (
            _ results: [Pet],
            _ error: NSError?) -> ())
    {

    var petsToDelete = [CKRecordID]()
    var petsToSave = [CKRecord]()

    //Empty any existing pet array
    pets = []

    //load local pets into an array
    if let localPets = loadPetsLocal()  {

    //Set up the array for CloudKit Pets
    fetchFromCloud(){ (results , error) -> () in
        cloudPets = results

        //Use records marked for deletion to mark the CKRecords for deletion and put them into an array for saving to CloudKit
        for localPet in localPets {
            //MARK: RECON DELETIONS (Offline deletions of CKRecords)
            //Check to see if the record is marked for deletion (marked while off the CloudKit) and put them in an array
            if localPet.petName.lowercased().range(of: "delete me") != nil{
                //If the record is marked for deletion, iterate through the pets array to find the related CKRecord
                for petToDelete in cloudPets{
                    if localPet.recordName == petToDelete.recordID.recordName{
                        //Put this record into the deletion array of CKRecordIDs
                        petsToDelete.append(petToDelete.recordID)
                    }
                }
            }

        //Put all new records (recordName = "Local") into an array for saving to CloudKit
            if localPet.recordName == "Local" {
                changedRecord = CKRecord(recordType: RemoteRecords.pet, zoneID: ArendK9DB.share.zoneID)
                changedRecord?[RemotePet.petName] = localPet.petName as NSString
                changedRecord?[RemotePet.dob] = localPet.dob as NSDate?
                changedRecord?[RemotePet.petSex] = localPet.petSex as NSString?
                //Some special handling to get the UIImage into a CKAsset
                if let photo = localPet.photo {
                    let imageData:Data = UIImageJPEGRepresentation(photo, 1.0)!
                    let path:String = Pet.documentsDirectoryPath.appendingPathComponent(Pet.tempImageName)
                    try? UIImageJPEGRepresentation(photo, 1.0)!.write(to: URL(fileURLWithPath: path), options: [.atomic])
                    Pet.imageURL = URL(fileURLWithPath: path)
                    try? imageData.write(to: Pet.imageURL, options: [.atomic])
                    let File:CKAsset?  = CKAsset(fileURL: URL(fileURLWithPath: path))
                    changedRecord?[RemotePet.photo] = File as! CKAsset
                }
                petsToSave.append(changedRecord!)
            }
        }
        //There is a lot more code here to check for records that have changed

        **strong text**//If nothing changed, just return the original array
        if petsToDelete == [] && petsToSave == [] {
            print("DEBUG I have nothing to save")
            if error != nil {
                print(error?.localizedDescription ?? "General Query Error: No Description")
            } else {
                /*guard let records = results else {
                 return
                 }*/
                for record in results {
                    if let pet = Pet(remoteRecord: record) {
                        self.pets.append(pet)
                    }
                }
                completion(pets, nil)
            }
        } else {
            //Save the new and deleted records to CloudKit
            saveUpdateCloud(petsToSave: petsToSave, recordIDsToDeleve: petsToDelete)
                { (results , error) -> () in
                    if error != nil {
                        print(error?.localizedDescription ?? "General Query Error: No Description")
                    } else {
                        print("DEBUG: I have returned to the Recon from the update and isFinished is \(results) and pets is \(pets)")
                        //Grab the new, updated list of CKRecords
                        fetchFromCloud(){ (r , e) -> () in
                            print("DEBUG Loading new CloudKit array at \(Date())")
                            if e != nil {
                                print(e?.localizedDescription ?? "General Query Error: No Description")
                            } else {
                                for record in r {
                                    if let pet = Pet(remoteRecord: record) {
                                        pets.append(pet)
                                    }
                                }
                                //Save this new array locally
                                print("DEGUG Saving records to local store")
                                pets[0].saveToLocal(petsToSave: self.pets)
                                print("DEBUG I have a new pet array from CloudKit \(pets)")
                                completion(pets, nil)
                            }
                        }

                    }

                }
        }
    }
    } else {
        fetchFromCloud(){ (r , e) -> () in
            print("DEBUG There is no local data so I am returning a new CloudKit array  \(r)")
            if e != nil {
                print(e?.localizedDescription ?? "General Query Error: No Description")
            } else {
                for record in r {
                    if let pet = Pet(remoteRecord: record) {
                        pets.append(pet)
                    }
                }
                //Save this new array locally
                print("DEGUG Saving records to local store")
                pets[0].saveToLocal(petsToSave: self.pets)
                print("DEBUG I have a new pet array from CloudKit \(pets)")
                completion(pets, nil)
            }
        }
    }
}


//MARK: Fetch current records from CloudKit
static func fetchFromCloud(
    completion: @escaping (
    _ results: [CKRecord],
    _ error: NSError?) -> ())
{

    print("Fetch from CloudKit... starting completion handler")

    //Retrieve CloudKit data into an arry for reference
    let predicate = NSPredicate(value: true)
    let query = CKQuery(recordType: RemoteRecords.pet, predicate: predicate)
    ArendK9DB.share.privateDB.perform(query, inZoneWith: ArendK9DB.share.zoneID, completionHandler: {(records: [CKRecord]?, e: Error?)  in
        if e != nil {
            print(e?.localizedDescription ?? "General Query Error: No Description")
        } else {
            guard let records = records else {
                return
            }
            print("DEBUG Fetching from CloudKit... ending completion handler with records = \(records)")
            completion(records, nil)
            }
        }
    )


}

//MARK: Save new records and delete records for multiple Pets
static func saveUpdateCloud(petsToSave: [CKRecord]?, recordIDsToDeleve: [CKRecordID]?,
    completion: @escaping (
    _ results: Bool,
    _ error: NSError?) -> ())
{
    //Execute the operation
    var savedRecordNames = [String]()
    let saveOperation = CKModifyRecordsOperation(recordsToSave: petsToSave, recordIDsToDelete: recordIDsToDeleve)
    saveOperation.perRecordCompletionBlock = {
        record, error in
        if error != nil {
            print(error!.localizedDescription)
        } else {
            print("Saving Record to Cloud: \(record)")
            savedRecordNames.append(record.recordID.recordName)
        }
    }

    saveOperation.completionBlock = {
        print("DEBUG In the completion block therefore isFinished is \(saveOperation.isFinished) at \(Date())")
        completion(saveOperation.isFinished, nil)

    }
    ArendK9DB.share.privateDB.add(saveOperation)
}

1 个答案:

答案 0 :(得分:1)

如果您使用CKFetchRecordZoneChangesOperation,那么该操作附带的完成块会触发已删除记录和已更改记录。这是一个例子:

let options = CKFetchRecordZoneChangesOptions()
options.previousServerChangeToken = previousChangeToken //<-- this is a local value you store to keep track of changes

let operation = CKFetchRecordZoneChangesOperation(recordZoneIDs: [recordZoneID], optionsByRecordZoneID: [recordZoneID:options])

operation.recordChangedBlock = { record in
  //Changed records available in `record`
  //Use a function here and pass the changed record to your local storage
}

operation.recordWithIDWasDeletedBlock = { recordId, type in
  //Deleted recordIds show up here
  //Here is where you line up your recordId.recordName with the recordNames in your local cache
}

operation.recordZoneChangeTokensUpdatedBlock = { (zoneId, token, data) in
  // Save new zone change token to disk
  previousChangeToken = token
}

operation.recordZoneFetchCompletionBlock = { (zoneId, token, _, _, error) in
  if let error = error {
    //Check error and try again
  }
  // Write this new zone change token to disk (again)
  previousChangeToken = token
}

operation.fetchRecordZoneChangesCompletionBlock = { (error) in
  if let error = error {
    //Check error and try again
  }
  //You can fire a callback here if we ever need to know when the sync is done
  print("All done fetching changed records!")
}

//This is whichever CloudKit database you are using
CKContainer(identifier: "...").privateCloudDatabase.add(operation)

所有这一切预先假定您已设置通知以在发生这些更改时进行更改(这可能比一次获取所有内容更有效)。

我希望有所帮助。 :)