作者:一枝红杏出墙来2001 | 来源:互联网 | 2023-02-09 12:44
我的应用程序中有2个视图控制器.
第一个"MainViewController"显示一个tableView,其中包含从私有CloudKit数据库获取的CKRecords字段.在此VC的viewWillAppear方法中,我从CloudKit获取记录并重新加载tableview的数据,以显示先前由用户保存在CloudKit中的最新获取结果.
第二个视图控制器"CreateRecordViewController"用于创建CKRecords并将它们保存到CloudKit的私有数据库.
所以我在CreateRecordViewController中创建记录并在MainViewController中显示它们.
问题如下:当我在CreateRecordViewController创建记录时,它在CloudKit服务器上保存,但在关闭此CreateRecordViewController并转到MainViewController之后,tableview并不总是及时更新.
这是我的CreateRecordViewController中保存记录的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | CloudKitManager.sharedInstance.privateDatabase.save(myRecord) { (savedRecord, error) -> Void in
if error == nil {
print( "successfully saved record code: \(savedRecord)" )
}
else {
// Insert error handling
print( "error Saving Data to iCloud: \(error.debugDescription)" )
}
}
|
记录保存后,关闭CreateRecordViewController并查看MainViewController.
正如我之前在MainViewController的viewWillAppear中所述,我检查iCloud是否可用,如果它可用,我从CloudKit获取所有记录并在tableView中显示它们.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | override func viewWillAppear(_ animated: Bool) {
CKContainer.default().accountStatus { (accountStatus, error) in
switch accountStatus {
case .noAccount: print( "CloudKitManager: no iCloud Alert" )
case .available:
print( "CloudKitManager: checkAccountStatus : iCloud Available" )
self.loadRecordsFromiCloud()
case .restricted:
print( "CloudKitManager: checkAccountStatus : iCloud restricted" )
case .couldNotDetermine:
print( "CloudKitManager: checkAccountStatus : Unable to determine iCloud status" )
}
}
}
|
在loadRecordsFromiCloud()中,当查询成功显示最新结果时,我也重新加载tableview异步.
我的MainViewController中的loadRecordsFromiCloud方法如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | func loadRecordsFromiCloud() {
// Get a private Database
let privateDatabase = CloudKitManager.sharedInstance.privateDatabase
let predicate = NSPredicate(value: true )
let query = CKQuery(recordType: "MyRecords" , predicate: predicate)
let operation = CKQueryOperation(query: query)
privateDatabase.perform(query, inZoneWith: nil) { (results, error) in
if ((error) != nil) {
// Error handling for failed fetch from public database
print( "error loading : \(error)" )
}
else {
// Display the fetched records
//print (results!)
self.tableViewDataArray = results!
DispatchQueue.main.async {
print( "DispatchQueue.main.sync" )
self.tableView.reloadData()
}
}
}
}
|
有时当CloudKit服务器工作得更快时,我可以在tableView中看到新记录,但大多数时候都有延迟(我在MainViewController加载时没有在tableview中看到新结果)我认为这是因为我取记录它取出旧数据(不知道为什么),但也许我犯了另一个错误.这是一个糟糕的用户体验,我想知道如何避免这种延迟.我希望我的tableView在关闭CreateRecordViewController之后显示更新的结果.
我的第一个想法是订阅CloudKit Records更改,并在收到通知时在tableView中获取和重新加载数据,但我真的不需要推送通知(我宁愿在代码中只有一个方法,我可以从中获取数据CloudKit之后我知道所有CloudKit记录都已保存,或者在我知道创建了新记录之后,在获取并获取tableView数据之后我将调用tableView.reloadData,但我不确定如何实现这个权利(用什么方法)并不确定这是否是最好的解决方案.我也听说过在WWDC 2016专用于CloudKit的视频中,现在有一些与订阅Record更改相关的新方法,也许其中一些方法可以帮助(不确定).寻找最佳,或任何好的和简单的解决方案来解决这个问题(延迟问题).
我正在使用XCode 8,iOS 10,swift 3
1> agibson007..:
无法保证记录何时可用于查询,但您可以执行某些操作.您可以重新打印新记录.因为当您创建并保存记录时,您可以创建记录ID,您可以进行ckfetchrecordsoperation并从新记录中传递ID,并保证立即将其恢复.索引有时可能需要一段时间,这对CloudKit来说是令人沮丧的.因此,基本上保证快速数据库的最佳方法是进行查询,如果新记录ID不在那里,则使用id进行提取并将其附加到结果中.希望这是有道理的.
我之前必须这样做,因为我对CK并不太热衷.以下是将记录缝合回来的操作的链接.https://developer.apple.com/reference/cloudkit/ckfetchrecordsoperation如果您正在使用图像,请查看我创建的这个库,它允许您排除图像数据键并按需下载和缓存,可以加快您的查询速度.https://github.com/agibson73/AGCKImage
评论后编辑:
我认为你没有得到的部分是由于索引的工作方式,记录可能会或可能不会在viewcontroller 1中查询.你甚至在你的问题中提到它获取旧数据.这是由于服务器索引.如果删除记录,也会发生同样的情况.它仍然可以在查询中显示一段时间.在这种情况下,您将跟踪最近删除的记录ID,并在查询后将其删除.再次,我正在谈论的手动添加和删除是保证用户看到的内容的唯一方法,并且查询的结果与用户期望的内容保持同步.
这里有一些代码虽然完全未经测试,但我希望能帮助您想象我上面所说的内容.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | func loadRecordsFromiCloud() {
// Get a private Database
let privateDatabase = CKContainer.default().privateCloudDatabase
let predicate = NSPredicate(value: true )
let query = CKQuery(recordType: "MyRecords" , predicate: predicate)
privateDatabase.perform(query, inZoneWith: nil) { (results, error) in
if ((error) != nil) {
// Error handling for failed fetch from public database
print( "error loading : \(error)" )
}
else {
//check for a newRecord ID that might be missing from viewcontroller 2 that was passed back
if self.passedBackNewRecordID != nil{
let newResults = results?.filter({$0.recordID == self.passedBackNewRecordID})
//only excute if there is a new record that is missing from the query
if newResults?.count == 0{
//houston there is a problem
let additiOnalOperation= CKFetchRecordsOperation(recordIDs: [self.passedBackNewRecordID!])
additionalOperation.fetchRecordsCompletiOnBlock= { recordsDict,fetchError in
if let newRecords = recordsDict?.values as? [CKRecord]{
//stitch the missing record back in
let final = newRecords.flatMap({$0}) + results!.flatMap({$0})
self.reloadWithResults(results: final)
self.passedBackNewRecordID = nil
} else {
self.reloadWithResults(results: results)
self.passedBackNewRecordID = nil
}
}
privateDatabase.add(additionalOperation)
} else {
//the new record is already in the query result
self.reloadWithResults(results: results)
self.passedBackNewRecordID = nil
}
} else {
//no new records missing to do additional check on
self.reloadWithResults(results: results)
}
}
}
}
func reloadWithResults(results:[CKRecord]?){
self.tableViewDataArray = results!
DispatchQueue.main.async {
print( "DispatchQueue.main.sync" )
self.tableView.reloadData()
}
}
}
|
这有点乱,但你可以看到我拼错了缺少的recordID,如果不是nil回到你正在做的查询,因为该查询不能实时保证给你预期的新记录.在这种情况下,self.passedBackNewRecordID是根据Viewcontroller 2中的新recordID设置的.你如何设置或跟踪这个变量取决于你,但你可能需要一个完整的队列系统,因为我告诉你的是对记录的更改如何应用以及删除.因此,在生产应用程序中,我必须跟踪具有更改,删除和添加的记录,并获取每个记录的新版本,以便您可以想象对象列表的复杂性.由于我停止使用CloudKit,因为逻辑删除或索引花费的时间太长而无法显示查询中的更改.
测试保存的代码可能如下所示.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | CloudKitManager.sharedInstance.privateDatabase.save(myRecord) { (savedRecord, error) -> Void in
if error == nil {
print( "successfully saved record code: \(savedRecord)" )
//save temporarily to defaults
let recordID = "someID"
UserDefaults.standard. set (recordID, forKey: "recentlySaved" )
UserDefaults.standard.synchronize()
//now we can dismiss
}
else {
// Insert error handling
print( "error Saving Data to iCloud: \(error.debugDescription)" )
}
}
|
在您在视图控制器1中调用查询的代码可能是viewWillAppear,您可以调用它
1 2 3 4 5 6 7 8 9 10 11 | func startQuery(){
UserDefaults.standard.synchronize()
if let savedID = UserDefaults.standard.value(forKey: "recentlySaved" ) as? String{
passedBackNewRecordID = CKRecordID(recordName: savedID)
//now we can remove from Userdefualts
UserDefaults.standard.removeObject(forKey: "recentlySaved" )
UserDefaults.standard.synchronize()
}
self.loadRecordsFromiCloud()
}
|
这应该非常贴合您的示例,并允许您测试我所说的内容,可能只进行微小的更改.