2018年3月6日火曜日

CloudKit(3): Subscription

CloudKitシリーズ第三弾、Subscription。
Subscriptionとはデータが変更されたタイミングをPushで受け取ることが出来る仕組み。


実はCloudKitは、Record Zoneの種類によって利用出来るSubscriptionが違う。
Subscriptionを使わない場合でも、ここを理解すること、Record Zoneの設計方法が少し見えてくるはず。


Subscriptionの種類

Subscriptionには以下の3種類がある。

Subscription対応する差分取得Operation
CKDatabaseSubscriptionCKFetchDatabaseChangesOperation
CKRecordZoneSubscriptionCKFetchRecordZoneChangesOperation
CKQuerySubscriptionCKFetchNotificationChangesOperation
第一回: CloudKit(1): DatabaseとRecord で説明したように、Record Zone には Default と Custom, Shared の3つの種類がある。それらのRecord ZoneごとのSubscription利用可・不可をマトリックスにすると以下になる。

Public DBPrivate DBShared DB
Default ZoneDefault ZoneCustom ZoneShared Zone
CKDatabaseSubscription××
CKRecordZoneSubscription×××
CKQuerySubscription×

前回の差分の取得のところで、さらっと 「Default Zone以外」と書いた。
じゃぁDefault Zone では差分取得ができないのか?
Default Zoneで差分を取得するには、CKQuerySubscription を利用できる....のだが....
CKFetchNotificationChangesOperation が iOS11 で deprecated になってしまった。
代わりにCKDatabaseSubscription, CKFetchDatabaseChangesOperation, CKFetchRecordZoneChangesOperation を使えとなっていますが、これらはDefault Zoneでは使えない。
詳しい調査はしていませんが、もし方法がないのであれば、Default Zoneでの差分取得はできなくなった(?)
関連: Apple Developer Forum: Notifications in iOS 11

ほかにも、CKFetchRecordChangesOperation がiOS10で deprecated になっているのをみると、Tokenを使ったRecord差分取得は、 CKFetchRecordZoneChangesOperation のみに限定...言い換えると、「Default Zone 以外で全件対象のみ可能」とする方向なのかもしれない?
(あくまで個人の感想)

以上の情報から、
Private DBでデータ量が多いものは特に、Custom Zone にしておくのが無難と思った。Customは選択肢が多いので状況に合わせていろいろ選択しやすい。
2014年のWWDC: Advanced CloudKit で、Custom ZoneはAtomic Commits ができるよ とも言っているし、なんか、ほかにも色々ありそうだ。
そして、可能な限り、Custom Zone は複数に分けておくとよさそう。
(Zoneが違うRecordを一気に更新することはできないので注意)

Default Zoneを使う場合は、一回のQueryが適切な件数になるようUIやIndexをうまく設計する というところだろうか。
さらに、Public DBの場合は、競合がなるべく起こらないような設計が大事そうだ。

ちなみに、Record Zoneの変更があると、DatabaseSubscriptionにも通知が届く。
そのため、Record Zoneの変更トリガーはCKDatabaseSubscription か CKRecordZoneSubscription のどちらかを登録していればOK。
アプリ利用中にRecord Zoneが増減するのであれば、CKDatabaseSubscription を使い、CKRecordZoneSubscriptionは使わない という選択もアリ。


Subscriptionの登録

3つのSubscription、ともに、CK***Subscriptionを作成し、CKModifySubscriptionsOperation で登録していく。

DatabaseSubscriptionの登録例
CKRecordZoneSubscriptionはZoneIDを、CKQuerySubscriptionは対象のRecord Type, NSPredicate(条件),タイミング(作成、変更、削除時)などを指定できる。


通知を受ける

通常のPush通知と同様、registerForRemoteNotifications しておいて、UIApplicationのdidReceiveRemoteNotification にて取得する。

通知設定の登録 通知の受信
Database, RecordZone のSubscriptionの受けには、前回の記事で書いた、Tokenを使った差分の取得をすれば良い。(※1)
CKQueryNotificationは、対象のRecordIDが取得できるが、通知が必ず届くとは保証されないので、そのRecordを最新にすれば全て最新の状態になるとは言えない。
だが、前述したように、通知の差分を取得するためのCKFetchNotificationChangesOperationがdeprecatedになったので、差分の取得をせずCKQueryOperationで検索かけて最新になるようするのが良さそうだ。


終わりに

SubscriptionはCloudKitを理解するための大きな山場だと思う。
当初、私はCustom Zoneを、「Defaultではない自由に作れるZone」 というイメージしか持っていなかったので混乱した。こんなに機能が違うのかと。
この記事の内容をざっくり理解してから、ドキュメントやサンプルコードをみると理解し易いのではないかと思う。


次回はShareについて書く予定!


※1
以下は、CKDatabaseSubscriptionで通知を受けた時、Record Zoneも一緒に反映する例としてもう少し詳しく書いたもの。長いので文中からは削除したがせっかくなのでペタリ。





• • •