☁️AquaSKKのiCloud対応
パッケージを作って ☁️AquaSKK with iCloud - みずぴー日記 に置いた。
AquaSKKのユーザ辞書をiCloudで同期するようにした。 自分の使っているMacBookとiMacで辞書を共有できるようになった。
現状
cloudkitブランチに最新の内容がpushしてある。 いくつかのコーナーケースが残っているが、実用上の問題はないと思う。
もうちょっと完成度があがったら、ベータ版のインストーラとか作ると思う。
iCloudの使い方
iCloudを使う方法は4種類ある。 それぞれの特徴については[iOS 8] CloudKit を使ってみよう (1) 概要 | Developers.IOの冒頭に簡潔にまとまっている。 Appleの文書だとiCloud設計ガイド(PDF)がよい。
この4種類のどれを使うかはかなり悩んだが、今回はCloudKtiを使うことにした。
- Key-value Storage
- 1MBまでしか保存できないので、容量が不足している。
- iCloud document storage
- ファイルベースを意識することになるので、単語登録したときの競合解決を実装するのが大変そう。
- Core Data storage
- 現時点でCore Dataを用いていないので、あまり利点がなさそう。
- CloudKit
- iCloud上に構築されたKVSとして扱えるので、辞書データをそのまま格納できそう。 また、CloudKit dashboardで登録されたデータを確認できるので、開発が楽そう。
保存領域
CloudKitにはどのユーザでもアクセスできるpublic領域と、そのユーザしかアクセスできないprivate領域がある。 public領域の容量はアプリによって決まり、private領域の容量はユーザのアカウントによって決まる。(参考: Designing for CloudKit)
AquaSKKの辞書はprivate領域に格納するようにしたので、他のアカウントから辞書データにアクセスすることはできない。また辞書データはユーザのiCloud driveに保存される。
主な構造
SKKのエンジンが直接iCloudと通信するのを避け、中間にユーザ辞書を挟む方式を採用した。
- SKKのエンジンは、これまで通りユーザ辞書に対して単語の検索・登録を行なう。
- 同期するためのクラスがユーザ辞書を読み込み、更新分をiCloudに送信する。
- 定期的にiCloudの更新を確認し、更新があった場合はユーザ辞書に単語を追加する。
単語を検索するたびにiCloudと通信する方法も検討したが、ネットワーク通信ができない環境での利用を想定して断念した。
単語の削除
基本的に、iCloudから取得した単語をローカルの辞書にマージすることで、辞書の同期を実現している。 ただし、それだけでは、辞書から単語を削除しても簡単に復活してしまう。
そこで、「ユーザ操作によって削除された単語」は別枠で管理するようにし、ここに登録された単語は同期のタイミングでユーザ辞書から消すようにした。
同期状態の表示
同期状態を通知するために通知センターを利用している。
Dropboxのようにメニューバーに出しているアイコンで通知したかったが、InputMethodのアイコンを動的に変化させることができなったため断念した。
通知センターに用いられるアイコンはシステムにキャッシュされるらしく、AppIconを更新しても反映されなかった。 バージョンやビルド番号を更新すれば反映された。
設定画面
設定画面からiCloud同期の有効・無効化を切り替えれる。 無効にした場合はこれまでと同様の動作をする。
その他の知見
- CloudKitの使い方はccabanero/ios-cloudkit-snippets · GitHubがシンプルにまとまってて、よかった。
- ただし、CloudKitからは一度に100件しかレコードを取得できないので、ページングが必要になることは書いてないので、注意が必要。
- まさしブログ: CloudKitでページング読み込みを参考にした。
- テストにはL辞書にない単語を使う必要があったので、ゆるゆりの登場人物名を使っていた。 特に、同じ読みの単語がない「歳納」と、同じ読みの単語がある「赤座」の2つが便利だった。
- AquaSKKのエンジン部はC++、CloudKitを利用する箇所はObjectvie-Cなので、Objective-C++を使うことになった。 いわゆる「文字列」に対応する型が、
const char[]
、std::string
、NSString*
の3種類登場してつらかった。