Core Data with CloudKit(三)——CloudKit儀表臺
本篇文章中,我們將一起研究CloudKit
儀表臺。
初識儀表臺
使用CloudKit Dashboard
需要開發者擁有Apple Developer Program賬號,訪問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 (一) —— 基礎中已經對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 Types
Record Type
是開發人員為CKRecord
指定的型別識別符號。你可以直接在程式碼中建立它,也可以在CloudKit
儀表盤上對其進行建立、修改。
在基礎篇中曾提到Entity
相較Record Type
擁有更多的配置資訊,但Record Type
也有一個Enitity
沒有的特性——元資料。
CloudKit
為每一個Record Type
預設了若干元資料欄位(即使開發者沒有建立任何其他欄位),每條資料記錄(CKRecord
)都會包含這些資訊,其中絕大多數都是系統自動設定的。
- createdTimestamp
CloudKit
首次將記錄儲存到伺服器的時間
- createUserRecordName
_creator
的使用者記錄,該記錄儲存在Users
(系統建立)中,每當使用者第一次對容器進行身份驗證時時系統會為該使用者建立使用者記錄
- _etag
版本令牌。每次CloudKit
儲存記錄時,都會將該記錄更新為新值。用於比較網路和本地資料的版本
- modifiedTimestamp
CloudKi
t更新記錄的最近時間
- modifiedUserRecordName
最後更新資料的使用者記錄
- recordName
記錄的唯一 ID。在建立CKRcord
時建立,通常會設定為UUID
字串
對於一些特殊型別的Record Type
,系統還會增加一些針對性的元資料,比如role
,cloud.shared
等
本文的主題為Core Data with CloudKit
,因此讓我們來看一下NSPersistentCloudKitContainer
是如何將Core Data
託管物件的屬性轉換成CloudKit
的Recore Type
欄位的。
上圖是我們在同步本地資料庫到iCloud私有資料庫中模版專案
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
的開發環境。
Zones
每個種類的資料庫都有預設Zone
,只有私有資料庫可以自定義Zone
。
對於私有資料庫中的資料,在建立CKRecord
時可以為資料指定Zone
。
```swift 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
:
swift
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
```swift 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 或 CloudKit Web 服務與資料進行互動。
已有開發者利用以上服務,開發出可在其他平臺訪問iCloud資料的第三方庫,比如DroidNubeKit(在安卓上訪問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
儀表盤。不過偶爾研究一下儀表盤上的資料,也是一種不錯的樂趣。
下一篇文章,我們將聊一下開發Core Data with CloudKit
專案經常會碰到的一些情況,比如除錯、測試、資料遷移等。
本文原載於我的個人部落格肘子的Swift記事本。
文章也一併釋出在微信公共號:肘子的Swift記事本
其他推薦:
如何在Xcode下預覽含有Core Data元素的SwiftUI檢視
https://www.fatbobman.com/posts/uikitInSwiftUI/
- 自定義 Button 的外觀和互動行為
- MacBook Pro 使用體驗
- 用 SwiftUI 的方式進行佈局
- 聊一聊可組裝框架( TCA )
- StateObject 與 ObservedObject
- 一些適合 SwiftUI 初學者的教程
- iBug 16 有感
- 在 SwiftUI 中實現檢視居中的若干種方法
- SwiftUI 佈局 —— 尺寸( 下 )
- SwiftUI 佈局 —— 尺寸( 上 )
- SwiftUI 佈局 —— 對齊
- Core Data with CloudKit(三)——CloudKit儀表臺
- Core Data with CloudKit(二)——同步本地資料庫到iCloud私有資料庫
- 在CoreData中使用持久化歷史跟蹤
- 用 Table 在 SwiftUI 下建立表格
- SwiftUI 4.0 的全新導航系統
- 如何在 Core Data 中進行批量操作
- Core Data 是如何在 SQLite 中儲存資料的
- 在 SwiftUI 檢視中開啟 URL 的若干方法
- 為自定義屬性包裝型別新增類 @Published 的能力