2018年3月4日日曜日

CloudKit(2): Operation

前回、CloudKit(1): DatabaseとRecord に続き、今回はCloudKitのOperationについて。

Databaseの取得

let privateDB = CKContainer.default().privateCloudDatabase

CKContainerからPublic, Private, Shared のDatabaseが取得可能。
CKContainer.default() は、アプリデフォルトのコンテナで、その設定はCapabilities, entitlementsにある。
アプリ間で同じコンテナを参照したい場合は、カスタムコンテナの作成を行う。
CloudKit Quick Start - アプリケーション間でコンテナを共有する


Databaseへデータ登録や取得

RecordやZoneなどをDatabaseに登録・取得する方法は2種類ある。

1. Databaseにあるメソッドを使う方法

saveRecord, fetchRecordWithID など、Databaseのメソッドを使う。
クラスメソッドさんの記事 にある方法。

2. CKOperation を使う方法

CKOperation のサブクラスが各操作ごとに用意されている。



たとえば、Recordを保存する場合は、

CKOperation は、CKDatabase の add メソッドで追加ができますが、databaseプロパティにDatabaseを設定していれば自分で作成したOperationQueueでもOK。

各CKOperationには ***CompletionBlock があるので、その処理が完了した時にcallbackすることも可能。
使い方は基本同じなので、該当するCKOperationのクラスを探して実装していけばOKと思う。

複数の処理をまとめて行いたい場合は、これらのOperationを使うと良さげ というのは理解し易いと思う。
CKOperationGroup を使えば、まとめてRollbackもしてくれる(はず)。
でも、それ以外にもメリットがある。
  • キャンセルがしやすい
  • 複数のRecordをまとめて指定できる(network request quotaの最適化)
  • desiredKeys の指定がある (複数回ダウンロードしたくないAssetとかに使えるっぽい)
  • Timeoutの制御
  • Long-Lived Operations
WWDCのセッションでも推奨しているような口ぶりでしたし、特に理由がない限りはCKOperationを使う方向でよさそうに思う。

ということで、今私は、単発処理も含め全てのDBアクセスをCKOperationで記述している。
ちなみに、DatabaseやZoneの変更系と、アプリで利用しているRecordの更新系の2つのQueueを用意して実装している。


CKOperationのTimeout

OperationのqualityOfService の設定により、タイムアウトの時間が変わる。



デフォルトは、7 days。
オフラインの時は、オンラインになるまで待ってから処理してくれる。

ちなみに userInteractive は 60sec となっているが、オフライン時はすぐにNetwork Unavailableのエラーが返ってくる。


Long-Lived Operations

アプリが終了した後も引き続き処理を行ってくれる仕組み。
詳しくは こちらのApple Documentを。

AirPlaneモードにして試してみたが、fetchAllLongLivedOperationIDs のIDが0件になってしまう。謎。
LongLivedのOperationIDを保存しておいて、明示的に指定して取得するとOperationを取得できる。謎。

Operationを実行
アプリ再起動時にLongLivedOperationを取得
関連: Apple Developer Forum - How to fetch all long-lived operations on iOS

fetchLongLivedOperationで取得したOperationには、callbackが設定されていないようなので改めて設定してから実行し直す必要がある。
「Long-Lived Operations」って名前を見た第一印象は、"裏で勝手に動き続けてくれ、処理完了したらアプリが自動的に裏で起動して動く?" というイメージを持っていたのだが、そうではなく、アプリ起動時に、完了していないOperationの情報を取得できるので再実行できる という機能のよう。



クライアントでRecordをキャッシュ

クライアントでRecordをキャッシュしたり、変更差分を更新したりするコードはアプリ側で全て用意する必要がある。CloudKitに便利な機能はない。

ちなみに、CoreData + iCloudの統合機能はiOS10でdeprecatedになったようだ。

試していないが、RealmとCloudKitの連携フレームワークなどもある様子。

だが私は、現時点でフレームワークを使うのは避けた方がいいのではと思っている。
CloudKit のAPIはまだまだ変更が多いのと、データ通信量の増加・フレームワークの学習コストなどをカバーできるぐらい楽になるのか? というと疑問なので。


差分の取得

Database, Record Zoneは serverChangeToken を使って差分のみの取得が可能。
(Default Zone 以外)
Fetch完了時に取得できるtokenをアプリでキャッシュしておき、次回Fetch時に指定する。

Databaseの場合は、CKFetchDatabaseChangesOperationのインスタンス時に前回取得していたServerChangeTokenを渡す。

Record Zoneの場合は、CKFetchRecordZoneChangesOptionsに設定する。

Tokenの期限切れエラーの場合は、Tokenをクリアし全取得しなおしが必要。




次回はSubscriptionを!


• • •