RabbitBoxをドキュメントベースAppにしたい話 その2
前回に引き続きドキュメントベースアプリ対応について書いていこうと思います。
apps.apple.comUIDocument クラス
前回の記事では比較的低レベルよりの仕様について記載しました。この記事を読んでもらうとわかりますが、ドキュメントの読み書き、変更検知など自前で実装すると結構大変です。
UIDocumentクラスはアプリデータを管理するクラス、いわゆるMVCのモデルクラスを実装するためのベースクラスです。このクラスをベースにリファレンス通り実装していくと簡単にドキュメントベースアプリ完成です。おめでとうございます🎉
UIDocument | Apple Developer Documentation
終
制作・著作
━━━━━
ⓊⓈⒼ
...となると書くことなくなるのでもう少し詳しく書いていきます。
UIDocumentの動き
UIDocumentオブジェクトはアプリ内で下図のような役割を行います。
当然といえば当然な構造で、UIDocumentクラスがファイルアクセスする際の煩雑な処理を全て隠蔽してくれます。
それとAppleデバイスでの開発経験がなかった人間の目線になりますが、次のような特徴があります。
- ファイルの Read / Write は非同期
- 基本的にファイルの読み書きは非同期で行われます
- ファイルの変更イベントを自動処理
- 例えば、開いているファイルがリネームされたり、内容が書き換えられるとUIDocumentオブジェクトへ自動反映されます
- 上記2つに関わりますが、デフォルトで排他制御を行ってくれます
- リファレンスでは Coordinated Reading / Writing と記載されています
- ファイルへ読み書きするプロシージャの実行タイミングをプロセス間レベルで調整してくれます(結果的に非同期ファイルアクセスとなる)
ドキュメントクラスの実装
UIDocumentのサブクラスを実装するためには、最低限次の2つのメソッドを実装します。
- contents(forType:)
- ドキュメントオブジェクト内のデータをファイル書き込み用にシリアライズする処理を実装します
- func load(fromContents contents: Any, ofType typeName: String?) throws
- ファイルの内容を読み込みドキュメントオブジェクト内にロードする処理を実装します
この2つの処理を実装するだけで基本的なことはUIDocumentクラスに任せて非同期かつ安全なドキュメントクラスが実装できます。
おわりに
UIDocumentクラスは前回記事のNSFileCoordinatorとNSFilePresenterを利用した実装に比べ非常に簡単な実装でドキュメント機能を実装できます。 おそらくほとんどのアプリはこのクラスの実装で十分でNSFileCoordinatorを直接使用する必要はないと思います。
ただ、RabbitBoxではcontents()とload()でどうしても回避できない次のような問題があったため、結果的にはUIDocumentクラスは利用せず、NSFileCoordinatorとNSFilePresenterによる実装を行なっています。
- ドキュメントパッケージ読み書き時に全データをメモリ上へ乗せてしまう
RabbitBoxでは数GiBレベルのドキュメントパッケージを想定しているため、デバイスのメモリを使い切ってクラッシュしてしまいます。どうしてもこの問題が回避できなかったため、本クラスを利用せず低レベルの実装を行なっています。 この件に関しては、また時間があれば書こうかと思います。