[iOS]バックアップ機能のよくない方式とよりよい方式

はじめまして、@gachalatteと申します。

個人でiOSアプリの開発をしています。先日、[Firevault][]というパーソナルデータベースアプリをリリースしました。よろしくお願いいたします。

今回は、[Firevault][]を開発するにあたって他のアプリの調査をしてきた中で、バックアップ機能の実装方式について感じたことを共有してみようと思います。

※この記事では手動でのバックアップ機能について取り上げます。


#バックアップ機能
たとえば日記アプリや家計簿アプリなど、ユーザーのコンテンツを記録するアプリにはバックアップ機能が不可欠です。iOSの機能で全体をバックアップ・復元することはできますが、今のユーザーはそれでは満足してくれないでしょう。バックアップ機能が備わっているかどうかはアプリをダウンロードするかどうかの判断基準のひとつにもなり得ます。実際、調査したアプリのほとんどがバックアップ機能を搭載していました。

バックアップ機能では、バックアップファイルをデバイスの外部に保存することが必要条件となります。この記事ではバックアップファイルを外部に保存する方法について、ユーザーと開発者、双方の立場から考えてみたいと思います。


#よくない方式
バックアップファイルを直接クラウドに保存するという方式をよく見かけます。これはユーザーにとっても開発者にとってもよい方式ではないといえます。

まず、ユーザーにとっては選択肢がないというのがよくありません。iOSであればiCloud Driveに対応していればあまり文句はないとは思いますが、保存先は自由に選択できるに越したことはありません。ファイルの保存先が固定されているためにバックアップの世代管理ができないということもよくない点としてあげることができます。

DropboxやGoogle Driveなど、サードバーティのクラウドサービスに対応する場合は、アカウントを管理することがデメリットになります。ユーザーはアプリを信頼しなければなりませんし、開発者はアカウントの情報を適切に管理する責任があります。さらに、アカウント認証のための煩雑な手続き、サービスごとのAPI呼び出し、ネットワークエラーや容量不足でエラーになるケースなどを考慮しなければならず、開発者の負担はかなり大きなものになります。


#よりよい方式
`UIDocumentPickerViewController`を使ってバックアップファイルの保存先をユーザーに委ねます。


```swift:ViewController.swift
class ViewController: UIViewController {
	
	func exportBackupFile(backupFileURL: URL) {
 		let documentPicker = UIDocumentPickerViewController(url: backupFileURL, in: .exportToService)
		present(documentPicker, animated: true)
	}

}
```

たったこれだけです。ユーザーはどこでも好きな場所にバックアップファイルを保存することができます。

<img width="207" alt="IMG_218423023A98-1.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553312/df6c62fd-9d03-8684-fc2a-148f29b6e5a7.jpeg">


復元するファイルを選択するときも`UIDocumentPickerViewController`を使用します。

```swift:ViewController.swift
class ViewController : UIViewController {

	func importBackupFile() {
	 	let documentPicker = UIDocumentPickerViewController(documentTypes: documentTypes, in: .import)
		documentPicker.delegate = self
		present(documentPicker, animated: true)
	}

}

extension ViewController: UIDocumentPickerDelegate {
    
	func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
		// 復元処理
	}

}
```

必要なコードは本当にこれだけです。

ユーザーにとってはバックアップファイルを管理する責任が生じますが、与えられた自由に比べればたいした問題ではないでしょう。


#補足：もうひとつの方式
`UIActivityViewController`を使う方式もあります。この方式の場合バックアップファイルを保存するだけでなく、メールで送信したり、他のアプリに送信したりすることができます。ただし、この方式の場合は復元の実装が`UIDocumentPickerViewController`を使った方式より複雑になります。具体的には、アプリ独自のファイル拡張子を定義して他のアプリからそのファイルを開けるようにするなどの実装が必要になります。


#考察
なぜわざわざ手間をかけて不自由な方式を選択してしまうのか考えてみました。

* `UIDocumentPickerViewController`が存在しなかった
* `UIDocumentPickerViewController`が使いものにならなかった
* `UIDocumentPickerViewController`の存在を知らなかった

`UIDocumentPickerViewController`はiOS 8で追加されています。現在はiOS 13ですので5年前には存在していたことになります。後方互換性を考慮したとしても3年前には十分選択肢にはなり得たのではないでしょうか。

`UIDocumentPickerViewController`が使いものにならなかった可能性もあります。手元にiOS 9のデバイスがあったので試してみたところ、期待通りの動作を確認することができました。Dropboxにもちゃんと保存できます。

最後の仮説を検証するために、Googleで3年より前の日本語のページという条件をつけて`UIDocumentPickerViewController`を検索してみました。

https://www.google.com/search?q=UIDocumentPickerViewController&tbs=cdr:1,cd_max:12/19/2016&lr=lang_ja

結果は10件で、そのうち`UIDocumentPickerViewController`の機能が紹介されているものは2件しかありませんでした。

以上を踏まえて、`UIDocumentPickerViewController`の存在が知られていないということが一番の原因であるという結論にいたりました。


#まとめ
`UIDocumentPickerViewController`の存在さえ知っていればみんなハッピーになれたのに。そういう思いでこの記事を書きました。

ぜひ、`UIDocumentPickerViewController`の存在を頭の片隅に入れておいてください。


[Firevault][]では`UIDocumentPickerViewController`を使ったバックアップを実装しています。無料でダウンロードできますのでよかったら実際に動かしてみてください。

ありがとうございました。


[Firevault]:https://apps.apple.com/jp/app/id1439389822?l=ja&ls=1