Core Data with CloudKit (一) —— 基礎

在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)建的難度灼芭;SwiftUICore 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 TargetSigning&Capabilities中創(chuàng)建。

  • NSPersistentStore vs CKDatabase/CkRecordZone

    NSPersistentStore是所有 Core Data 持久存儲的抽象基類贡珊,支持四種持久化的類型(SQLite最爬、BinaryXMLIn-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ù)據庫中都會默認生成一個名為_defaultZoneCKRecoreZone绰播。

    因此骄噪,當我們保存數(shù)據到CloudKit數(shù)據庫時,不僅需要指明數(shù)據庫(私有蠢箩、公有链蕊、共享)類型,同時也需要標明具體的zoneID(當保存到_defaultZone時無需標記)谬泌。

  • NSManagedObjectModel vs Schema

    NSManagedObjectModel是托管對象模型滔韵,標示著Core Data對應的數(shù)據實體(Enities)。絕大多數(shù)情況下掌实,開發(fā)者都是使用XcodeData Model Editor來對其進行的定義陪蜻,定義會被保存在xcdatamodeled文件中,其中包含了實體屬性潮峦、關系、索引勇婴、約束忱嘹、校驗、配置等等信息耕渴。

    當在應用程序中啟用CloudKit后拘悦,將在CKContainer創(chuàng)建一個SchemaSchema中包括記錄類型(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 ObjectIDNSMangedObjectID)的創(chuàng)建過程旋圆,Core Data將為我們處理一切宠默,但對于CKRecord,多數(shù)情況下灵巧,我們需要在代碼中明確為每條記錄設定CKRecordIdentifier搀矫。作為CKRecord的唯一標識,CKRecordIdentifier被用于確定該CKRecord在數(shù)據庫的唯一位置刻肄。如果數(shù)據保存在自定義的CKRecordZone瓤球,我們也需要在CKRecord.ID中指明。

  • CKSubscription

    CloudKit是云端服務敏弃,它要同一iCloud賬戶的不同設備(私有數(shù)據庫)或者使用不同iCloud賬號的設備(公共數(shù)據庫)的數(shù)據變化做出相應的反饋卦羡。

    開發(fā)者通過CloudKitiCloud上創(chuàng)建CKSubscription,當CKContainer中的數(shù)據發(fā)生變化時,云端服務器會檢查該變化是否滿足某個CKSubscription的觸發(fā)條件,在條件滿足時绿饵,對訂閱的設備發(fā)送遠程提醒(Remote Notification)欠肾。這就是當我們在Xcode TargetSigning&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 ModesRemote notifications雷酪。

Core Data with CloudKit 的實現(xiàn)猜想

結合上面介紹的基礎知識,讓我們嘗試推測一下Core Data with CloudKit的實現(xiàn)過程涝婉。

以私有數(shù)據庫同步為例:

  • 初始化:

    1. 創(chuàng)建CKContainer
    2. 根據NSManagedObjectModel配置Schema
    3. 在私有數(shù)據庫中創(chuàng)建ID為com.apple.coredata.cloudkit.zoneCKRecordZone
    4. 在私有數(shù)據庫上創(chuàng)建CKDatabaseSubscription
  • 數(shù)據導出(將本地Core Data數(shù)據導出到云端)

    1. NSPersistentCloudKitContainer創(chuàng)建后臺任務響應持久化歷史跟蹤NSPersistentStoreRemoteChange通知
    2. 根據NSPersistentStoreRemoteChangetransaction哥力,將Core Data的操作轉換成CloudKit的操作。比如對于新增數(shù)據墩弯,將NSManagedObject實例轉換成CKRecord實例吩跋。
    3. 通過CloudKit將轉換后的CKRecord或其他CloudKit操作傳遞給iCloud服務器
  • 服務器端

    1. 按順序處理從遠端設備提交的CloudKit操作數(shù)據
    2. 根據初始化創(chuàng)建的CKDatabaseSubscription檢查該操作是否導致私有數(shù)據庫的com.apple.coredata.cloudkit.zone中的數(shù)據發(fā)生變化
    3. 對所有創(chuàng)建CKDatabaseSubscription訂閱的設備(同一iCloud賬戶)分發(fā)遠程通知
  • 數(shù)據導入(將遠程數(shù)據同步到本地)

    1. NSPersistentCloudKitContainer創(chuàng)建的后臺任務響應云端的靜默推送
    2. 向云端發(fā)送刷新操作要求并附上上次操作的令牌
    3. 云端根據每個設備的令牌,為其返回自上次刷新后數(shù)據庫發(fā)生的變化
    4. 將遠端數(shù)據轉換成本地數(shù)據(刪除渔工、更新锌钮、添加等)
    5. 由于視圖上下文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記事本

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末谋国,一起剝皮案震驚了整個濱河市槽地,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芦瘾,老刑警劉巖捌蚊,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異近弟,居然都是意外死亡缅糟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門祷愉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來窗宦,“玉大人,你說我怎么就攤上這事谣辞∑人ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵泥从,是天一觀的道長句占。 經常有香客問我,道長躯嫉,這世上最難降的妖魔是什么纱烘? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮祈餐,結果婚禮上擂啥,老公的妹妹穿的比我還像新娘。我一直安慰自己帆阳,他們只是感情好哺壶,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般山宾。 火紅的嫁衣襯著肌膚如雪至扰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天资锰,我揣著相機與錄音敢课,去河邊找鬼。 笑死绷杜,一個胖子當著我的面吹牛直秆,可吹牛的內容都是我干的。 我是一名探鬼主播鞭盟,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼圾结,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了齿诉?” 一聲冷哼從身側響起疫稿,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鹃两,沒想到半個月后遗座,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡俊扳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年途蒋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片馋记。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡号坡,死狀恐怖,靈堂內的尸體忽然破棺而出梯醒,到底是詐尸還是另有隱情宽堆,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布茸习,位于F島的核電站畜隶,受9級特大地震影響,放射性物質發(fā)生泄漏号胚。R本人自食惡果不足惜籽慢,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望猫胁。 院中可真熱鬧箱亿,春花似錦、人聲如沸弃秆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至脑豹,卻和暖如春氢卡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晨缴。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留峡捡,地道東北人击碗。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像们拙,于是被迫代替她去往敵國和親稍途。 傳聞我的和親對象是個殘疾皇子独柑,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內容