LoginSignup
40
26

More than 3 years have passed since last update.

CoreDataとCloudKit: 9つのアドバイスと留意点、デバッギングのガイド

Last updated at Posted at 2021-01-29

Core DataCloudKit のフレームワークを合わせて用いるプロジェクトが色々あります。以前いくつか問題点があったので、皆さんがプロジェクトでセットアップのエラーを防げるように、問題点の多くをリスト化しました。

プロジェクトでそのような問題が起きないように、アプリケーションを発行する前にこのチェックリストによく目を通しておいてください。

既存のCoreDataデーターベースのクラウド機能を設定するためには、コードのNSPersistentContainerではなく、単にNSPersistentCloudKitContainerを使用してください。

1. ベーシックプロジェクト設定

iCloudコンテナー

必ず特定の名称フォーマットの iCloud CloudKit コンテナーを作成するようにして下さい:iCloud.あなたの_App_Bundle_ID.

バックグラウンドモード

必ずXcodeの Background Modes セクションで Remote notifications を有効にして下さい。

2. さまざまなデータベース環境

CloudKit データベースがローカルの CoreData データベーススキーマと同一のスキーマであることを確認してください

シミュレーターでiOSアプリケーションを実行、あるいはXcodeを使ってiPhone上のアプリケーションを起動する場合は、お使いのCloudkit データベース環境は開発モードにあります。

CoreDataとCloudKitを使用するプロジェクトを設定すると、レコード作成イベントが発生したときに初めてデータベーススキーマが作成されます。データベーススキーマをローカルに設定するだけでは、クラウドのデータベーススキーマは作成されません。

システムがクラウドサーバー上に適切なデータベーススキーマを作成するためには、すべてのプロパティを持つレコードを作成する必要があります。

たとえば、namebirthdayfavoriteToys というプロパティを持つレコードタイプCat があるとします。

.xcdatamodeldファイルでスキーマを定義した後も、 CloudKitデータベースにはスキーマが含まれません。

name プロパティを使用してシミュレータ上にレコードを作成すると、 CloudKitデータベースは、 name プロパティのみを含むCD_Catという名前の新しいスキーマを自動的に作成します。

CloudKitデータベースのスキーマをローカルスキーマと同じにするには、 namebirthdayfavoriteToysを含むテストレコードをローカルに作成する必要があります。

開発とプロダクション環境の違い

TestFlightまたはApp Storeを通じてアプリを実行すると、CloudKit データベース環境はプロダクションに設定されます。このプロパティを .entitlements ファイルに追加することでアプリを手動でプロダクション環境に設定することもできます:

<key>com.apple.developer.icloud-container-environment</key>
<string>Production</string>

開発データベースと本番データベース間のデータが同じではありません。

3. マージポリシーを設定する

Core Data には様々なマージポリシーがあります。マージポリシーは、リモートデータとローカル (メモリ内の) データの間に競合がある場合に処理します。

手動で処理することができますが、この記事ではマージポリシーを使用します。

データベースが自身に変更を書き込もうとする際には、コンフリクトが発生することがあります。(例えばオブジェクトの重複など。)

デフォルトで、Core Data フレームワークは例外を発生させます。

viewContextmergePolicy プロパティーを設定することで、コンフリクトの解消を試みることができます。

self.viewContext.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType)

以下は一部の結合ポリシーの説明です。

Name Explanation
NSErrorMergePolicyType デフォルト。try viewContext.save() を呼び出すと、例外が報告されます。
NSMergeByPropertyStoreTrumpMergePolicyType メモリ内の変更は外部変更を置き換えます
NSMergeByPropertyObjectTrumpMergePolicyType 外部変更はメモリ内の変更を置き換えます。
NSOverwriteMergePolicyType メモリ内のオブジェクト全体が永続ストアにプッシュされます。
NSRollbackMergePolicyType 対立する変更されたオブジェクトについて、すべての状態が破棄されます

通常、NSMergeByPropertyObjectTrumpMergePolicy を使用すると、Core Data はデータベースのデータバージョンを新しい変更で自動的に上書きします。

ここではCoreDataフレームワークのヘッダーファイルから説明します。

// This singleton policy merges conflicts between the persistent store's version of the object and the current in memory version. The merge occurs by individual property. For properties which have been changed in both the external source and in memory, the in memory changes trump the external ones.
@available(iOS 3.0, *)
public var NSMergeByPropertyObjectTrumpMergePolicy: AnyObject

メモリ内の変更が外部の変更を上書きするという意味です。

4. クラウドからの変更を自動的にマージします

Core Data がクラウドから変更を自動的に取得し、ローカルデータベースに適用できるようにする必要があります。

localStorageContext.automaticallyMergesChangesFromParent = true

5. まったく同じ値のオブジェクトを作成しないでください

データベースに保存する各オブジェクトにランダムなIDを追加し、同じオブジェクトが2つ存在しないようにしてください。

6. 複数のNSManagedObjectContextの利用は避けましょう

