我正在尝试在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)
}
答案 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)
所有这一切预先假定您已设置通知以在发生这些更改时进行更改(这可能比一次获取所有内容更有效)。
我希望有所帮助。 :)