CloudKit + Core Data
概要
ドキュメント
WWDC のセッションでも、ドキュメントを豊富に用意しましたと謳っている。
Entity にユニーク制約が付与できない
Attribute の型に Undefined と objectID が利用できない
Relationship は全て optional である必要がある
Relationship の操作が atomic にならない場合がある
Relationship は全て inverse を持つ必要がある
Relationship の deletion rule のうち、Deny はサポートされていない
ある Configuration 内の Entity は、他の Configuration の Entity と relationship を持ってはいけない
開発中のリセット
開発中はまだ CloudKit のスキーマが確定していない可能性があるし、インテグレーションテストの際には毎回スキーマの初期化がしたいかもしれない。そのような場合には スキーマの初期化 が利用できる。ただし、プロダクションコードにスキーマの初期化は含めてはならない。 /*
This method creates a set of representative CKRecord instances for all stores in the container
that use Core Data with CloudKit and uploads them to CloudKit. These records are "fully saturated"
in that they have a representative value set for every field Core Data might serialize for the given
managed object model.
After records are successfully uploaded the schema will be visible in the CloudKit dashboard and
the representative records will be deleted.
This method returns YES if these operations succeed, or NO and the underlying error if they fail.
Note: This method also validates the managed object model in use for a store, so a validation error
may be returned if the model is not valid for use with CloudKit.
*/
open func initializeCloudKitSchema(options: NSPersistentCloudKitContainerSchemaInitializationOptions = []) throws
プロダクションへの反映
スキーマをプロダクションに反映すると、ReordType や Attribute はもう変更できない。新しい RecordType の追加はできるし、既存の Record に新しい Property を追加することはできるが、修正したり削除することはできない。
詳しくは下記。
プロダクションスキーマの更新
CloudKit の RecordType やフィールドは変更不可能であるため、モデルに変更を加えたい場合は以下を検討する。 新しいフィールドを既存の RecordType に追加する。ただし、前バージョンのアプリからはそのフィールドは読み取れない
Entity に Version attribute を含め、アプリバージョンによってフェッチするデータをフィルタする
概要
WIP
大体以下に書いてある。型がどう変換されるとか、Relationship がどのように iCloud 上で実現されているかとか
Tips: データの一意性
既存の Core Data を利用したアプリで CloudKit を利用しようとした場合、問題が発生する場合がある。データの重複である。ある端末のデータがまず iCloud にアップロードされると、他の端末にそれがダウンロードされる。その端末内の既存のデータとは別に。結果、全てのデータが二重になってしまう。ということが起き得るらしい。 自分は体験していないからわからないけど、理由を聞くとなんとなくわかる。全てのデータは Attribute に UUID を持っていたらしいが、それは別に主キーというわけではない。CloudKit ではユニーク制約は使えないし。そのため、CloudKit からすると、どれとどれが同一のデータなのか はわからない。そのため、一度 CloudKit にアップロードされ、その後同期されたデータなら問題ないけど、元々端末内にあったデータについては、CloudKit からすればたまたま似たようなデータがその端末のローカルに存在しただけなので、無視して CloudKit からデータを DL してしまう。 Cloud からのデータ取得を通知する
CloudKit の同期をアプリのバックグラウンド時に行う
方法が見つからなかった。
iCloud 同期がオフからオンに切り替わっても変更をマージできる
iCloud 同期設定をアプリ内から切り替える
code:swift
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "MyModel")
guard let description = container.persistentStoreDescriptions.first else {
fatalError("Failed to retrieve a persistent store description.")
}
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
container.loadPersistentStores { _, error in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}
iCloud 同期設定の確認をする
iCloud 同期設定/iCloud アカウントのアプリ外での変更を検知する
code:swift
NotificationCenter.default.addObserver(self,
name: .CKAccountChanged,
object: nil)
@objc
func didChange(notification: NSNotification) {
CKContainer.default().accountStatus { status, error in
if let error = error {
fatalError("Failed to check iCloud account status. \(error.localizedDescription)")
}
switch status {
case .available:
// iCloud アカウントが利用可能
case .noAccount:
// iCloud アカウントログインされていない
// (ただし、iCloud 設定を端末の設定画面からオフにしてもこれになるようだ)
case .couldNotDetermine:
// なんらかの理由でステータスを判断できなかった
case .restricted:
// システムが iCloud アカウントへのアクセスを拒否された
@unknown default:
// 不明
}
}
}
別の iCloud アカウントに切り替えた場合の挙動はどうなるか
以下のようなエラーが出て、CoreData ローカルのデータが勝手にリセットされた。ので、アプリケーション側でのハンドリングは不要そうだった
code:text