我正在尝试将记录保存到CloudKit但是收到错误。我曾在其他地方看到这是一个需要知道如何保存的问题,但我无法让它发挥作用。
var database:CKDatabase = CKContainer.defaultContainer().publicCloudDatabase
var aRecord:CKRecord!
if self.cloudId == nil {
var recordId:CKRecordID = CKRecordID(recordName: "RecordId")
self.cloudId = recordId // Setup at top
}
aRecord = CKRecord(recordType: "RecordType", recordID: self.cloudId)
aRecord.setObject(self.localId, forKey: "localId")
// Set the normal names etc
aRecord.setObject(self.name, forKey: "name")
var ops:CKModifyRecordsOperation = CKModifyRecordsOperation()
ops.savePolicy = CKRecordSavePolicy.IfServerRecordUnchanged
database.addOperation(ops)
database.saveRecord(aRecord, completionHandler: { (record, error) in
if error != nil {
println("There was an error \(error.description)!")
} else {
var theRecord:CKRecord = record as CKRecord
self.cloudId = theRecord.recordID
}
})
这给了我错误:
There was an error <CKError 0x16d963e0: "Server Record Changed" (14/2017); "Error saving record <CKRecordID: 0x15651730; xxxxxx:(_defaultZone:__defaultOwner__)> to server: (null)"; uuid = 369226C6-3FAF-418D-A346-49071D3DD70A; container ID = "iCloud.com.xxxxx.xxxx-2">!
不确定,因为我已经添加了CKModifyRecordsOperation。可悲的是,Apple的文档中没有任何示例。我想念(你在MSDN上得到的)。
谢谢偷看!
答案 0 :(得分:24)
可以使用CKDatabase的便捷方法saveRecord:
或通过CKModifyRecordsOperation将记录保存到iCloud。如果它是单个记录,您可以使用saveRecord:
,但需要在将其保存回iCloud之前使用fetchRecordWithID:
来修改您想要修改的记录。否则,它只允许您使用新的RecordID保存记录。 More here.
database.fetchRecordWithID(recordId, completionHandler: { record, error in
if let fetchError = error {
println("An error occurred in \(fetchError)")
} else {
// Modify the record
record.setObject(newName, forKey: "name")
}
}
database.saveRecord(aRecord, completionHandler: { record, error in
if let saveError = error {
println("An error occurred in \(saveError)")
} else {
// Saved record
}
}
上面的代码只是方向正确但不会按原样运行,因为当fetchRecordWithID的completionHandler返回时,saveRecord已经被解雇了。一个简单的解决方案是将saveRecord嵌套在fetchRecordWithID的completionHandler中。一个可能更好的解决方案是将每个调用包装在NSBlockOperation
中,并将它们添加到NSOperationQueue中,saveOperation依赖于fetchOperation。
这部分代码适用于CKModifyRecordsOperation
,如果您只更新单个记录则不需要:
var ops:CKModifyRecordsOperation = CKModifyRecordsOperation()
ops.savePolicy = CKRecordSavePolicy.IfServerRecordUnchanged
database.addOperation(ops)
如果您确实使用了CKModifyRecordsOperation
,那么当使用现有记录检测到冲突时,您还需要设置至少一个完成块并处理错误:
let saveRecordsOperation = CKModifyRecordsOperation()
var ckRecordsArray = [CKRecord]()
// set values to ckRecordsArray
saveRecordsOperation.recordsToSave = ckRecordsArray
saveRecordsOperation.savePolicy = .IfServerRecordUnchanged
saveRecordsOperation.perRecordCompletionBlock { record, error in
// deal with conflicts
// set completionHandler of wrapper operation if it's the case
}
saveRecordsOperation.modifyRecordsCompletionBlock { savedRecords, deletedRecordIDs, error in
// deal with conflicts
// set completionHandler of wrapper operation if it's the case
}
database.addOperation(saveRecordsOperation)
除了CloudKitAtlas演示应用程序之外,还有很多示例代码,它位于Objective-C中。希望这会有所帮助。
答案 1 :(得分:19)
一般来说,你有单一的方法(比如 saveRecord ),它一次只处理一条记录,而群众操作(喜欢 CKModifyRecordsOperation ),它同时处理几条记录。
这些保存操作可用于保存记录,或更新记录(即,获取它们,对其应用更改,然后再次保存)。
您创建了一条记录,并希望将其保存到CloudKit DB:
let database = CKContainer.defaultContainer().publicCloudDatabase
var record = CKRecord(recordType: "YourRecordType")
database.saveRecord(record, completionHandler: { (savedRecord, saveError in
if saveError != nil {
println("Error saving record: \(saveError.localizedDescription)")
} else {
println("Successfully saved record!")
}
})
您创建了一堆记录,并希望一次保存所有记录:
let database = CKContainer.defaultContainer().publicCloudDatabase
// just an example of how you could create an array of CKRecord
// this "map" method in Swift is so useful
var records = anArrayOfObjectsConvertibleToRecords.map { $0.recordFromObject }
var uploadOperation = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil)
uploadOperation.savePolicy = .IfServerRecordUnchanged // default
uploadOperation.modifyRecordsCompletionBlock = { savedRecords, deletedRecordsIDs, error in
if error != nil {
println("Error saving records: \(error.localizedDescription)")
} else {
println("Successfully saved records")
}
}
database.addOperation(uploadOperation)
通常,您有3种情况需要更新记录:
你知道有一个唯一的记录要更新,但你不知道它的recordID:在这种情况下,你将使用一个查询方法 performQuery ,选择您需要的(仅)一个 saveRecord
您正在处理要更新的许多记录:在这种情况下,您将使用查询来获取所有记录 ( performQuery )和 CKModifyRecordsOperation 以全部保存。
案例1 - 您知道要更新的记录的唯一标识符:
let myRecordName = aUniqueIdentifierForMyRecord
let recordID = CKRecordID(recordName: myRecordName)
database.fetchRecordWithID(recordID, completionHandler: { (record, error) in
if error != nil {
println("Error fetching record: \(error.localizedDescription)")
} else {
// Now you have grabbed your existing record from iCloud
// Apply whatever changes you want
record.setObject(aValue, forKey: attributeToChange)
// Save this record again
database.saveRecord(record, completionHandler: { (savedRecord, saveError) in
if saveError != nil {
println("Error saving record: \(saveError.localizedDescription)")
} else {
println("Successfully updated record!")
}
})
}
})
案例2 - 您知道有一条与您的条件相对应的记录,并且您想要更新它:
let predicate = yourPredicate // better be accurate to get only the record you need
var query = CKQuery(recordType: YourRecordType, predicate: predicate)
database.performQuery(query, inZoneWithID: nil, completionHandler: { (records, error) in
if error != nil {
println("Error querying records: \(error.localizedDescription)")
} else {
if records.count > 0 {
let record = records.first as! CKRecord
// Now you have grabbed your existing record from iCloud
// Apply whatever changes you want
record.setObject(aValue, forKey: attributeToChange)
// Save this record again
database.saveRecord(record, completionHandler: { (savedRecord, saveError in
if saveError != nil {
println("Error saving record: \(saveError.localizedDescription)")
} else {
println("Successfully updated record!")
}
})
}
}
})
案例3 - 您想要抓取多条记录,并立即更新所有记录:
let predicate = yourPredicate // can be NSPredicate(value: true) if you want them all
var query = CKQuery(recordType: YourRecordType, predicate: predicate)
database.performQuery(query, inZoneWithID: nil, completionHandler: { (records, error) in
if error != nil {
println("Error querying records: \(error.localizedDescription)")
} else {
// Now you have grabbed an array of CKRecord from iCloud
// Apply whatever changes you want
for record in records {
record.setObject(aValue, forKey: attributeToChange)
}
// Save all the records in one batch
var saveOperation = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil)
saveOperation.savePolicy = .IfServerRecordUnchanged // default
saveOperation.modifyRecordsCompletionBlock = { savedRecords, deletedRecordsIDs, error in
if error != nil {
println("Error saving records: \(error.localizedDescription)")
} else {
println("Successfully updated all the records")
}
}
database.addOperation(saveOperation)
}
})
现在,这是对你的问题的一个长篇答案,但你的代码混合了单一的保存方法和CKModifyRecordsOperation。
此外,您必须了解,每次创建CKRecord时,CloudKit都会为其提供唯一标识符(record.recordID.recordName),除非您自己提供。所以你必须知道你是否想要获取现有记录,或者在调用所有这些漂亮方法之前创建一个新记录:-) 如果您尝试使用与另一个相同的唯一标识符创建新的CKRecord,那么您肯定会收到错误。
答案 2 :(得分:1)
我有同样的错误,但是我已经按照Guto所描述的那样通过ID获取记录了。事实证明我多次更新同一条记录,事情变得不同步。
我有一个更新和保存方法,主线程有时会快速调用它。
我正在使用块并立即保存,但如果您快速更新记录,则可能会出现以下情况:
更新A&#39;&#39;将失败,因为记录已在服务器上更新。
我通过确保等待更新记录来解决这个问题,如果我正在更新记录中。