CoreData通过CloudKit同步公开数据库
您好,欢迎来到WWDC。
嗨,我叫尼克·吉列(Nick Gillett)。我是苹果公司核心数据团队的一名工程师,今天我们将讨论使用NSPersistentCloudKitContainer构建应用程序(将核心数据存储与公共CloudKit数据库同步)的感觉。为此,我们将引入一些新的API,并且会讨论很多有关为公共数据库构建应用程序时需要考虑的事项。最后,我们将详细研究如何使用公共数据库中的NSPersistentCloudKitContainer进行导入。
现在,在我们开始之前,我想快速回顾一下到目前为止。在2019年的“使用CloudKit使用核心数据”会话中,我们引入了NSPersistentCloudKitContainer,以轻松地将您的核心数据存储与私有CloudKit数据库同步。同样,我们引入了一个新的示例应用程序来演示其工作方式,并且编写了大量有关NSPersistentCloudKitContainer如何工作以及如何将其与应用程序集成的文档。因此,如果在本届会议期间的任何时候觉得您缺乏上下文或我们正在讨论的某些概念感到陌生,我强烈建议您阅读我们的文档和上一届会议。我也想复习一些条款。您会看到,Core Data和CloudKit是非常相似的框架,它们通过相似的想法和API来表达自己。我们从对象,模型和存储方面考虑每个框架。在Core Data中,我们将它们称为NSManagedObject的实例,并且它们是您直接在应用程序中使用的对象。同样在CloudKit中,我们将它们称为CKRecord。
我们为这些对象建模或使用NSManagedObjectModel的实例描述它们,在CloudKit中,这称为架构。最后,在NSPersistentStore实例中或在CloudKit中,作为CKRecord数据库一部分的CKRecordZone,对象是持久性的(使用本地数据)。现在我们已经解决了这个问题,我想向您展示实际构建应用程序的感觉,或者在这种情况下,定制与公共CloudKit数据库一起使用的应用程序。为此,我将打开示例应用程序,并在寻找一个名为Core Data Stack的特定文件。您会看到Core Data堆栈是该应用程序保留设置其NSPersistentCloudKitContainer实例所需的所有代码的地方。您可以在顶部看到它已经在使用NSPersistentCloudKitContainer,这意味着它已经准备好与私有数据库同步。现在,要使其与公共数据库一起使用,我们只想添加一行新代码。我只是要更改现有商店描述以设置其cloudKitContainerOptions,并使用新的databaseScope属性将其设置为public。
您可以看到我刚刚粘贴了很多代码,因此让我们详细了解一下这里实际发生的情况。在这段代码中,我们创建了一个新的NSPersistentStoreDescription实例,并使用常规的cloudKitContainerOptions对其进行了自定义-历史跟踪和远程更改通知之类的东西。
然后,我们创建一个新的NSPersistentCloudKitContainerOptions实例。
这就是告诉NSPersistentCloudkitContainer我们要将此存储描述与CloudKit一起使用的原因。我们将其databaseScope属性设置为public,这表明它要与公共数据库一起使用。
接下来,我们将cloudKitContainerOptions分配给商店描述,并将商店描述附加到我们希望NSPersistentCloudKitContainer加载的商店数组中。最后,我们像平时一样加载商店。现在,我想说的就是所有要做的,但是我们必须配置CloudKit容器以使其与NSPersistentCloudKitContainer一起正常工作。您会看到它在公共数据库中获取数据的方式有所不同。因此,我现在想使用CloudKit仪表板来更改架构的配置。为此,我要打开Safari,您会看到我已经有需要在Safari中打开的页面。因此,让我们将自己定位在当前位置。在顶部,您可以看到我已经为示例应用程序选择了iCloud容器,并且我们处于开发环境中,专门研究架构。我们正在查看模式的索引部分,因为我们需要向您在屏幕左侧列出的所有五种记录类型中添加一些索引。我们需要为每个记录类型添加两个新索引,然后,我将在此处使用此“添加索引”按钮。我将单击一次,为recordName添加一个,然后为ModifyAt日期添加另一个,最后我将保存更改。这些是NSPersistentCloudKitContainer对公共数据库运行其查询以获取此类型的记录所需的索引。当然,现在对于您的应用程序,您将必须对所有记录类型重复这些步骤,因此我们将对示例应用程序再做四遍。这就是我们构建与公共CloudKit数据库一起使用的应用程序所需要做的全部工作。
我们只需采用NSPersistentCloudKitContainerOptions的新databaseScope属性,并为所有记录类型添加几个索引。现在,如果您没有在CloudKit仪表板中看到所有索引,则可能需要执行一个称为架构初始化的过程。
模式初始化将在我们的文档以及2019年的会议中详细介绍。完成此操作后,您将在运行应用程序的设备上拥有CloudKit中所有数据的完整本地镜像。
现在您可能想知道为什么我们要创建一个完整的本地镜像。原因是-您要求我们这样做。您会看到,自2019年我们的演讲以来,我们收到了许多功能请求以支持公共数据库,并且它们有一些共同的主题。首先是所有请求都希望创建一个每个人都可以使用的数据集-也就是说,应用程序的所有用户都可以访问。现在有时这是您(开发人员)将创建的数据,例如应用程序模板或初始数据集,因此用户从第一天开始就对您的应用程序具有丰富的经验。在其他情况下,则是用户创建的数据。现在,我将在这里坦率。为此,我们最常见的要求是在视频游戏中获得高分,这实际上是我们希望NSPersistentCloudKitContainer支持的一个很好的例子。您需要本地所有数据,以便您可以快速获取,排序,排序数据,而这正是您的用户想要贡献的。我们也知道您想根据这些应用程序的需要来混合私有数据库中的数据。这很有道理。您的用户可能想要上载他们的高分,但他们可能不想上载他们保存的游戏状态或角色配置到公共数据库。而要同步,您将使用私有数据库。
这实际上是我们下一节的主要内容,是在构建与公共数据库一起使用的应用程序时需要考虑的特殊注意事项或事项。为了深入探讨这一点,我们将比较和对比使用私有数据库和公共数据库时NSPersistentCloudKitContainer的行为。
我们要看的第一件事是帐户和所有权的概念,以及两者之间的关系。您会看到,在私有数据库中,必须有帐户,并且所有数据都由一个用户拥有,因此行为相当简单,但公共数据库却无法正常工作。
帐户是可选的。您可以在没有iCloud帐户的情况下从公共数据库读取数据,并且该数据可以由您的应用程序的任何用户拥有。
我们还将讨论导入以及公共数据库和私有数据库之间的导入方式。您会看到私有数据库支持推送通知,而公共数据库则不支持,因此我们必须使用称为轮询的过程来查询要导入的记录。现在我想在这里喊出口,因为我们不会谈论它。在私有数据库和公共数据库之间,它的工作方式相同。我们在2019年的会议中详细讨论了此问题。当我们考虑帐户和所有权时,我们真正要谈论的是登录或注销iCloud时应用程序可以执行的一组操作帐户。现在,正如我所说,在私有数据库中,这非常简单。如果您已退出,则无法执行任何操作。如果您已登录,则可以执行所有操作。世界是您的牡蛎,所有数据都是您的工作之本。但是在公共数据库中,这变得更加复杂。如果您已登出或登录,则可以读取数据,但是如果您登录到iCloud帐户,则只能创建数据-新记录。那修改呢?好吧,这很棘手。对于像我们的示例应用程序那样具有UI的应用程序,这带来了一个问题。
也许您有一些编辑控件,例如,左上角的“编辑”按钮或右上角的“ +”按钮,使我们可以创建新记录。同样,我们的示例应用程序具有一个Detail视图控制器,该控制器允许我们编辑单个对象,并且还具有编辑控件。我们的应用程序可能需要根据该对象的存储位置或该对象所在的数据库来知道是否可以使用这些控件中的一个或任何一个。事实证明,可以使用NSPersistentCloudKitContainer的现有API来解决此问题。您只需向CloudKit询问当前帐户即可。然后,您要求NSPersistentCloudKitContainer提供支持您正在使用的对象的记录。而且,如果您比较创建者的UserID,那么您将知道该对象是否可变。
但是,仅要将这个问号变成是或否,这是荒谬的代码量,并且对于应用程序中的每个用户界面元素而言,这都是疯狂的代码量。这样我们可以做得更好。我们有。这是NSPersistentCloudKitContainer的新canUpdateRecord forManagedObjectWithID方法,该方法确切地告诉我们我们需要了解关于该对象相对于设备的当前配置是否可变,该对象所保存的持久存储以及该存储是否为可变对象由CloudKit支持。它甚至可以处理您可能期望的所有极端情况,例如将对象存储在完全不受CloudKit支持的商店中,或者在您使用应用程序时在设备上更改帐户。并且我们缓存了此状态,以使其在用户界面中足够有效。现在,如果您有一个更核心的世界屏幕视图(例如我们的应用程序中的此表视图),则可以在存储中使用NSPersistentCloudKitContainer的canModifyObjects来判断给定持久性存储中的任何对象是否相对于设备上的配置以及该商店是否受CloudKit支持。现在,我想稍作调整,并详细讨论如何在NSPersistentCloudKitContainer的公共数据库中进行导入。为此,我想回顾一下我们在2019年的会议中的一些内容。您可能还记得这张图,其中我们讨论了如何从CloudKit收到推送通知时,我们安排了一个导入操作,该操作将所有已更改的记录汇总到本地商店,并使它们可用于您的应用程序。
即使您有大量的更改记录集或非常复杂的对象图,此方法也能很好地工作。无论您使用的是公共数据库还是私有数据库,NSPersistentCloudKitContainer都能有效地导入复杂的图形和较大的更改集。为了了解它们之间的不同,我们想看看云与本地持久存储之间到底发生了什么。当我说我们创建导入操作时,我的真正意思是我们创建了CKFetchRecordsZoneChangesOperation的实例,并且这对CloudKit服务器创建了一个请求,该请求从私有数据库中删除了所有已更改的记录。但是CKFetchRecordsZoneChangesOperation依赖于专用于私有数据库的某些技术,因此在公共数据库中,我们必须改用CKQueryOperation。现在,如果您曾经使用过CKQueryOperation,您就会知道查询会影响特定的记录类型,这意味着我们必须针对该帖子发出一个请求,针对其标签发出另一个请求..对于附件发出另一个请求...要求提供图片...以及另一个要求多对多关系的请求。等等。它一直持续到我们获取应用程序中的所有记录类型为止。
这意味着由公共数据库支持的存储的公共数据库正在做更多的工作。这就是我们将数据导入公共数据库的方式。
NSPersistentCloudKitContainer必须使用CKQueryOperation而不是CKFetchRecordsZoneChangesOperation,并且我们必须轮询更改,而不是使用推送通知来确切知道何时发出请求。
现在这意味着我们需要谨慎对待如何加载CloudKit服务器,因为我们希望您的应用程序随公共数据库扩展的方式。
因此,我们仅在应用程序启动时或大约每30分钟使用应用程序后轮询一次更改。这是为了确保我们将这些请求的负载与您的应用程序的实际使用情况保持一致。
当然,这确实意味着您可以从公共数据库获得的新鲜度质量与私有数据库明显不同。
即使您有大量的更改记录集或非常复杂的对象图,此方法也能很好地工作。无论您使用的是公共数据库还是私有数据库,NSPersistentCloudKitContainer都能有效地导入复杂的图形和较大的更改集。为了了解它们之间的不同,我们想看看云与本地持久存储之间到底发生了什么。当我说我们创建导入操作时,我的真正意思是我们创建了CKFetchRecordsZoneChangesOperation的实例,并且这对CloudKit服务器创建了一个请求,该请求从私有数据库中删除了所有已更改的记录。但是CKFetchRecordsZoneChangesOperation依赖于专用于私有数据库的某些技术,因此在公共数据库中,我们必须改用CKQueryOperation。现在,如果您曾经使用过CKQueryOperation,您就会知道查询会影响特定的记录类型,这意味着我们必须针对该帖子发出一个请求,针对其标签发出另一个请求..对于附件发出另一个请求...要求提供图片...以及另一个要求多对多关系的请求。等等。它一直持续到我们获取应用程序中的所有记录类型为止。
这意味着由公共数据库支持的存储的公共数据库正在做更多的工作。这就是我们将数据导入公共数据库的方式。
NSPersistentCloudKitContainer必须使用CKQueryOperation而不是CKFetchRecordsZoneChangesOperation,并且我们必须轮询更改,而不是使用推送通知来确切知道何时发出请求。
现在这意味着我们需要谨慎对待如何加载CloudKit服务器,因为我们希望您的应用程序随公共数据库扩展的方式。
因此,我们仅在应用程序启动时或大约每30分钟使用应用程序后轮询一次更改。这是为了确保我们将这些请求的负载与您的应用程序的实际使用情况保持一致。
当然,这确实意味着您可以从公共数据库获得的新鲜度质量与私有数据库明显不同。
现在,我还要提及的是,这意味着更简单的托管对象模型(即其中包含较少实体的托管对象模型)对公共数据库的请求将更少。这并不是说您的应用程序中不应包含复杂的对象图,或者公共数据库不支持它们。实际上,您绝对应该。您应该构建公共数据库所需的管理对象模型,但这确实意味着您应该将该模型限制为仅在公共数据库中使用的实体,这样NSPersistentCloudKitContainer不会发出不必要的请求。现在,我们使用管理对象模型中的配置来执行此操作,并在文档中详细讨论有关如何设置要在NSPersistentCloudKitContainer中使用的管理对象模型的内容。您想将使用公共数据库的实体限制为该存储的特定配置。现在为什么有什么关系呢?好吧,因为查询会影响删除如何在整个公共数据库中传播。考虑以下对象集。
如果我们将这些对象导入到两个设备上,则这两个设备将像我们期望的那样对云中的内容完全相同。对?在私有数据库中,如果我们从一台设备上删除一个对象,然后将该删除操作导出到云中,则会留下一个称为墓碑的外壳,其中包含记录类型和已删除对象的记录ID。
现在,当我们从私有数据库中导入更改并在另一台设备上删除该记录时,这使我们能够通过CKFetchRecordZoneChangesOperation来获取该逻辑删除,因此两台设备再次呈现出云中的相同图片。但是,正如我所说,NSPersistentCloudKitContainer不能将CKFetchRecordsZoneChangesOperation与公共数据库一起使用。
它必须使用CKQueryOperation。
因此,当我们从一台设备删除记录并将该删除内容导出到公共数据库时,该记录将立即被删除。通过查询操作,我们询问公共数据库发生了什么变化。它什么也没说。
没有遗留下来供我们取用。这意味着我们无法将删除的内容传播到另一台设备,它们将对云中的内容有不同的了解。这给应用程序带来了潜在的问题,尤其是当它们具有这样的用户界面时,例如在表视图中我们可以滑动删除。滑动删除该对象时,该删除不会在公共数据库中传播到已下载此对象的所有设备。事实证明,使用CKRecordZone上的API(称为功能)可以完全检测到这种情况的发生。
如果某个区域不支持fetchChanges功能,则无法使用CKFetchRecordsZoneChangesOperation对其中的重要数据进行处理。我们必须改为使用查询。但这需要花费大量的代码才能集成到您的应用程序中,我什至不用费心把它放到幻灯片上。相反,我们可以使用NSPersistentCloudKitContainer的新canDeleteRecord(forManagedObjectWithID)方法在一行代码中检测到这一点。如果返回false,则意味着该记录存储在公共数据库中,并且NSPersistentCloudKitContainer无法以与从私有数据库相同的方式导入删除。因此,删除操作不会传播到已下载此对象的所有设备。现在,这并不意味着删除根本不起作用。因为NSPersistentCloudKitContainer必须使用CKQueryOperation来获取对象,所以您需要确定应用程序是尝试删除对象还是要从UI中删除对象。现在,这并不意味着您无法删除。实际上,您绝对需要删除数据,但是为了从公共数据库中删除数据和从应用程序的用户界面中删除数据,删除数据之间存在区别。在这段代码中,我们使用canDeleteRecord(forManagedObjectWithID)告诉我们是否应该更新一条记录以将其从用户界面中删除,而不是删除它。为此,当我们无法从公共数据库中删除某些内容时,我们将此标签上的isTrashed属性设置为“ true”,并且在我们的应用程序中,我们使用带有谓词的访存请求,该谓词过滤掉所有被破坏的记录以将其从中删除。用户界面。这样,我们使用更新或修改来代替删除,以在我们的用户界面中实现所需的效果。现在,我们可以进一步推广这种范例。举例来说,将记录中的所有字段一经删除就不再需要了。这样可以保留...从而回收公共数据库以及用户设备磁盘上的空间,并且一旦您确信所有下载的这些记录已经处理了此更新。因此,您在应用程序中真正需要确定的是-是否要从公共数据库中删除某些内容,以便没人再次下载?还是要从UI中提取某些内容?在本课程中,我们学到了很多有关NSPersistentCloudKitContainer的知识,包括其新的databaseScope API,该API允许您配置是将商店用于公共数据库还是私有数据库。我们详细研究了公共数据库对您的应用程序意味着什么。最后,我们了解了其他一些NSPersistentCloudKitContainer API,以及它们如何帮助您简化其中的一些注意事项。
这就是关于NSPersistentCloudKitContainer和公共数据库的全部内容。
推荐文章
CoreData篇
- SwiftUI数据存储之做个笔记App 新增与查询(CoreData)
- SwiftUI进阶之存储用户状态实现登录与登出
- SwiftUI 数据之List显示Sqlite数据库内容(2020年教程)
Combine篇
- 一篇文章学懂弄通SwiftUI与Combine(含轮播动画App源码)
TextField篇
- 《SwiftUI 一篇文章全面掌握TextField文本框 (教程和全部源码)》
- 《SwiftUI实战之TextField风格自定义与formatters》
- 《SwiftUI实战之TextField如何给键盘增加个返回按钮(隐藏键盘)》
- 《SwiftUI 当键盘出现时避免TextField被遮挡自动向上移动》
- 《SwiftUI实战之TextField如何给键盘增加个返回按钮(隐藏键盘)》
JSON文件篇
- SwiftUI JSON文件下载、存储、解析和展示(代码大全)
一篇文章系列
- SwiftUI一篇文章全面掌握List(教程和源码)
- 《SwiftUI 一篇文章全面掌握TextField文本框 (教程和全部源码)》
- SwiftUI一篇文章全面掌握Picker,解决数据选择(教程和源码)
- SwiftUI一篇文章全面掌握Form(教程和源码)
- SwiftUI Color 颜色一篇文章全解决
技术交流
QQ:3365059189
SwiftUI技术交流QQ群:518696470
- 请关注我的专栏icloudend, SwiftUI教程与源码
https://www.jianshu.com/c/7b3e3b671970