ファイルシステムの操作は、あらゆるモバイルアプリケーションの基盤です。各プラットフォームは独自のアクセスモデルを提供しています。iOSのSandboxはアプリケーションを個別のコンテナに分離し、AndroidのScoped Storageは共有ストレージへの直接アクセスを制限します。Google Developer Documentation (2026)によると、Android 10でのScoped Storageの導入により、データストレージアーキテクチャの完全な見直しが必要になりました。このガイドでは、両プラットフォームのFileManager、MediaStore API、Storage Access Framework、DocumentProviderについて説明します。
重要ポイント
モバイルアプリケーションにおけるファイルシステムとは、iOSおよびAndroidデバイスでのデータの保存とアクセスを定義するAPI、セキュリティルール、制約のセットです。デスクトップOSとは異なり、モバイルプラットフォームは各アプリケーションを分離し、他のプログラムによる不正なデータ読み取りからユーザーデータを保護します。
iOSはSandboxモデルを採用しており、各アプリは厳しく制限された権限を持つ独自のコンテナ内に存在します。バージョン10以前のAndroidは外部ストレージへの完全なアクセスを提供していましたが、Scoped Storageの導入により、アプローチはiOSに近づきました。主な違いは、iOSがファイルシステムを完全に分離するのに対し、Androidはプライベートディレクトリ、パブリックなMediaStore、SAFを介した一時的なアクセスという複数のアクセスレベルを提供することです。
モバイルアプリケーションは3種類のデータストレージを使用します。プライベートストレージ — 内部ファイルとキャッシュのためのアプリのみがアクセス可能なディレクトリ。共有ストレージ — MediaStore(Android)またはFiles App(iOS)を介したメディアファイル。クラウドストレージ — デバイス間の同期のためのiCloud DriveとGoogle Drive。各タイプには、サイズ、ファイルのライフタイム、アクセス条件に関する独自の制限があります。
| ストレージタイプ | iOS | Android |
|---|---|---|
| プライベート | Documents, Library, Caches | getFilesDir(), getCacheDir() |
| 共有メディア | PHPhotoLibrary(ピッカー経由) | MediaStore API (ContentResolver) |
| 共有ドキュメント | UIDocumentPicker経由のFiles App | Storage Access Framework (SAF) |
| クラウド | iCloud Drive (UIDocument) | Google Drive API |
| キャッシュ | Cachesディレクトリ(システムが削除可能) | getCacheDir(), getExternalCacheDir() |
Sandboxは、各アプリケーションを分離するiOSのセキュリティアーキテクチャです。アプリは自身のサンドボックス内でのみ読み書きできます。連絡先、写真、他のアプリのファイルにアクセスするには、UIImagePickerControllerやUIDocumentPickerViewControllerなどのシステムピッカーを使用する必要があります。Files Appへのアクセスは、Info.plistのUIFileSharingEnabledフラグで設定します。iOSでのモバイル開発におけるファイルシステムを理解するには、ディレクトリ構造を把握し、データの種類ごとに適切な場所を選択する必要があります。
iOSのサンドボックスは、いくつかの標準ディレクトリで構成されています。Documents — ユーザーファイル用で、iCloud Backupに含まれます。Caches — ストレージが不足したときにシステムが削除する可能性のある一時データ用。Temporary — 現在のセッションのファイル用で、再起動時にクリアされます。Application Support — ユーザーから隠されたアプリの内部データ用。誤ったディレクトリを選択すると問題が発生します。Documentsにキャッシュを保存するとiCloudの容量を無駄に消費し、Appleのファイルシステムガイドラインに違反します。
import Foundation
let fileManager = FileManager.default
guard let documentsURL = fileManager.urls(
for: .documentDirectory,
in: .userDomainMask
).first else { return }
let fileURL = documentsURL.appendingPathComponent("notes.txt")
let text = "Содержимое файла"
// アトミック書き込みと暗号化
try text.write(
to: fileURL,
atomically: true,
encoding: .utf8
)
FileManagerクラスは、iOSでファイルを管理するための完全なメソッドセットを提供します。FileManager.defaultはスレッドセーフなシングルトンで、ほとんどの操作に適しています。fileExists(atPath:)、createDirectory(at:withIntermediateDirectories:attributes:)、copyItem(at:to:)、removeItem(at:)などのメソッドが基本的なシナリオをカバーします。1MBを超えるファイル操作は、DispatchQueue.global()を介してバックグラウンドスレッドで実行する必要があります。大量のデータをストリーミングする場合は、ファイル全体をメモリにロードする代わりにFileHandleを使用します。
func readDocumentsFile(named fileName: String) -> String? {
guard let docsURL = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
).first else { return nil }
let fileURL = docsURL.appendingPathComponent(fileName)
return try? String(contentsOf: fileURL)
}
Android 10のリリースに伴い、GoogleはScoped Storage — ファイルシステムへの制限付きアクセスモデルを導入しました。アプリはプライベートディレクトリ内でのみ自由に読み書きできます。メディアファイル(写真、動画、音声)には、ContentResolverを介してMediaStore APIを使用します。任意のドキュメントには、Intent ACTION_OPEN_DOCUMENTを介してStorage Access Frameworkを使用します。Android 11+では、外部ストレージのルートへの直接アクセスは完全に禁止されており、すべての開発者は新しいAPIを使用する必要があります。
MediaStoreは、デバイス上のメディアファイルにアクセスするためのシステムContentProviderです。ContentResolverを介して、アプリは直接パスではなくファイルのUriを要求します。MediaStore.Files — すべてのファイルタイプ用、Images — 画像用、Video — 動画用、Audio — 音声録音用。共有ディレクトリへの書き込みは、DISPLAY_NAME、MIME_TYPE、RELATIVE_PATHを指定してinsert()を介して行われます。挿入後、アプリはUriを受け取り、それを介してバイトが書き込まれます。MIMEタイプは重要な役割を果たします — 誤ったタイプはファイルを開くときにエラーを引き起こします。
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "report.pdf")
put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
put(MediaStore.MediaColumns.RELATIVE_PATH, "Documents/Reports")
}
val uri = contentResolver.insert(
MediaStore.Files.getContentUri("external"),
contentValues
)
uri?.let {
contentResolver.openOutputStream(it)?.use { stream ->
stream.write(pdfBytes)
}
}
SAFは、実行時権限なしでファイルを選択および作成するための統一インターフェースを提供します。Intent ACTION_OPEN_DOCUMENTは、Androidのシステムファイルマネージャーを開きます。選択後、アプリはFLAG_GRANT_READ_URI_PERMISSIONを介した一時的なアクセス権を持つcontent:// Uriを受け取ります。ACTION_CREATE_DOCUMENTを使用すると、ユーザーが選択した任意の外部ストレージの場所にファイルを保存できます。SAFはAndroid 5+で動作し、DocumentsProviderを介して接続されたクラウドプロバイダーのファイルへのアクセスを提供します。
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(
"application/pdf",
"text/plain"
))
}
startActivityForResult(intent, REQUEST_CODE)
両方のプラットフォームは、ユーザーがファイルを選択するための組み込みメカニズムを提供しています。モバイルアプリケーションにおけるドキュメントは、永続的な権限なしでファイルへの一時的なアクセスを許可するシステムピッカーを介して渡されます。iOSではUIDocumentPickerViewController、AndroidではACTION_OPEN_DOCUMENTです。モバイルアプリケーションにおけるドキュメントは、ローカルストレージとクラウドサービスの両方から選択できます。ユーザーが明示的にファイルを指定すると、アプリは限られた有効期間のUriまたはURLを受け取ります。
UIDocumentPickerViewControllerはFiles Appを開き、1つまたは複数のドキュメントを選択できます。モード:インポート(サンドボックスにコピー)とオープン(セキュリティスコープ付きURLを介したアクセス)。ファイルのフィルタリングには、UTTypeの配列が渡されます — 例えば.pdfや.plainTextなど。URLを受け取った後、アプリは読み取り前にstartAccessingSecurityScopedResource()を、終了後にstopAccessingSecurityScopedResource()を呼び出す必要があります。stopAccessingを呼び出さないと、システムリソースがリークします。iOSのモバイルアプリケーションにおけるドキュメントは、ファイル操作終了後に一時的な権限を必ず解放する必要があります。
let picker = UIDocumentPickerViewController(
forOpeningContentTypes: [.pdf, .plainText]
)
picker.allowsMultipleSelection = true
picker.delegate = self
present(picker, animated: true)
// デリゲートでアクセスを解放
func documentPicker(
_ controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]
) {
guard let url = urls.first else { return }
url.startAccessingSecurityScopedResource()
defer { url.stopAccessingSecurityScopedResource() }
}
FileProviderは、アプリ間でファイルを安全に共有するためのContentProviderのサブクラスです。指定されたXMLディレクトリのファイルに基づいて、一時的なcontent:// Uriを生成します。他のアプリはFLAG_GRANT_READ_URI_PERMISSION付きのIntentを介してアクセスを取得します。DocumentProviderはFileProviderとは異なり、SAFにファイルを公開し、他のアプリがファイルシステムの一部としてアプリのコンテンツを参照できるようにします。DocumentsProviderを実装するには、queryRoots()、queryChildDocuments()、openDocument()をオーバーライドし、AndroidManifest.xmlに登録する必要があります。
クラウド同期により、ユーザーはすべてのデバイスでドキュメントにアクセスできます。モバイルアプリケーションにおけるファイルシステムはクラウドレイヤーによって拡張されます。iOSのUIDocumentは変更を自動的に追跡し、iCloudを介して同期します。Androidでは、同様の機能がGoogle Drive APIまたはクラウドルートを持つDocumentsProviderを介して構築されます。信頼性の高いデバイス間同期を構築するには、モバイル開発におけるファイルシステムの理解が不可欠です。
UIDocumentは、iCloudドキュメントを扱うための抽象クラスです。変更を自動的に保存し、データを読み取り、デリゲートに更新を通知します。書き込み競合が発生した場合、NSFileVersionが利用可能なバージョンのリストを提供します。開発者は最新バージョンを選択するか、ユーザーに競合解決オプションを表示できます。iCloud Driveを使用するには、プロジェクトのCapabilitiesでUbiquity Containerを設定する必要があります。NSFileCoordinatorとNSFilePresenterは、複数のスレッドやデバイスからの同時アクセス時のデータ競合を防ぎます。
iOSは自動的にDocumentsディレクトリをiCloud Backupに含めます。AndroidはAuto Backup for Appsで動作します — システムはgetFilesDir()、SharedPreferences、SQLiteデータベースのデータをGoogle Driveに保存します。キャッシュと外部ファイルはバックアップに含まれません。両方のプラットフォームで除外設定が可能です。iOSではNSURLIsExcludedFromBackupKey、AndroidではXMLバックアップルールの設定です。個人データを含むファイルの暗号化は必須です。iOSではNSDataWritingFileProtectionComplete、Androidではsecurity-cryptoライブラリのEncryptedFileを使用します。
| パラメータ | iOS | Android |
|---|---|---|
| デフォルトのバックアップ | DocumentsとLibrary | getFilesDir(), SharedPreferences, DB |
| ファイル除外 | isExcludedFromBackupKey | XMLバックアップルール (fullBackupContent) |
| 暗号化 | NSDataWritingFileProtectionComplete | EncryptedFile (security-crypto) |
| クラウド同期 | UIDocument + iCloud | Google Drive API + SAF |
| 自動復元 | インストール後のiCloud Restore | 再インストール時のAuto Backup |
よくある質問
Sandboxは、iOS上の各アプリのための分離環境です。UIDocumentPickerViewControllerなどのシステムピッカーを使用せずに、他のアプリのファイルにアクセスすることはできません。
Scoped Storageは、Android 10+のファイルシステムへの制限付きアクセスモデルです。アプリは自身のファイルのみを直接読み取り、メディアにはMediaStore API、ドキュメントにはStorage Access Frameworkを使用します。
UIDocumentPickerViewController — Files AppまたはiCloud Driveからドキュメントを選択するためのシステムピッカーを使用します。選択後、一時的なアクセス権を持つセキュリティスコープ付きURLを取得します。
メディアファイルの場合は、MIMEタイプを指定してContentResolverを介してMediaStore APIを使用します。任意のドキュメントの場合は、Intent ACTION_OPEN_DOCUMENTとともにStorage Access Frameworkを使用します。
FileProviderは、FLAG_GRANT_READ_URI_PERMISSION付きの一時的なcontent:// Uriを介してアプリ間でファイルを安全に共有するためのContentProviderのサブクラスです。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。