☁️

CloudKitを使用した、iCloud同期設定を行う際のタイミング制御の違い

に公開

CloudKit

CloudKitを使って自分のアプリにiCloudのバックアップ機能を追加しました。
そもそもCloudKitとは何かというのはこちらに書かれています。
CloudKitを使ったアプリの開発
https://developer.apple.com/jp/icloud/cloudkit/


CloudKit Documentation
https://developer.apple.com/documentation/cloudkit/


アプリ開発をしている際に同期するタイミングをどのように設定しようか考えていたところ、アプリの実装によっては細かく設定できたり、自動だったりとやり方によってできることとできないことがあることに気付いたので比較画像表を作りました。


同期制御レベルの概要

  1. 完全手動制御(◎) - 生のCloudKit API
    • 同期のタイミングを完全に開発者がコントロール
    • すべての同期操作を明示的に記述する必要あり
      詳細な説明
      生のCloudKit API利用
      生のCloudKit APIを使用する方法では、開発者が同期プロセスを完全にコントロールします。この方法の特徴として、クラウドとのデータ同期はすべてアプリ側からCKDatabaseの操作(レコード保存やフェッチなど)を明示的に呼び出して行う必要があります。
      CKDatabaseやCKOperationを用いることで、レコードの取得・保存を任意のタイミングで実行でき、同期のタイミングを細かく制御することが可能です。ただし、バックグラウンドでの自動同期機能は提供されていないため、ネットワーク状況の監視やエラー時の再試行ロジック、プッシュ通知受信時のデータフェッチなどを開発者自身で実装する必要があります。

CloudKitは「コンテナ」「データベース」「レコードゾーン」「レコード」という階層構造でデータを管理します。CloudKitはCKContainer、CKDatabase、CKRecordZone、CKRecordという階層でデータを整理し、Appleのオペレーティングシステムとデバイスに深く結びついています。
CloudKitのコアとなるのが「アプリのデータを保存するキーと値のペアのコレクション」であるCKRecordと、「レコードゾーンとサブスクリプションのコレクションを表すオブジェクト」であるCKDatabaseです。
同期処理については、開発者が完全に制御する必要があります。オフラインモードでの対応や同期のタイミング制御など、実装にはさまざまな考慮が必要です。

ドキュメント
A Guide to CloudKit: How to Sync User Data Across iOS Devices
CKRecord
CKDatabase


  1. 半自動制御(○) - CloudKit Sync Engine
    • デフォルトは自動だが、必要に応じて手動介入が可能
    • システム管理の自動同期と開発者による手動同期を組み合わせられる
      詳細な説明
      CloudKit Sync Engine利用
      CloudKit Sync Engine(iOS 17以降で利用可能)は、半自動の同期制御を提供します。デフォルト設定では、システムが同期を自動的にスケジューリングします。CKSyncEngineがCloudKitのプッシュ通知を自動監視し、通知受信時に内部のスケジューラで同期処理を行います。
      automaticallySyncプロパティを使用することで自動同期の有効/無効を切り替えることができます。自動同期を無効化した場合は、開発者が必要なタイミングでsendChanges()やfetchChanges()などのメソッドを明示的に呼び出して同期を実行することで、同期フローに介入することが可能です。

CKSyncEngineは、iOS 17で導入された新しいAPIで、「ローカルとリモートのレコードデータの同期を管理するオブジェクトです。
WWDCの発表によると、「CKSyncEngineはCloudKitデータベースとの同期に関する一般的なロジックをカプセル化し、柔軟性を保ちながら便利なAPIを提供することを目的としています」
CKSyncEngineによって、「従来のCloudKit APIを直接使用する場合と比較して、プッシュ通知の処理やサーバー変更トークンのキャッシュ、ゾーンセットアップなどの複雑な作業が大幅に簡略化されます」
同期は基本的に自動化されていますが、「必要に応じて手動制御することも可能です」

ドキュメント
CKSyncEngine
Sync to iCloud with CKSyncEngine
Syncing data with CloudKit in your iOS app using CKSyncEngine and Swift


  1. 自動のみ(×) - Core Data + CloudKit
    自動のみ(×) - Swift Data + CloudKit
    • 同期タイミングはシステムが完全に管理
    • 開発者による同期タイミングの直接制御は不可
      詳細な説明
      Core Data + CloudKit利用
      Swift Data + CloudKit利用
      Core DataとCloudKitを組み合わせた方法では、同期プロセスは完全に自動化されています。Core DataローカルストアとCloudKitの同期(ミラーリング)はフレームワークにより自動管理され、コンテキストを保存すると変更が自動的にアップロードされ、他端末の変更も適宜フェッチされます。
      この方法では同期タイミングを直接操作するAPIは提供されておらず、開発者が「今すぐ同期開始/停止」といった制御を行うことは基本的にできません。同期のタイミングを調整したい場合は、CloudKitと連携しない一時的なCore Dataストアに書き込んで後で移行するなどの迂回策が必要になります。

