在WWDC 2019上,蘋果為Core Data
帶了一項重大的更新——引入了NSPersistentCloudKitContainer
奥邮。這意味著無需編寫大量代碼,使用Core Data with CloudKit
可以讓用戶在他所有的蘋果設備上無縫訪問應用程序中的數(shù)據罗珍。
Core Data
為開發(fā)具有結構化數(shù)據的應用程序提供了強大的對象圖管理功能洽腺。CloudKit允許用戶在登錄其iCloud賬戶的每臺設備上訪問他們的數(shù)據,同時提供一個始終可用的備份服務覆旱。Core Data with CloudKit
則結合了本地持久化+云備份和網絡分發(fā)的優(yōu)點蘸朋。
2020年、2021年扣唱,蘋果持續(xù)對Core Data with CloudKit
進行了強化藕坯,在最初僅支持私有數(shù)據庫同步的基礎上,添加了公有數(shù)據庫同步以及共享數(shù)據庫同步的功能噪沙。
我將通過幾篇博文介紹Core Data with CloudKit
的用法炼彪、調試技巧、控制臺設置并嘗試更深入地研究其同步機制正歼。
Core Data with CloudKit的局限性
-
只能運行在蘋果的生態(tài)
不同于其他的跨平臺解決方案辐马,
Core Data with CloudKit
只能運行于蘋果生態(tài)中,并且只能為蘋果生態(tài)的用戶提供服務局义。 -
測試門檻較高
需要有一個Apple Developer Program賬號才能在開發(fā)過程中訪問
CloudKit
服務和開發(fā)團隊的CKContainer
喜爷。另外,在模擬器上的運行效果也遠沒有在真機上可靠萄唇。
Core Data with CloudKit的優(yōu)點
-
幾乎免費
開發(fā)者基本上不需要為網絡服務再額外支付費用檩帐。私有數(shù)據庫保存在用戶個人的iCloud空間中,公共數(shù)據庫的容量會隨著應用程序使用者的增加而自動提高另萤,最高可增加到1 PB 存儲湃密、10 TB 數(shù)據庫存儲,以及每天 200 TB 流量四敞。之所以說幾乎免費勾缭,畢竟蘋果會扣取15-30%的app收益。
-
安全
一方面蘋果通過沙盒容器目养、數(shù)據庫區(qū)隔俩由、加密字段、鑒權等多種技術手段保證了用戶的數(shù)據安全癌蚁。另一方面幻梯,鑒于蘋果長期以來在用戶中樹立的隱私捍衛(wèi)者的形象兜畸,使用
Core Dat with CloudKit
可以讓用戶對你的應用程序增加更多的信任。事實上碘梢,正是在WWDC2019年看到這個功能后咬摇,我才有了開發(fā)【健康筆記】的原動力——既保證數(shù)據隱私又能長久的保存數(shù)據。
-
集成度高煞躬、用戶感知好
鑒權肛鹏、分發(fā)等都是無感的。用戶不需要進行任何額外的登錄便可享受全部的功能恩沛。
Core Data
Core Data
誕生于2005年在扰,它的前身EOF
在1994年便已經獲得的不少用戶的認可。經過了多年的演進雷客,Core Data
已經發(fā)展的相當成熟芒珠。作為對象圖和持久化框架,幾乎每個教程都會告訴你搅裙,不要把它當作數(shù)據庫皱卓,也不要把它當作ORM
。
Core Data
的功能包括但不限于:管理序列化版本部逮、管理對象生命周期娜汁、對象圖管理、SQL隔離兄朋、處理變更存炮、持久化數(shù)據、數(shù)據內存優(yōu)化以及數(shù)據查詢等蜈漓。
Core Data
提供的功能繁多,但對于初學者并不十分友好宫盔,擁有陡峭的學習曲線融虽。最近幾年蘋果也注意到了這個問題,通過添加PersistentContainer
極大的降低了Stack
創(chuàng)建的難度灼芭;SwiftUI
及Core Data模版
的出現(xiàn)讓初學者也可以較輕松地在項目中使用其強大的功能了有额。
CloudKit
在蘋果推出iCloud
之后的幾年中,開發(fā)者都無法將自己的應用程序同iCloud
結合起來彼绷。這個問題直到2014年蘋果推出了CloudKit
框架后才得到解決巍佑。
CloudKit
是數(shù)據庫、文件存儲寄悯、用戶認證系統(tǒng)的集合服務萤衰,提供了在應用程序和iCloud容器
之間的移動數(shù)據接口。用戶可以在多個設備上訪問保存在iCloud
上的數(shù)據猜旬。
CloudKit
的數(shù)據類型脆栋、內在邏輯和Core Data
有很大的不同倦卖,需要做一些妥協(xié)或處理才能將兩者的數(shù)據對象進行轉換。事實上椿争,當CloudKit
一經推出怕膛,開發(fā)者就強烈希望兩者之間能夠進行便捷的轉換。在推出Core Data with CloudKit
之前秦踪,已經有第三方的開發(fā)者提供了將Core Data
或其他數(shù)據的對象(比如realm
)同步到CloudKit
的解決方案褐捻,這些方案中的大多數(shù)目前仍在提供支持。
依賴于之前推出的持久化歷史追蹤功能椅邓,蘋果終于在2019年提供了自己的解決方案Core Data with CloudKit
柠逞。
Core Data對象 vs CloudKit對象
兩個框架都有各自的基礎對象類型,相互之間并不能被一一對應希坚。在此僅對本文涉及的一些基礎對象類型做簡單的介紹和比較:
-
NSPersistentContainer vs CKContainer
NSPersistentContainer
通過處理托管對象模型(NSManagedObjectModel
)边苹,對持久性協(xié)調器(NSPersistentStoreCoordinator
)和托管對象上下文(NSManagedObjectContext
)進行統(tǒng)一的創(chuàng)建和管理。開發(fā)者通過代碼創(chuàng)建其的實例裁僧。CKContainer
則和應用程序的沙盒邏輯類似个束,在其中可以保存結構化數(shù)據、文件等多種資源聊疲。每個使用CloudKit
的應用程序應有一個屬于自己的CKContainer
(通過配置茬底,一個應用程序可以對應多個CKContainer
,一個CKContainer
也可以服務于多個應用程序)获洲。開發(fā)者通常不會在代碼中直接創(chuàng)建新的CKConttainer
阱表,一般通過iCoud控制臺
或在Xcode Target
的Signing&Capabilities
中創(chuàng)建。 -
NSPersistentStore vs CKDatabase/CkRecordZone
NSPersistentStore
是所有Core Data
持久存儲的抽象基類贡珊,支持四種持久化的類型(SQLite
最爬、Binary
、XML
和In-Memory
)门岔。在一個NSPersistentContainer
中爱致,通過聲明多個的NSPersistentStoreDescription
,可以持有多個NSPersistentStore實例
(可以是不同的類型)寒随。NSPersistentStore
沒有用戶鑒權的概念糠悯,但可以設置只讀或讀寫兩種模式。由于Core Data with CloudKit
需要持久化歷史追蹤的支持妻往,因此只能同步將SQLite
作為存儲類型的NSPersistentStore
互艾,在設備上,該NSPersistentStore的實例
將指向一個SQLite數(shù)據庫文件
讯泣。在
CloudKit
上纫普,結構化的數(shù)據存儲只有一種類型,但采用了兩個維度對數(shù)據進行了區(qū)分好渠。從用戶鑒權角度局嘁,
CKDatabase
分別提供了三種形式的數(shù)據庫:私有數(shù)據庫溉箕、公有數(shù)據庫、共享數(shù)據庫悦昵。應用程序的使用者(已經登錄了iCloud
賬號)只能訪問自己的私有數(shù)據庫肴茄,該數(shù)據庫的數(shù)據保存在用戶個人的iCloud
空間中,其他人都不可以對其數(shù)據進行操作但指。在公共數(shù)據庫中保存的數(shù)據可以被任何授權過的應用程序調用寡痰,即使app的使用者沒有登錄iCloud
賬戶,應用程序仍然可以讀取其中的內容棋凳。應用程序的使用者拦坠,可以將部分數(shù)據共享給其他的同一個app
的使用者,共享的數(shù)據將被放置在共享數(shù)據庫中剩岳,共享者可以設置其他用戶對于數(shù)據的讀寫權限贞滨。數(shù)據在
CKDatabase
中也不是以零散的方式放置在一起的,它們被放置在指定的RecoreZone
中拍棕。我們可以在私有數(shù)據庫中創(chuàng)建任意多的Zone
(公共數(shù)據庫和共享數(shù)據庫只支持默認Zone
)晓铆。當CKContainer
被創(chuàng)建后,每種數(shù)據庫中都會默認生成一個名為_defaultZone
的CKRecoreZone
绰播。因此骄噪,當我們保存數(shù)據到CloudKit數(shù)據庫時,不僅需要指明數(shù)據庫(私有蠢箩、公有链蕊、共享)類型,同時也需要標明具體的
zoneID
(當保存到_defaultZone
時無需標記)谬泌。 -
NSManagedObjectModel vs Schema
NSManagedObjectModel
是托管對象模型滔韵,標示著Core Data
對應的數(shù)據實體(Enities)。絕大多數(shù)情況下掌实,開發(fā)者都是使用Xcode
的Data Model Editor
來對其進行的定義陪蜻,定義會被保存在xcdatamodeled
文件中,其中包含了實體屬性潮峦、關系、索引勇婴、約束忱嘹、校驗、配置等等信息耕渴。當在應用程序中啟用
CloudKit
后拘悦,將在CKContainer
創(chuàng)建一個Schema
。Schema
中包括記錄類型(Record Type
)橱脸、記錄類型類型之間可能存在的關系础米、索引以及用戶權限分苇。除了直接在
iCloud
控制臺創(chuàng)建Schema
的內容外,也可以通過在代碼中創(chuàng)建CKRecord
屁桑,讓CloudKit
自動為我們創(chuàng)建或更新Schema
中對應的內容医寿。Schema
中有權限的設定(Security Roles
),可以分別為world
蘑斧、icloud
以及creator
設定不同的讀寫權限靖秩。 -
Entities vs Record Types
盡管我們通常會強調
Core Data
不是數(shù)據庫,但實體(Enitities
)與數(shù)據庫中的表非常相似竖瘾。我們在實體中描述對象沟突,包括其名稱、屬性和關系捕传。最終將其描述成NSEntityDescription
并匯總到NSManagedObjectModel
中惠拭。在
CloudKit
中用Record Types
描述數(shù)據對象的名稱、屬性庸论。Enitiy
中有大量的信息可以配置职辅,但Record Types
只能對應描述其中的一部分。由于兩方無法一一對應葡公,因此在設計Core Data with CloudKit
的數(shù)據對象時要遵守相關規(guī)定(具體規(guī)定將在下一篇文章中探討)罐农。 -
Managed Object vs CKRecord
托管對象(
Managed Object
)是表示持久存儲記錄的模型對象。托管對象是NSManagedObject
或其子類的實例催什。托管對象在托管對象上下文(NSManagedObjectContext
)中注冊涵亏。在任何給定的上下文中,托管對象最多有一個實例對應于持久存儲中的給定記錄蒲凶。在
CloudKit
上气筋,每條記錄被稱作為CKRecord
。我們不需要關心
Managed Object
的ID
(NSMangedObjectID
)的創(chuàng)建過程旋圆,Core Data
將為我們處理一切宠默,但對于CKRecord
,多數(shù)情況下灵巧,我們需要在代碼中明確為每條記錄設定CKRecordIdentifier
搀矫。作為CKRecord
的唯一標識,CKRecordIdentifier
被用于確定該CKRecord
在數(shù)據庫的唯一位置刻肄。如果數(shù)據保存在自定義的CKRecordZone
瓤球,我們也需要在CKRecord.ID
中指明。 -
CKSubscription
CloudKit
是云端服務敏弃,它要同一iCloud
賬戶的不同設備(私有數(shù)據庫)或者使用不同iCloud
賬號的設備(公共數(shù)據庫)的數(shù)據變化做出相應的反饋卦羡。開發(fā)者通過
CloudKit
在iCloud
上創(chuàng)建CKSubscription
,當CKContainer
中的數(shù)據發(fā)生變化時,云端服務器會檢查該變化是否滿足某個CKSubscription
的觸發(fā)條件,在條件滿足時绿饵,對訂閱的設備發(fā)送遠程提醒(Remote Notification
)欠肾。這就是當我們在Xcode Target
的Signing&Capabilities
中添加上CloudKit
功能時,會Xcode
自動添加Remote Notification
的原因拟赊。在實際使用中刺桃,需要通過
CKSubscription
的三個子類完成不同的訂閱任務:CKQuerySubscription
,當某個CKRecord
滿足設定的NSPercidate
時推送Notification
要门。CKDatabaseSubscription
虏肾,訂閱并跟蹤數(shù)據庫(CKDatabase
)中記錄的創(chuàng)建、修改和刪除欢搜。該訂閱只能用于私有數(shù)據庫和共享數(shù)據庫中自定義的CKRecordZone
封豪,并只會通知訂閱的創(chuàng)建者
。在以后的文章中炒瘟,我們可以看到Core Data with CloudKit
是如何在私有庫中使用該訂閱的吹埠。CKRecordZoneNotification
,當用戶疮装、或者在某些情況下缘琅,CloudKit
修改該區(qū)域(CKRecordZone
)的記錄時,記錄區(qū)的訂閱就會執(zhí)行廓推,例如刷袍,當記錄中某個字段的值發(fā)生變化時。對于
iCloud
服務器推送的遠程通知樊展,應用程序需要在Application Delegate
中做出響應呻纹。多數(shù)情況下,遠程提醒
可以采用靜默通知
的形式专缠,為此開發(fā)者需要在的應用程序中啟用Backgroud Modes
的Remote notifications
雷酪。
Core Data with CloudKit 的實現(xiàn)猜想
結合上面介紹的基礎知識,讓我們嘗試推測一下Core Data with CloudKit
的實現(xiàn)過程涝婉。
以私有數(shù)據庫同步為例:
-
初始化:
- 創(chuàng)建
CKContainer
- 根據
NSManagedObjectModel
配置Schema
- 在私有數(shù)據庫中創(chuàng)建ID為
com.apple.coredata.cloudkit.zone
的CKRecordZone
- 在私有數(shù)據庫上創(chuàng)建
CKDatabaseSubscription
- 創(chuàng)建
-
數(shù)據導出(將本地
Core Data
數(shù)據導出到云端)-
NSPersistentCloudKitContainer
創(chuàng)建后臺任務響應持久化歷史跟蹤
的NSPersistentStoreRemoteChange
通知 - 根據
NSPersistentStoreRemoteChange
的transaction
哥力,將Core Data
的操作轉換成CloudKit
的操作。比如對于新增數(shù)據墩弯,將NSManagedObject
實例轉換成CKRecord
實例吩跋。 - 通過
CloudKit
將轉換后的CKRecord
或其他CloudKit操作
傳遞給iCloud
服務器
-
-
服務器端
- 按順序處理從遠端設備提交的
CloudKit操作數(shù)據
- 根據初始化創(chuàng)建的
CKDatabaseSubscription
檢查該操作是否導致私有數(shù)據庫的com.apple.coredata.cloudkit.zone
中的數(shù)據發(fā)生變化 - 對所有創(chuàng)建
CKDatabaseSubscription
訂閱的設備(同一iCloud
賬戶)分發(fā)遠程通知
- 按順序處理從遠端設備提交的
-
數(shù)據導入(將遠程數(shù)據同步到本地)
-
NSPersistentCloudKitContainer
創(chuàng)建的后臺任務響應云端的靜默推送
- 向云端發(fā)送刷新操作要求并附上上次操作的
令牌
- 云端根據每個設備的
令牌
,為其返回自上次刷新后數(shù)據庫發(fā)生的變化 - 將遠端數(shù)據轉換成本地數(shù)據(刪除渔工、更新锌钮、添加等)
- 由于
視圖上下文
的automaticallyMergesChangesFromParen
屬性設置為真,本地數(shù)據的變化將自動在視圖上下文
中體現(xiàn)出來
-
上述步驟中省略了所有技術難點及細節(jié)涨缚,僅描述了大概的流程轧粟。
總結
本文中,我們簡單介紹了關于Core Data
脓魏、CloudKit
以及Core Data with CloudKit
的一點基礎知識兰吟。在下一篇文章中我們將探討如何使用Core Data with CloudKit
實現(xiàn)本地數(shù)據庫和私有數(shù)據庫的同步。
PS:介紹如何使用NSPersistentContainer的文章并不少茂翔,但同其他Core Data的功能一樣混蔼,用好并不容易。在兩年多的使用中珊燎,我便碰到不少問題惭嚣。借著今年打算在【健康筆記3】中實現(xiàn)共享數(shù)據庫
功能的機會,我最近較系統(tǒng)地重新學習了Core Data with CloudKit
并對其知識點進行了梳理悔政。希望通過這個系列博文能讓更多的開發(fā)者了解并使用Core Data with Cloudkit
功能晚吞。
本文原載于我的個人博客肘子的Swift記事本。