クラスの共有インスタンスを生成し、データベースを管理してください。複数のデータベースコンテキストを操作する場合、そのプログラムに問題が生じる場合があります。

class PersistenceController {
    static let shared = PersistenceController()

    let localStorageContext: NSManagedObjectContext

    init() {
        let container = NSPersistentCloudKitContainer(name: "[.xcdatamodeldファイルの名前]")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                print(error.userInfo)
            }
        })
        localStorageContext = container.viewContext
        localStorageContext.automaticallyMergesChangesFromParent = true
        localStorageContext.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType)
    }
}

7. ローカルの変更がiCloudにアップロードされるかどうかを確認します

シミュレータ上のすべてのレコードタイプとすべてのプロパティをテストする必要があります。テストを行った後、https://icloud.developer.apple.com に移動し、作成したレコードがそこに存在することを確認します。

メニューからアプリケーションを選択し、Development データベースの Records オプションを選びます。データベースをプライベートデータベースに設定します(なぜなら、コアデータは設定をプライベートデータベースに保存するからです)。ゾーンを com.apple.coredata.cloudkit.zone に設定し、Type:CD_[あなたのデータ型] に設定します。

クエリー操作を許可してください

レコードに対する問い合わせを行う前にクエリー操作の許可を求められる場合があります。

トップメニューを選択し、Schema をクリックしてください

その後、タイトル Record Type をクリックし、Indexes オプションを選択してください

データ型名 (CD_[あなたのデータ型]) を選択し → Add Index をクリックし → recordName を選択し → Save Changes をクリックして下さい

これで保存されたコアデータの全記録を見て、記録が適切にバックアップされているかが確認できるようになっているはずです。

テストアプリを走らせる際のアカウントはアップル開発者アカウントと同じものにして下さい。アクセスできるのは、ご自身の開発者アカウントのiCloudにあるプライベートデータのみです。

また、製品に変更を施す場合には、その前に必ずインデックスキー recordName を削除して下さい。このインデックスは不要です。

8. 制作環境に変化を加えましょう

ギアのアイコンをクリックし、Deploy Schema to Production をクリックすることで、開発環境から制作へと変化を加えることができます。アップストアでアプリを公開する前にそれを行う必要があります。

スクリーンショット 0003-01-29 16.33.20.png

9. データベーススキーマに変更を加えるたびに、新しいデータベースバージョンを作成し、その新バージョンを現在のデータベースとして設定してください

Core Data ローカルスキーマに変更を加える際には、システムがデータの移行方法を把握できるよう新たなデータベースバージョンを作成する必要があります。また、プロパティの削除と追加を同時に行うことは(データを移行するコードを手作業で記述しない限り)お勧めしません。

私は通常、プロパティを削除せずに新しいプロパティを追加するだけにしています。

新しいデータベーススキーマバージョンを作成するには、データベースモデルファイルを選択し、最上部の Editor メニューをクリックして Add Model Version を選択してください。

Finish ボタンをクリックすると、データベースの新しいバージョンを作成できるようになります。

スクリーンショット 0003-01-29 16.38.24.png

次に新しく作成したデータベースモデルのバージョンをクリックします(この例ではInkMemo 3.xcdatamodel)。修正しましょう。

そして右側のバーで、新しいデータベースバージョンを現在のバージョンとして選択してください。

データベースモデル内での変更が反映されるよう、コードファイルの更新を行ってください。

他の設定

また、 .xcdatamodel ファイルをクリックしてから、 CONFIGURATIONS セクション内の Default をクリックして、Used with CloudKit をトグルしてください

スクリーンショット 0003-01-29 16.42.21.png

この設定が機能に大きな影響を与えることはないと思います。この設定をオンにしなくても、データは普通にCloudKitにバックアップされているようです...

デバッグのヒント

アプリを実行するとコンソールに多くのログメッセージが表示されるはずです。これらのメッセージを必ず読むようにしてください。また、CloudKitダッシュボードで、すべてのデータベーススキーマが作成され、適切にデプロイされていることを確認してください。

ここでは、おおよその情報を得るために追加できるデバッグ引数をいくつか紹介します。

-com.apple.CoreData.SQLDebug 3
-com.apple.CoreData.Logging.stderr 3
-com.apple.CoreData.ConcurrencyDebug 3
-com.apple.CoreData.MigrationDebug 3
-com.apple.CoreData.CloudKitDebug 3

これらの引数は、RunモードのArguments Passed On Launchセクションで追加することができます。数値が大きいほど、Xcodeコンソールでより詳細なデバッグメッセージを得ることができます。

スクリーンショット 0003-01-29 16.47.22.png

また、シミュレーターでCloudKitをテストする際は、実際のデータを作成し、CloudKit Dashboardに行ってiCloudにデータがきちんとアップロードされているか確認してください。


この記事が Core Data + CloudKit の問題の修正にお役に立てますように。


:relaxed: Twitter @MszPro

:sunny: 私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。(52のiOS記事)

40
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
26