NSPersistentCloudKitContainerは、「アプリのCore Dataスタックをカプセル化し、選択された永続ストアをCloudKitプライベートデータベースにミラーリングするコンテナ」です。
2019年のWWDCで発表されたこの技術は、「Core Dataのアプリをすべてのユーザーのデバイス間で同期させるための便利なラッパー」として機能します。
「ローカルで行われた変更は自動的にリモートのCloudKitデータベースに送信され、リモートの変更はバックグラウンドプッシュ通知を通じてフェッチされ、デバイス間のローカルストアに取り込まれます」
同期タイミングの制御はシステムによって完全に管理され、「NSPersistentCloudKitContainerはプッシュ通知を受信して対応するために必要なすべての作業を処理します」

ドキュメント
10. NSPersistentCloudKit Container
CloudKit Samples: Core Data with CloudKit
Getting Started With NSPersistentCloudKitContainer


  1. 限定的制御(△) - iCloudキー値ストア、iCloud Drive
    • 基本的には自動同期だが、一部の操作で同期を促進可能
    • システムポリシーにより実際の同期タイミングは制限される
      詳細な説明
      Cloudキー値ストア利用
      iCloudキー値ストア(NSUserDefaultsに類似)の同期はシステム管理で行われ、変更はバックグラウンドで自動的に各デバイスに同期されます。synchronize()メソッドを呼ぶことで明示的に同期を試みることも可能ですが、実際の同期タイミングはシステム次第であり、頻繁には行われません(約1分間に数回程度が上限)。
      この方法は主に少量の設定データを同期する用途に向いており、同期タイミングを細かく指定する用途には適していません。

NSUbiquitousKeyValueStoreは、「ユーザーの接続されたデバイス上で実行されているアプリのインスタンス間でデータを共有するために使用するiCloudベースのキーと値のペアのコンテナ」です。
このストレージは、「iOSがアプリから何かを書き込むとキー値ストアをiCloudにアップロードし、同じアプリの別のインストールに同じキー値ストアをダウンロードする」仕組みを提供します。
同期タイミングには制限があり、「最大で1分間に数回程度」で、「synchronizeメソッドを呼び出しても、iCloudに新しいキーと値を強制的に書き込むわけではなく、新しいキーと値がアップロードできることをiCloudに通知するだけです」
また、「合計ストレージ容量はユーザーあたり1MBで、キーあたりの値サイズ制限は1MB、最大1024キーまで」いう制限があります。

ドキュメント
NSUbiquitousKeyValueStore
Using NSUbiquitousKeyValueStore with SwiftUI
NSUbiquitousKeyValueStore sync issues
NSUbiquitousKeyValueStore vs NSUserDefaults

詳細な説明
iCloud Drive(ファイル同期)利用
アプリのiCloud Driveファイルは保存とともに自動的にクラウドへアップロードされ、他のデバイスとも同期されます(同期処理はOSがバックグラウンドで管理)。未ダウンロードのクラウド上ファイルは、NSFileManager.startDownloadingUbiquitousItem(at:)によりアプリからダウンロードを開始させることが可能です。
ただし、ファイルのアップロード/ダウンロード時期そのものを直接制御するAPIはなく、大容量データの転送タイミングやネットワーク条件判断はシステムに委ねられます(必要に応じてNSFilePresenter等で同期状態を監視・検知することができます)。

iCloud Driveを使用したファイル同期では、「アプリのドキュメントや設定データをiCloudに保存し、ユーザーの各デバイスと同期させることができます」
ファイルの操作は、UIDocumentやNSFileManagerなどのファイルマネージャAPIを通じて行い、同期はシステムによって管理されます。しかし、「アップロードやダウンロードの正確なタイミングを直接制御することはできません」

ドキュメント
Designing for Key-Value Data in iCloud
startDownloadingUbiquitousItem(at:)


比較画像表の作り方

最初はChatGPTで簡単な比較表を作ってもらう予定だったのですが、o3でうっかりDeep Researchをクリックした状態で調べてもらったら凄い時間をかけて大掛かりな作業になってました。その上詳細な説明欄が読めない状態だったのでそこはClaude 3.7 Sonnetにデータを渡してわかりやすい文章を作ってもらいました。
そしてChatGPT 4oで画像生成で簡単に作ってくれないかなと思ったのですが文字化けして全く出来ずじまい。またHTML+CSSで変換してもSVGファイルにしても画像生成ではダメだったので、そのデータをまたClaude 3.7 Sonnetで作り直してSVGファイルで作ってもらいました。その後にFigmaでSVGファイルを開いてレイアウトなどの崩れや文字列の整理、罫線の設定など手直しして作りました。

完全にAIだけでは、比較画像表は作れませんでした。


引用

Sync to iCloud with CKSyncEngine
https://developer.apple.com/videos/play/wwdc2023/10188/#:~:text=If you want a full,improve your app's sync experience

Prevent CloudKit Sync and Trigger sync manually
https://stackoverflow.com/questions/73343274/prevent-cloudkit-sync-and-trigger-sync-manually

How to enforce immediate Coredata & CloudKit sync?
https://stackoverflow.com/questions/71163121/how-to-enforce-immediate-coredata-cloudkit-sync

Proper handling of NSUbiquityKeyValueStore updates across devices?
https://stackoverflow.com/questions/13402938/proper-handling-of-nsubiquitykeyvaluestore-updates-across-devices#:~:text=You should read the documentation,whole%2C not just your app

startDownloadingUbiquitousItem(at:)
https://developer.apple.com/documentation/foundation/filemanager/startdownloadingubiquitousitem(at:)

Discussion