【iOS】画像と動画を含む端末内ストレージでの下書き機能の実装
はじめに
私たちはオンラインサロン・コミュニティ専用サービス「FANTS(ファンツ)」を開発しています。
機能の1つに投稿があるのですが、WebViewの投稿画面からネイティブでの投稿画面にリニューアルするリリースをしました。その一環でテキスト、画像、動画等を含む端末内ストレージへの下書き機能を実装しました。
リリースした下書き投稿機能
下書き機能の仕様
サーバー保存か端末内ストレージ保存か
アプリの性質上、新幹線移動や外でのアクティビティの合間など、インターネット回線の状況の悪い場所でも投稿されることが予想されます。
インターネットが繋がらず投稿に失敗した際も端末内ストレージに保存され、後から投稿可能な方がユーザーフレンドリーだよね、という結論に至りました。そのため、端末内ストレージに下書きを保存する仕様にしています。
保存する内容
投稿の下書きなので複数のプロパティを保存する仕様です
- テキスト
- 画像
- 動画
- 投稿予約時間
- その他いろいろ(投稿時にPUSH通知をするか等々)
下書き機能の実現方法
データ永続化ライブラリの選定
端末内でのデータの永続化にはCoreDataを採用しました。iOS17よりSwiftDataも利用できますが、私たちの開発しているアプリのサポート下限がiOS15なので採用は見送りました。
また、Realm等の標準以外のライブラリも調査しましたが、CoreDataで事足りそうだとわかったため、あえて標準以外のライブラリを採用しませんでした。SwiftDataへ将来的に移行する際もCoreDataとの互換性があるようなので、それも採用の決め手になりました。
画像、動画の下書き保存方法
3通りの実現方法が思いつきました。
1.CoreDataのBinary Data型カラムへの保存
CoreDataはBinary Dataの保存も可能なのでそこに画像や動画を保存することも可能です。しかし、メモリの大量消費でパフォーマンスへの影響が懸念されます。
2.アプリ内ディレクトリに画像、動画データをコピーしてファイルパスをCoreDataに保存
ファイルパスを保存しておくことで、「1.CoreDataのBinary Data型カラムへの保存」の懸念は回避できます。一方で、下書きが増えるたびにストレージ容量の圧迫がされる懸念があります。
3.画像、動画のassetIdentifierをCoreDataに保存
PHPickerで画像、動画を選択した際にアプリがそのアセットに対してアクセス権を持っていた場合に、assetIdentifier
が取得できます。
extension HogePickerAssistant: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
let assetIdentifiers = results.compactMap(\.assetIdentifier)
//何らかの処理
}
}
PHAsset.fetchAssets
とassetIdentifier
を利用してアプリがそのアセットに対してアクセス権を持っていた場合にアセットを取得することができます。
func fetchPHAsset(withIdentifier identifier: String) -> PHAsset? {
PHAsset.fetchAssets(withLocalIdentifiers: [identifier], options: nil).firstObject
}
今回は仕様として下書き保存が増えてもストレージを圧迫しないようにしたかったため、「3.画像、動画のassetIdentifierをCoreDataに保存」を選択しました。
CoreDataのスキーマの決定とクエリの実装
CoreDataに関する記事や本はたくさんあるのでそれらの紹介にとどめます。
レコチョクさんの記事は何度か拝見させていただきました。特にテストの書き方を参考にさせていただきました。
CoreDataがが古い技術なので新しめの本は見当たらなかったのですが、体系的に学ぶために「Swift+Core DataによるiOSアプリプログラミング」を読みました。CoreDataはGUIで操作する部分が多く、本のXcodeのバージョンが古いのでちょっと迷う場面もありましたが、全体感を掴むには役立ちました。
今後の課題
アセットへのアクセス権の付与についての課題があります。
現状では投稿画面でアプリに対してアセットへのフルアクセスを認めないと画像や動画の選択ができなくなっています。これは「3.画像、動画のassetIdentifierをCoreDataに保存」を下書き保存の実現方法として選択したため、そのようになってしまっています。
解決方法として考えられるのは以下の画像のように、SlackとXはアクセス権限が付与されているアセットのみを表示できるフォトピッカーがあります。Apple標準でこのようなフォトピッカーが用意されておらず、自前で実装する必要があります。
Slack | X |
---|---|
まとめ
CoreDataの使い方などの参考記事はかなり出てくるのですが、メディアの保存をどのように実装するかについてはそれほど見当たりませんでした。
特に「3.画像、動画のassetIdentifierをCoreDataに保存」は今回のユースケースでは必要になりますが、参考記事が見つからなかったので今回記事にまとめました。
Discussion