ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Core Dataスキーマを進化させる
Appのアップデータ後にCore Dataスキーマをスムーズに移行し、データモデルの変更を簡単に行う方法を紹介します。組み込みの移行ツールを活用してデータストレージを最新の状態に保ち、Core Dataがスキーマを解析してデータモデルの移行を推測する方法について解説します。また、ベストプラクティスも紹介するので、移行に伴う困難な問題にも対処できるようになります。Core Dataスキーマが、CloudKitと連携して、クラウドでの容易な移行をサポートする仕組みをご覧ください。 このセッションを最大限に活用するには、Core Dataスキーマとデータタイプに関する知識と、Core DataベースとCloudKitの同期に関する基本的な知識を習得しておくことをお勧めします。
リソース
関連ビデオ
WWDC23
WWDC22
-
ダウンロード
♪ ♪
David Stites: Core Dataスキーマを進化させる 私は Core Dataチームのエンジニア David Stitesです このセッションではAppの Core Dataのスキーマの 更新や移行する方法についてお話します 本セッションの課題はスキーマの移行とは何か なぜAppがデータモデルを 更新した後に実行するのか 既存のスキーマの移行の仕方や CloudKitでのスキーマの移行が どう相互作用するのかを学ぶことです まず スキーマの移行とは何か なぜデータモデルを更新する際に 移行が必要なのかを説明します
Appの進化に伴い データモデルを 変更する必要があり得ます データモデルの更新はその変更を 元になるストレージのスキーマに 適用する必要があります このデータモデルを考察しましょう Aircraft のエントリーは type と numEngines の 2つの属性を持っています これらの属性は 元となる ストレージに反映されます 乗客数の属性を追加する場合 それに対応するストレージにも 追加する必要があります 移行後は 変更が完全に 元になるストレージに反映されます 元になるストレージに 変更をマイグレートしないと 変更後のモデルは ストレージに使用されているモデルと 一致しないため Core Dataは 永続ストアを開くことを拒否します 不適合なストアを開けば Incompatible Version Hashの エラーが発生します このエラーが表示された場合は 移行が必要であることを示します さて スキーマ移行とは何か なぜ Appを進化させるために 不可欠かを説明しましたが 移行がどのように行われるのか説明します Core Dataにはデータ移行ツールが 組み込まれており データストレージを 現在のデータモデルで 最新の状態に保つことができます これらは Lightweight Migration 軽量移行と呼ばれます
軽量移行が望ましい移行方法です 軽量移行は移行元と移行先の 管理対象オブジェクトモデルの違いを 自動的に分析し 移行方法を推論します Core Dataは実行時にNSBundleクラスの .allBundlesと.allFrameworksメソッドで 返されるバンドル内のモデルを探します 軽量移行は Appで行った変更を データベーススキーマに反映するための マッピングモデルを生成します
軽量移行を使用するには データモデルへの変更が 適切な移行パターンに適合している 必要があります
軽量移行で使える 属性に関する操作としては 属性の追加 削除 非オプショナル属性のオプショナルへの変更 オプショナル属性の非オプショナル化と デフォルト値の定義 属性のリネームなどがあります 属性の名前を変更する場合は 変更先モデルのリネーム識別子を 変更元モデルの 対応する属性の名前に設定します
リネーム識別子はXcode Data Model Editorの property inspectorで確認できます 例えば Aircraft のエントリーの color属性をpaintColorに変更してみます リネーム識別子は正規名を作成するので その属性がリネーム識別子を すでに持っている場合を除き ソースモデルの属性名に リネーム識別子を設定します このリネーム識別子を使って モデルのバージョン2で属性名を変更し バージョン3で 再び名前を 変更できるということです バージョン2から3への移行と バージョン1から3への移行で リネームは共に正しく行われます
軽量移行はリレーションの変更にも 無理なく対応できます 新しいリレーションの追加や 既存のリレーションを削除できます また 属性と同様にリネーム識別子を使用して リレーションの名前を変更できます さらに リレーションの種類の変更も可能で 例えば 1対1から1対多への移行や 順序なしの1対多から順序付きへ またその逆の移行も可能です
エンティティも軽量移行の 対象となります 新しいエンティティの追加 既存のエンティティの削除 エンティティの名前の変更が可能です また 新規に親や子エンティティを作成し エンティティ階層内で 属性を上下に移動することも可能です エンティティを階層に移動したり 階層から外したりできます ただし エンティティ階層のマージはできません 2つの既存のエンティティが 移行元で共通の親を持たない場合 移行先で共通の親を持つことはできません 軽量移行は2つのオプションキーで 制御されます NSMigratePersistentStores- AutomaticallyOption と NSInferMappingModel- AutomaticallyOption です この2つが YES の場合 ストアを永続コーディネーターに 追加する時に 永続ストアと現在のモデルで 不一致を検出すると Core Dataが自動的に軽量移行を 実行します NSPersistentContainer または NSPersistentStoreDescription を 使用する場合 これらの選択は自動的に設定されるため 何もする必要はありません NSPersistentStoreCoordinator の .addPersistentStore(type:configuration: at:options:) のような 代替APIを使用している場合 NSMigratePersistentStores- AutomaticallyOption と NSInferMappingModel- AutomaticallyOption を YESに設定し軽量移行を要求します Core Dataは 永続ストアが 現モデルと一致しないことを検出すると 自動的に軽量移行を実行します
以下 コードでその仕組みを説明します まず Core Dataをインポートして マネージドオブジェクトモデルを作成し 次に そのモデルを使って 永続ストアコーディネータを作成します ストアを永続化コーディネータに追加する時に 作成したオブションを指定します コーディネータにストアを追加すると 必要に応じて自動的に移行が行われます どのAPIを使ったとしても データモデルの変更は Appにバンドルされた 同じモデルで直接行えます 変更のためにモデルの新らしい版を 作成する必要はありません Core Dataが移行元と移行先モデルの間の マッピングモデルが推論可能かどうか 実際の移行作業を行わずに 事前に判断したい場合は NSMappingModel.inferredMappingModel のメソッドを使用します Core Dataがモデルを作成できた時に 推論されたモデルを返します それ以外の場合はゼロを返します
時には スキーマへの複合的な変更が 軽量移行の能力を超えることがあります その問題に対処し かつ軽量移行を 使用する方法について説明します 先ほどのモデルに戻り FLIGHT_DATA はバイナリデータ用の 外部ストレージに格納されており flightData にそのファイルのパスを持っています その属性を変更して データを内部に保存し 外部ストレージを削除する 必要があるとします この移行が軽量移行の機能に 当てはまるか確認したところ 当てはまらないと判明しました この変更を行うことができず 立ち往生しているように見えます しかし 心配は無用です 複数のステップになりますが 軽量移行はより複雑で非適合な移行を 実行できる可能性があります
軽量移行の対象とならない移行タスクを 軽量移行の対象となる最小限の一連の移行に 分解します 元のモデルがAで 目的のモデルがBの時 モデルBに軽量移行の対象と ならない変更があれば 変更を分解した1つまたは 複数のモデルバージョンを 導入することでブリッジを作成できます
導入された各モデルは 不適合な変更を構成する 軽量移行の能力の範囲内の 1つ以上の工程を持つことになります 各モデルは軽量移行が可能ですが 結果的に非適合な移行と同等となる 一連の移行が行われることになります 軽量化の対象外だった例に戻ると 元のモデルは A です 新しいモデルのバージョン A' を導入し 外部ファイルから取り込んだデータを 一時的に保存するための 新しい属性"tmpStorage"を追加して タスクの分解を開始します
次に 外部ファイルから 新しい属性にデータをインポートします このデータをインポートするためのコードは Core Dataが提供する機能とは 別に用意します このインポートの実行は 移行の間で行われます 無事にデータがインポートできたら モデル A' から もう一つ新しいバージョン モデル A'' を作ります A'' では 古い外部ストレージの属性を削除し 同時に 新しい属性の名前を変更します 説明した各ステップは 軽量移行の能力の範囲内です 軽量移行オプションが設定された 永続ストアを開き 未処理の各モデルを 順番に繰り返し処理する イベントループを構築してやれば Core Dataがストアの移行を行います 外部ファイルからデータを インポートした例のように 移行中にApp固有のロジックを実行する場合 そのロジックは プロセスが 終了し移行が中断された時に "再起動可能" でなければなりません Core DataとCloudKitを使用するAppの場合 Core Dataでデータモデルを設計する際に 注意すべき点があります Core Dataストアと CloudKitデータベースの間で レコードを受け渡すには データモデルの理解の共有が必要です このモデルは Core Data の モデルエディタで定義され その後 CloudKitのスキーマの 生成に使用されます 生成されたスキーマは最初は Development環境で作成され その後 Production環境に昇格されます CloudKitはCore Dataモデルの全ての機能を サポートしているわけではないことに 注意が必要です モデルを設計する際には以下の制約を意識して 互換性のあるデータモデルを作成してください 例えばエンティティに対する ユニーク制約はサポートされていません 属性タイプとして未定義と objectIDはサポートされていません リレーションはオプショナルで 逆リレーションを持たなければなりません また CloudKitは削除拒否の ルールをサポートしません Appの開発ではDevelopment環境を使用します この環境では CloudKitのスキーマを 自由に変更できます しかし スキーマをProductionに昇格させた後は レコードタイプとそのフィールドは 不変になります 軽量移行は様々なシナリオに対応しますが CloudKitは対応するものが限定されます 先ほど説明した 軽量操作の多くは未対応です CloudKitで具体的にサポートされるのは 既存のレコードタイプへの 新しいフィールドを追加と 新しいレコードタイプの追加です 既存のレコードタイプやフィールドの 変更や削除はできません モデルスキーマを変更する際には この制限を考慮してください
データモデルを更新する時は 軽量移行はローカルのストアファイルの スキーマの変更のみを 行うことに留意してください ストアがCloudKitで使用されているか どうかに関わらず 移行はディスク上のストアを変更するだけで CloudKitのスキーマには変更を加えません スキーマ・イニシャライザーを実行して Developmentデータベースでの変更を行い CloudKit Consoleを使用して Developmentでの変更を Productionにプロモートする必要があります Appのユーザーは新しい型だけでなく まだ古い型を使っている人がいることを 念頭に置いてください 最新版のAppはスキーマに追加された 新しい内容をもちろん知っています 旧い版では 新しいフィールドや レコードタイプを知りません
CloudKitのスキーマは基本的に追加型なので 古い版のAppを実行しているデバイスへの スキーマ移行の影響を考慮してください 例えば よくある落とし穴で 古い版のAppでは使用し 新しい版で使わない古いフィールドの 更新のし忘れがあります CloudKitスキーマを移行する ための戦略を紹介します 1つ目は 既存のレコードタイプに 新しいフィールドを段階的に追加することです この場合 古い版では ユーザーが作成したすべての レコードにアクセスできますが すべてのフィールドではありません
2つ目は 版の属性を持たせてエンティティを バージョン管理し フェッチ要求で現在のAppの版と互換性のある レコードのみを選択することです この手法を採用すると古い版のAppは より新版で作成したレコードを取得しないため そのデバイスではレコードが効果的に隠されます 最後の方法は NSPersistentCloudKitContainerOptions を使用して 完全に新しいコンテナを作成し 新しいストアを新しいコンテナと 関連付ける方法です ユーザが大きなデータセットを持っている場合 iCloudへのデータセットのアップロードに 時間かかるので注意してください どの方法でもデータモデルの 設計には注意が必要です 版間の互換性の問題を考慮し 異なる版のデータモデルを 一緒にテストすることをお勧めします さて データモデル 移行 CloudKitについて 十分に説明したところで 実際にデモを行います ご推察の通り私はパイロットです 飛行時間を記録する小さなAppを作りました 以下はそのAppのデータモデルです LogEntryという1つのエンティティを作成し 航空機の種類 飛行時間 出発地 目的地 尾翼番号などの属性を追加して 必要な経験情報を記録できるようにしました このAppを初めて実行すると Core Dataがストアを作成し そのストアにスキーマを実体化します Appを実行する前に環境変数 com.apple.CoreData.SQLDebug と com.apple.CoreData.MigrationDebug を オンにしておきます これにより Core Data は その実行ステップを記録します これらの設定を行なった状態で Appを実行してみます
Appを起動するとCore Dataは その実行ステップを記録しています ファイルの作成 ストアのメタデータの作成 スキーマの実体化 SQLiteは私たちのスキーマを含む テーブル ZLOGENTRY を作成しました これは sqlite3 コマンドラインツールを使って ストアファイルを見ることで確認できます LogEntry テーブルがあり データモデルで作成した 属性に対応するカラムがあります いくつか軽量な変更を加えてみます
新しいエンティティ Aircraft Pilot Airport を追加します これでスキーマを正規化できます LogEntryエンティティの属性の一部を リレーションシップに変更します たとえば destination と origin は文字列属性から Airport to one リレーションシップに変更します また Airport エンティティには icaoIdentifier と faaIdentifier という 2 つの新しい属性があります type 属性は新しいエンティティ Aircraft に昇格し tailNumber と registrationNumber という 2 つの新しい属性を追加しています LogEntry では LogEntry から Aircraft への 1 対 1 のリレーションを作成しています
最後に名前と証明書IDを持つ Pilot エンティティを追加しました
各ログエントリは Pilot エンティティに 関連付けられます データモデルの変更が完了したので Appを再度実行してみます
おっと Appの実行でエラーが出ました NSPersistentStore- IncompatibleVersionHashError となっています このエラーは現在のモデルが ストアのモデルのスキーマと 一致しなくなったことを意味します ストアスキーマの移行が必要です その方法は3つあります 最初の方法は NSPersistentContainer を 使用するようにコードを変換することで 軽量移行オプションが自動的に設定されます 2番目は NSPersistentStoreDescription の使用で ここでも軽量移行のオプションが 自動的に設定されます 最後に 3つ目の方法は 軽量移行のオプションを手動で設定し ストアを開くときにその設定を コーディネータに渡します 最初の方法 NSPersistentContainer を使ってみます NSPersistentContainer を使う コードに変更したので Appを起動して Core Dataが ストアファイル内のスキーマを 移行していることを再度確認します
再び sqlite3 コマンドラインツールで確認します 新しいスキーマは Core Data によって自動的に 軽量移行で実体化されています もっと簡単な方法はないでしょうか? デモを終える前に 3番目の オプションを紹介したいと思います この方法では軽量移行のオプションを 手動で設定し ストアを開くときにその設定を コーディネータに渡すことを 思い出してください 最終的な結果として ストアが新しいスキーマに移行される という点では同じです データモデルの変更は 軽量移行を利用しましょう 軽量移行は データモデルの 変更の大部分に対して 非常に柔軟で簡単に利用できます より複雑なデータモデルがある場合は そのモデルを軽量な変更で 構成されるものに分解します 最後に AppでCloudKitを使用する場合 データモデルの変更の影響を 慎重に検討してください データモデルを変更する時は 徹底的にテストをすること この情報が役に立ち あなたのプロジェクトでモデルを更新して 素晴らしい新機能を構築することを 検討していただけると幸いです ご搭乗ありがとうございました 素敵なWWDCになりますように
-
-
6:16 - Migrate your Core Data schema
import CoreData let storeURL = NSURL.fileURL(withPath: "/path/to/store") let momURL = NSURL.fileURL(withPath: "/path/to/model") guard let mom = NSManagedObjectModel(contentsOf: momURL) else { fatalError("Error initializing managed object model for URL: \(momURL)") } let coordinator = NSPersistentStoreCoordinator(managedObjectModel: mom) do { let opts = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true] try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: Optional<String>.none, at: storeURL, options: opts) } catch { fatalError("Error configuring persistent store: \(error)") }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。