Core Data with CloudKit(三)——CloudKit仪表台
Core Data with CloudKit(三)——CloudKit仪表台
本系列文章一共六篇,如果想获得更好的阅读体验可以访问我的博客 www.fatbobman.com[1]
本篇文章中,我们将一起研究CloudKit
仪表台。
初识仪表台
使用CloudKit Dashboard
需要开发者拥有Apple Developer Program[2]账号,访问https://icloud.developer.apple.com即可使用。
近两年苹果对CloudKit仪表台
的布局做过较大的调整,上面的截图是2021年中时的样子。
仪表台主要分为三个部分:
•数据库(CloudKit Database
)数据库Web
客户端。涵盖管理Schema
、Record
、Zone
、用户权限、容器环境等功能。•遥测(Telemetry
)使用直观的可视化效果,深入了解应用程序的服务器端性能以及跨数据库和推送事件的利用率。•日志(Logs
)CloudKit 服务器生成实时和历史日志,记录并显示应用程序和服务器之间的交互。
在绝大多数使用
Core Data with CloudKit
的场景下,我们仅需要使用仪表板中极少数的功能(环境部署),但利用CloudKit Dashboard
,我们可以更清楚的了解Core Data
数据同步背后运作的一些机制。
数据库(CloudKit Database)
在Core Data with CloudKit (一) —— 基础[3]中已经对CKContainer
、CKDababase
、CKZone
、CKSubscription
、CKRecord
等基础对象做了简单的说明,本文还将介绍CloudKit
的其他一些对象和功能。
环境
CloudKit
为你的应用程序网络数据分别提供了开发环境(Develpment)和生产环境(Production)。
•开发环境当你的项目仍处于开发阶段时,所有通过CloudKit
产生的数据都只被保存开发环境中,只有开发团队的成员才能访问该环境中的数据。在开发环境中,你可以随时进行Schema
结构调整、对Record Type
的属性进行删除修改等操作。即使这些操作可能会引起不同版本之间数据冲突都没有问题(可以随时重置开发环境)。非常类似Core Data
的应用程序上线前的状态,即使数据无法正常迁移,只需要删除重装app即可。通过开发环境,开发者可以在向用户提供CloudKit
服务之前对应用程序进行充分的测试。•生产环境当应用程序完成开发并准备提交应用商店时,需要将开发环境的结构部署到生产环境(Deploy Schema Changes
)。Schema
一旦部署到生产环境,则意味着开发者不可以像在开发环境中那样随意对Schema
进行修改,所有的修改都必须以向前兼容的方式进行。原因非常简单,一旦应用程序上线,我们无法控制客户端的更新频率,也就是客户端可能存在任何的结构版本,为了能够让低版本的客户端一样可以访问数据,任何对数据模型的更改都需要向下兼容。在App Store
上销售的应用程序只能访问生产环境。
即使开发者的开发者账户同个人iCloud
账户一致,开发环境和生产环境也是两个不同的沙盒,数据是互不影响的。当使用Xcode
调试程序时,应用只能访问开发环境,而通过Testflight
或App Store
下载的应用则只能访问生产环境。
在开发环境下,点击Deploy Schema Changes
将开发环境的Schema
部署到生产环境。
部署时,会显示自上次部署后开发环境做出的修改。
即使Schema
已经部署到生产环境后,我们仍可继续改动开发环境并部署到生产环境,如果模型无法满足兼容条件,CloudKit
仪表台将会禁止你的部署行为。
在容器名称下方会显示Schema
是否已经部署到生产环境。上图是尚未部署的状态,下图是已经部署的状态。
在做任何操作之前,要首先确认是否处于正确的环境设定中。
鉴于
CloudKit
的环境部署规则,在采用Core Data with CloudKit
的项目中设计Core Data
数据模型时一定要特别小心!。我个人的原则是可加、可减、尽量不改。我将在下篇文章详细讨论该如何对Core Data with CloudKit
数据模型做版本迁移。
安全角色(Security Roles)
安全角色仅适用于公共数据库。
CloudKit
使用基于角色的访问控制(RBAC
)来管理权限和控制对公共数据库中数据的访问(私有数据库对于应用程序的用户是的)。通过CloudKit
,你可以为一个角色设置权限级别,然后将该角色分配给一个给定的记录类型(Record Type
)。
权限包括读、写、创建。读权限只允许读取记录,写权限允许读取和写入记录,而创建权限允许读取和写入记录以及创建新记录。
CloudKit
包含3个预设角色,分别为World(_world
)、Authenticated(_icloud
)和 Creator(_creator
)。World表示任何人,无论其是否为iCloud用户。Authenticated适用于任何经过验证的iCloud用户。Creator则是作为记录(Record
)的创建者。
默认的设置为,任何人都可以读取数据,只有经过验证的iCloud用户才可以创建新记录,记录的创建者可以更新自己的记录。
我们可以创建自定义安全角色,但是不能创建用户记录(User Record
),当用户次对容器进行身份验证时时系统会为该用户创建用户记录。我们可以查找现有用户并将其分配给任意的自定义的角色。
安全角色是数据模型(Schema
)的一部分,每当开发者修改了安全设置后,需要将其部署到生产环境才能在生产环境生效。部署后无法删除安全角色。
大多数
Core Data with CloudKit
应用场合,直接使用系统的默认配置即可。
索引(Indexes)
CloudKit
的索引分为三种类型:
•可查询(queryable
)•可搜索(searchable
)•可排序(sortable
)
当我们通过CloudKit
创建Recored Type
后,可以根据需要为每个字段创建所需的索引(只有NSString
支持可搜索)。索引类型选项是独立的,如果你希望该字段既可查询又可排序,则需要分别创建两个索引。
只有为Record Type
的recordName
创建了queryable
索引后,才可以在Records
中浏览该Type的数据。
Core Data with CloudKit
会自动为Core Data
数据模型的每个属性在CloudKit
上创建需要的索引(不包含recordName
)。除非你需要在CloudKit
仪表台上浏览数据,否则我们不需要对索引做任何添加。
Record *
Record Type
是开发人员为CKRecord
指定的类型标识符。你可以直接在代码中创建它,也可以在CloudKit
仪表盘上对其进行创建、修改。
在基础篇[4]中曾提到Entity
相较Record Type
拥有更多的配置信息,但Record Type
也有一个Enitity
没有的特性——元数据。
CloudKit
为每一个Record Type
预设了若干元数据字段(即使开发者没有创建任何其他字段),每条数据记录(CKRecord
)都会包含这些信息,其中绝大多数都是系统自动设定的。
•createdTimestamp CloudKit
将记录保存到服务器的时间•createUserRecordName_creator
的用户记录,该记录保存在Users
(系统创建)中,每当用户次对容器进行身份验证时时系统会为该用户创建用户记录•_etag版本令牌。每次CloudKit
保存记录时,都会将该记录更新为新值。用于比较网络和本地数据的版本•modifiedTimestampCloudKi
t更新记录的近时间•modifiedUserRecordName后更新数据的用户记录•recordName记录的 ID。在创建CKRcord
时创建,通常会设置为UUID
字符串
对于一些特殊类型的Record Type
,系统还会增加一些针对性的元数据,比如role
,cloud.shared
等
本文的主题为Core Data with CloudKit
,因此让我们来看一下NSPersistentCloudKitContainer
是如何将Core Data
托管对象的属性转换成CloudKit
的Recore Type
字段的。
上图是我们在同步本地数据库到iCloud私有数据库[5]中模版项目
Item
在CloudKit
对应的Record Type
。CloudKit
会自动为托管对象实体的每个属性创字段,将属性名称映射到了具有CD_[attribute.name]
键名的字段。该字段的类型在Core Data
和CloudKit
之间可能也会有所不同。Record Type
名称为CD_[entity]
。一切的操作都是由系统自动完成的,我们无需干预。另外,还会为Enitity
生成一个CD_entityName
的字段,内容为Entity
的类映射名。这些以
CD_
为前缀的字符串,在数据同步过程中将不断出现在控制台上,了解了它的构成对调试代码有一定帮助。
Record Type
部署到生产环境后,字段不可以删除,字段名称也不可以修改。因此一些Core Data
中的操作在Core Data with CloudKit
中是不允许的。不要对已经上线的应用程序数据模型的
Entity
进行更名,也不要对Attribute
更名,即使使用Mapping Model、Renaming ID都是不行的。在开发阶段如果需要更名的话,可能需要删除app重装并重置CloudKit
的开发环境。即使已经在
Core Data
中删除了Entity
的某个Attribute
,该字段仍然会存在于Record Type
中(并不会影响同步)。
Zones
每个种类的数据库都有默认Zone
,只有私有数据库可以自定义Zone
。
对于私有数据库中的数据,在创建CKRecord
时可以为数据指定Zone
。
let zone = CKRecordZone(zoneName: "myZone")
let newStudent = CKRecord(recordType: "Student",
recordID: CKRecord.ID(recordName: UUID().uuidString,
zoneID: zone.zoneID))
NSPersistentCloudKitContainer
在将托管对象转换成CKRecord
时,将ZoneID
统一设置为com.apple.coredata.cloudkit.zone
。必须切换到正确的Zone
才能浏览到数据。
•OWNER RECORD NAME用户记录,对应Zone
的_creator
•CHANGE TOKEN比对令牌•ATOMIC当CloudKit无法更新Zone
中的一个或多个记录时,如果值为true
则整个操作失败
Records
用于数据记录的浏览、创建、删除、更改、查询。
在浏览数据时,需注意以下几点:
•选择正确的环境(开发环境和生产环境的数据完全不同)•选择正确的Database
、Zone
•确认需要浏览的Record Type
元数据recordName
已经添加了queryable
索引•如果需要对字段进行排序或过滤,请给该字段创建对应的索引•索引只有在部署后才会在生产环境下起作用
在
CloudKit
仪表台中修改Core Data
的镜像数据,客户端会立即收到远程通知并进行更新。不过并不推荐此种做法。
你也可以在代码中获取到Core Data
托管对象对应的CKRecord
:
func getLastUserID(_ object:Item?) -> CKRecord.ID? {
guard let item = object else {return nil}
guard let ckRecord = PersistenceController.shared.container.record(for: item.objectID) else {return nil}
guard let userID = ckRecord.lastModifiedUserRecordID else {
print("can't get userID")
return nil
}
return userID
}
上面的代码,将获取托管对象记录对应的CKRecord
的后修改用户
Subscriptions
浏览在容器上注册的CKSubscription
。
CKSubscription是通过代码创建的,在仪表盘上只可以查看或删除。
比如下面的代码将创建一个CKQuerySubscription
let predicate = NSPredicate(format: "name = 'bob'")
let subscription = CKQuerySubscription(recordType: "Student",
predicate: predicate,
options: [.firesOnRecordCreation])
let info = CKSubscription.NotificationInfo()
info.alertLocalizationKey = "create a new bob"
info.soundName = "NewAlert.aiff"
info.shouldBadge = true
info.alertBody = "hello world"
subscription.notificationInfo = info
publicDB.save(subscription) { subscription, error in
if let error = error {
print("error:\(error)")
}
guard let subscription = subscription else { return }
print("save subscription successes:\(subscription)")
}
NSPersistentCloudKitContainer
会为Core Data
镜像的私有数据库注册一个CKDatabaseSubscription
。当com.apple.coredata.cloudkit.zone
数据更新时,会推送远程通知。
Tokens&Keys
设置容器的API令牌。
除了可以通过代码和CloudKit
仪表台对数据进行操作外,苹果还提供了从网络或其他平台访问iCloud
数据的手段。在获取令牌后,开发者还可以通过使用 CloudKit JS [6]或 CloudKit Web 服务[7]与数据进行交互。
已有开发者利用以上服务,开发出可在其他平台访问iCloud数据的第三方库,比如DroidNubeKit[8](在安卓上访问CloudKit
)。
对于
Core Data
的网络镜像数据,除非你的数据模型足够简单,否则不推荐做这种尝试。CloudKit Web
服务更适合直接通过Cloudkit
创建的数据记录。
Sharing Fallbackd
为低版本操作系统(低于iOS 10、macOS Sierra)提供数据记录共享回调支持。
遥测(Telemetry)
通过查看Telemetry的指标,方便你在开发或更新应用程序时可视化性能。包括请求数量、错误数量、推送数量、服务器延迟以及平均请求大小等等。通过设定范围,仅显示与你相关的数据,帮助你更好地了解应用程序的流量配置及使用趋势。
日志(Logs)
在历史日志中,你可以查看包括时间、客户端平台版本、用户(匿名)、事件、组织、细节等信息。
在提供详尽信息的基础上,CloudKit
尽可能地保持用户数据的隐秘性。日志显示每个用户记录的服务器事件,但不暴露任何个人身份信息。仅显示匿名的、特定于容器的CloudKit
用户。
AppStoreConnect
的分析信息仅来自已同意与 App 开发者共享诊断和使用信息的用户,CloudKit
日志信息则来自于你的应用程序中所有使用了CloudKit
服务的用户。两者结合使用,可以获得更好的效果。
总结
大多数使用Core Data with CloudKit
的场景,开发者基本无需使用CloudKit
仪表盘。不过偶尔研究一下仪表盘上的数据,也是一种不错的乐趣。
比如:从2021年7月末开始,健康笔记[9]的CloudKit
日志中频繁出现了iphone13
设备的身影。
下一篇文章,我们将聊一下开发Core Data with CloudKit
项目经常会碰到的一些情况,比如调试、测试、数据迁移等。
相关文章