- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
假设您正在使用 CKFetchRecordZoneChangesOperation
获取任何更改。
您已成功获取所有已更改的CKRecords
,但是,您目前尚未创建本地缓存。但是您确实在 recordZoneChangeTokensUpdatedBlock
中缓存了一个新的更改 token 您正准备为 CKRecords
创建一个本地缓存,但不知何故您的用户决定终止您的应用程序,并且您被终止。
重新启动后,你想重新下载更改,但现在有了新的更改 token ,从新 token 开始,没有任何更改。
如何解决?
有人指出我可以在记录写入本地数据库后缓存 token 。
但这并不总是可能的,因为在应用程序的初始启动时,应用程序可能必须下载大量 CKRecords
如果您不及时处理它们会消耗内存,在另一方面,CKFetchRecordZoneChangesOperation
使用两个不同的 block ,一个用于从服务器获取的新记录,另一个用于 token 更新。因此,您将不得不编写复杂的代码来协调这两个 block 。
var recordChangedBlock
var recordZoneChangeTokensUpdatedBlock
最佳答案
在您更新本地记录缓存之后之前,不要缓存您的更改 token 。对我有用...
要添加的 EDII:
让 CloudKit 同步是一件复杂的事情——至少对我来说是这样!我是 iOS 的新手,我花了很长时间才让我的系统正常工作——通过学习操作和 GCD 走了弯路。您将需要将其拆分为不同的操作或方法才能成功完成。就像您说的那样,同步可以随时中断/取消,您的系统需要对此具有弹性。
我建议不要使用 CKFetchRecordZoneChangesOperation 来处理任何事情,只是用来获取那些结果和标记,然后将它们传递给系统中的下一步。我正在使用操作,并且我有一个 CKFetchRecordZoneChangesOperation 的包装器操作 - 它是获取、修改和上传更改的链中的一个步骤。这不是整个类(class),只是适用于该问题的相关片段,让您了解我的意思。您的代码将(可能非常)不同:
class FetchRecordZoneChangesOperation: AsyncOperation {
// MARK: - Properties
var inputRecordZoneIDs: [CKRecordZoneID]?
var outputCKRecords: [CKRecord]?
var outputDeletedRecordIDs: [CKRecordID]?
var outputServerChangeToken: CKServerChangeToken?
var useServerChangeToken = true
override func main() {
if self.isCancelled {
self.finish()
return
}
if let recordZoneIDsDependency = dependencies
.filter({ $0 is CKRecordZoneIDsProvider })
.first as? CKRecordZoneIDsProvider
, inputRecordZoneIDs == nil {
inputRecordZoneIDs = recordZoneIDsDependency.ckRecordZoneIDs
}
// This record zone stuff is kinda redundant but it works...
var recordZoneID: CKRecordZoneID
var recordZoneIDs: [CKRecordZoneID]
if let zoneIDs = self.inputRecordZoneIDs, let zoneID = zoneIDs.first {
recordZoneID = zoneID
recordZoneIDs = zoneIDs
} else {
recordZoneID = UserDefaults.standard.ckCurrentRecordZoneID
recordZoneIDs = [UserDefaults.standard.ckCurrentRecordZoneID]
}
let operation = CKFetchRecordZoneChangesOperation()
// QOS
operation.qualityOfService = .userInitiated
operation.recordZoneIDs = recordZoneIDs
// if I have a database change token, use that, otherwise I'm getting all records
if useServerChangeToken, let token = UserDefaults.standard.ckRecordZoneChangeToken {
// TODO: This will change when I have more than 1 list
let fetchOptions = CKFetchRecordZoneChangesOptions()
fetchOptions.previousServerChangeToken = token
operation.optionsByRecordZoneID = [ recordZoneID : fetchOptions]
}
operation.recordChangedBlock = { record in
if self.outputCKRecords != nil {
self.outputCKRecords?.append(record)
} else {
self.outputCKRecords = [record]
}
}
operation.recordWithIDWasDeletedBlock = { recordID, somethingStringy in
if self.outputDeletedRecordIDs != nil {
self.outputDeletedRecordIDs?.append(recordID)
} else {
self.outputDeletedRecordIDs = [recordID]
}
}
operation.recordZoneChangeTokensUpdatedBlock = { recordZoneID, serverChangeToken, clientChangeTokenData in
self.outputServerChangeToken = serverChangeToken
}
operation.recordZoneFetchCompletionBlock = { recordZoneID, serverChangeToken, clientChangeTokenData, moreComing, error in
if error != nil {
cloudKit.errorController.handle(error: error, operation: .fetchChanges)
} else {
// Do I need to handle things with the clientChangeTokenData? Working flawlessly without currently. Right now I just store the server one
self.outputServerChangeToken = serverChangeToken
}
}
}
然后下一个操作会在本地修改记录:
class ModifyObjectsOperation: AsyncOperation {
var debug = true
var debugMore = false
var inputCKRecords: [CKRecord]?
var inputDeleteIDs: [CKRecordID]?
var inputRecordZoneChangeToken: CKServerChangeToken?
var inputDatabaseChangeToken: CKServerChangeToken?
override func main() {
if isCancelled {
self.finish()
return
}
if let recordDependency = dependencies
.filter({ $0 is CKRecordsProvider })
.first as? CKRecordsProvider
, inputCKRecords == nil {
inputCKRecords = recordDependency.ckRecords
}
if let recordZoneTokenDependency = dependencies
.filter({ $0 is CKRecordZoneTokenProvider })
.first as? CKRecordZoneTokenProvider
, inputRecordZoneChangeToken == nil {
inputRecordZoneChangeToken = recordZoneTokenDependency.ckRecordZoneChangeToken
}
if let databaseTokenDependency = dependencies
.filter({ $0 is CKDatabaseTokenProvider })
.first as? CKDatabaseTokenProvider
, inputDatabaseChangeToken == nil {
inputDatabaseChangeToken = databaseTokenDependency.ckDatabaseChangeToken
}
if let deleteDependency = dependencies
.filter({ $0 is CKRecordIDsProvider })
.first as? CKRecordIDsProvider
, inputDeleteIDs == nil {
inputDeleteIDs = deleteDependency.ckRecordIDs
}
if self.inputCKRecords == nil && self.inputDeleteIDs == nil {
if self.debug {
print("📚 ModifyObjectsOperation - no changes or deletes 👍🏼")
}
self.finish()
return
}
// NOW MODIFY YOUR RECORDS HERE. IF SUCCESSFUL, CACHE YOUR TOKEN.
}
如果您有大量数据要同步或有大量记录,那么这将需要多次旅行是对的。上传比下载更痛苦...有 200 条记录的上传限制(虽然我发现这并没有严格执行 - 我一次成功上传了多达 400 条小记录)。我发现我可以在一个 block 中下载数千条小记录。
真正知道您是安全的唯一方法是等到您拥有更改、在本地缓存您的更改,然后才保存该 changeToken。您宁愿获得重复数据也不愿在此过程中丢失某些东西。如果你做对了,你永远不会失去一些东西,你最终可能会做一些多余的工作。
编辑 2:
刚刚看到你上面问题的最后一部分。如果您的应用程序依赖于远程数据才能使用,那么您似乎只有几个选择。提供某种他们在使用该应用程序之前必须等待的进度指示器。或者重新构建您所拥有的,以便您可以在拥有该远程数据之前使用该应用程序。它可能不是最终状态,但至少可以使用。
对于我正在处理的工作,初始同步时可能有数万或数十万条记录,在使用该应用程序之前等待所有记录都同步是行不通的。他们可以开始使用该应用程序,并且可以在他们使用和更改基础数据时进行同步,因为它的构建具有弹性。
关于ios - 如果在使用 CKFetchRecordZoneChangesOperation 时应用程序被杀死会怎样?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50354368/
IO 设备如何知道属于它的内存中的值在memory mapped IO 中发生了变化? ? 例如,假设内存地址 0 专用于保存 VGA 设备的背景颜色。当我们更改 memory[0] 中的值时,VGA
我目前正在开发一个使用Facebook sdk登录(通过FBLoginView)的iOS应用。 一切正常,除了那些拥有较旧版本的facebook的人。 当他们按下“使用Facebook登录”按钮时,他
假设我有: this - is an - example - with some - dashesNSRange将使用`rangeOfString:@“-”拾取“-”的第一个实例,但是如果我只想要最后
Card.io SDK提供以下详细信息: 卡号,有效期,月份,年份,CVV和邮政编码。 如何从此SDK获取国家名称。 - (void)userDidProvideCreditCardInfo:(Car
iOS 应用程序如何从网络服务下载图片并在安装过程中将它们安装到用户的 iOS 设备上?可能吗? 最佳答案 您无法控制应用在用户设备上的安装,因此无法在安装过程中下载其他数据。 只需在安装后首次启动应
我曾经开发过一款企业版 iOS 产品,我们公司曾将其出售给大型企业,供他们的员工使用。 该应用程序通过 AppStore 提供,企业用户获得了公司特定的配置文件(包含应用程序配置文件)以启用他们有权使
我正在尝试将 Card.io SDK 集成到我的 iOS 应用程序中。我想为 CardIO ui 做一个简单的本地化,如更改取消按钮标题或“在此保留信用卡”提示文本。 我在 github 上找到了这个
我正在使用 CardIOView 和 CardIOViewDelegate 类,没有可以设置为 YES 的 BOOL 来扫描 collectCardholderName。我可以看到它在 CardIOP
我有一个集成了通话工具包的 voip 应用程序。每次我从我的 voip 应用程序调用时,都会在 native 电话应用程序中创建一个新的最近通话记录。我在 voip 应用程序中也有自定义联系人(电话应
iOS 应用程序如何知道应用程序打开时屏幕上是否已经有键盘?应用程序运行后,它可以接收键盘显示/隐藏通知。但是,如果应用程序在分屏模式下作为辅助应用程序打开,而主应用程序已经显示键盘,则辅助应用程序不
我在模拟器中收到以下错误: ImageIO: CGImageReadSessionGetCachedImageBlockData *** CGImageReadSessionGetCachedIm
如 Apple 文档所示,可以通过 EAAccessory Framework 与经过认证的配件(由 Apple 认证)进行通信。但是我有点困惑,因为一些帖子告诉我它也可以通过 CoreBluetoo
尽管现在的调试器已经很不错了,但有时找出应用程序中正在发生的事情的最好方法仍然是古老的 NSLog。当您连接到计算机时,这样做很容易; Xcode 会帮助弹出日志查看器面板,然后就可以了。当您不在办公
在我的 iOS 应用程序中,我定义了一些兴趣点。其中一些有一个 Kontakt.io 信标的名称,它绑定(bind)到一个特定的 PoI(我的意思是通常贴在信标标签上的名称)。现在我想在附近发现信标,
我正在为警报提示创建一个 trigger.io 插件。尝试从警报提示返回数据。这是我的代码: // Prompt + (void)show_prompt:(ForgeTask*)task{
您好,我是 Apple iOS 的新手。我阅读并搜索了很多关于推送通知的文章,但我没有发现任何关于 APNS 从 io4 到 ios 6 的新更新的信息。任何人都可以向我提供 APNS 如何在 ios
UITabBar 的高度似乎在 iOS 7 和 8/9/10/11 之间发生了变化。我发布这个问题是为了让其他人轻松找到答案。 那么:在 iPhone 和 iPad 上的 iOS 8/9/10/11
我想我可以针对不同的 iOS 版本使用不同的 Storyboard。 由于 UI 的差异,我将创建下一个 Storyboard: Main_iPhone.storyboard Main_iPad.st
我正在写一些东西,我将使用设备的 iTunes 库中的一部分音轨来覆盖 2 个视频的组合,例如: AVMutableComposition* mixComposition = [[AVMutableC
我创建了一个简单的 iOS 程序,可以顺利编译并在 iPad 模拟器上运行良好。当我告诉 XCode 4 使用我连接的 iPad 设备时,无法编译相同的程序。问题似乎是当我尝试使用附加的 iPad 时
我是一名优秀的程序员,十分优秀!