私の質問を効果的にするために、まず私が直面している正確なシナリオを考えてみましょう:
一般的な設定
- ホスト iOS 8 アプリ。
- ホスト アプリにバンドルされている 1 つ以上の iOS 8 拡張機能 (WatchKit、Share など)。
- ホスト アプリとすべての拡張機能は、共有アプリ グループ コンテナー内の同じ Core Data SQLite ストアを共有します。
- 各アプリ/拡張機能には、独自の NSPersistentStoreCoordinator と NSManagedObjectContext があります。
- 各永続ストア コーディネーターは、他のすべての永続ストアと同じ SQLite リソースをグループ コンテナー内で共有する永続ストアを使用します。
- アプリとすべての拡張機能は、共通のコードベースを使用して、インターネット上のリモート API リソースからコンテンツを同期します。
問題につながる一連のイベント
ユーザーがホスト アプリを起動します。リモート API リソースからのデータのフェッチを開始します。コア データ モデル オブジェクトは、API 応答に基づいて作成され、ホスト アプリのマネージド オブジェクト コンテキストに「アップサート」されます。各 API エンティティには、リモート API バックエンドでそれを識別する uniqueID があります。「アップサート」とは、API エンティティごとに、特定の uniqueID の既存のエントリが見つからない場合にのみ、ホスト アプリが Core Data に新しいエントリを作成することを意味します。
一方、ユーザーはホスト アプリの拡張機能の 1 つも起動します。これも、同じリモート API から何らかのフェッチを実行します。また、API 応答を解析するときに「アップサート」を実行しようとします。
問題:ホスト アプリと拡張機能の両方が同じ API エンティティのコア データ エントリを同時にアップサートしようとするとどうなりますか? これがどのように発生するかを確認するために、アップサートの一連のイベントを見てみましょう。
コア データ アップサート シーケンス:
- API 解析コードは、特定の API エンティティの uniqueID を解析します。
uniqueID
パーサーは、解析された uniqueID と等しい述語に一致するすべてのエントリに対してコア データ フェッチを実行します。- 既存のエントリが見つからない場合、パーサーはこの API エンティティの新しい Core Data エントリを挿入し、その
uniqueID
属性を解析済みの uniqueID に設定します。 - パーサーは管理オブジェクト コンテキストを保存し、新しいエントリ データを SQLite バッキング ストアにプッシュします。
問題の詳細
ホスト アプリと拡張機能が、同じ API エンティティの API 応答を同時に個別に解析しているとします。ホスト アプリと拡張機能の両方がステップ 4 を完了する前にステップ 3 に到達した場合、両方とも同じ uniqueID の新しい Core Data エントリを挿入しようとします。ステップ 4 に到達save:
し、それぞれの管理対象オブジェクト コンテキストを呼び出すと、Core Data は問題なく重複したエントリを作成します。
私の知る限り、Core Data には属性を一意としてマークする方法がありません。SQLite INSERT OR IGNORE
+UPDATE
コンボに相当する Core Data が必要です。. または、永続ストアの SQLite バッキング ストアを「ロック」する方法が必要です。これは、トラブルのレシピのように聞こえます。
iOS 8 拡張機能によって導入された、このかなり新しい問題に対する既知のアプローチはありますか?