Xamarin.iOS でのファイル システム アクセス

Download Sampleサンプルのダウンロード

Xamarin.iOS と System.IO .NET 基本クラス ライブラリ (BCL) のクラスを使用して、iOS ファイル システムにアクセスできます。 File クラスでは、ファイルを作成し、削除し、読み込むことができます。Directory クラスでは、ディレクトリの内容を作成し、削除し、列挙できます。 サブクラスを使用 Stream することもできます。これにより、ファイル操作をより高度に制御できます (圧縮やファイル内での位置検索など)。

iOS では、アプリケーションのデータのセキュリティを維持し、悪性のアプリからユーザーを保護するために、ファイル システムでアプリケーションが実行できることに制限があります。 これらの制限は、アプリケーション サンドボックス一部であり、ファイル、基本設定、ネットワーク リソース、ハードウェアなどに対するアプリケーションのアクセスを制限する一連のルールです。アプリケーションは、ホーム ディレクトリ (インストールされている場所) 内のファイルの読み取りと書き込みに限定されます。別のアプリケーションのファイルにアクセスできません。

iOS にはファイル システム固有の機能もあります。特定のディレクトリでは、バックアップとアップグレードに関して特別な処理が必要です。アプリケーションは、ファイルを互いに共有したり 、ファイル アプリ (iOS 11 以降) や iTunes を介してファイルを共有したりすることもできます。

この記事では、iOS ファイル システムの機能と制限について説明し、Xamarin.iOS を使用していくつかの単純なファイル システム操作を実行する方法を示すサンプル アプリケーションが含まれています。

A sample of iOS executing some simple file system operations

一般的なファイル アクセス

Xamarin.iOS では、iOS でのファイル システム操作に .NET System.IO クラスを使用できます。

次のコード スニペットは、一般的なファイル操作を示しています。 これらのすべてが、この記事のサンプル アプリケーションの SampleCode.cs ファイルにあります。

ディレクトリの使用

このコードは、アプリケーションの実行可能ファイルの場所である現在のディレクトリ ("./" パラメーターで指定) 内のサブディレクトリを列挙します。 出力は、アプリケーションと共に展開されているすべてのファイルとフォルダーの一覧になります (デバッグ中はコンソール ウィンドウに表示されます)。

var directories = Directory.EnumerateDirectories("./");
foreach (var directory in directories) {
      Console.WriteLine(directory);
}

ファイルの読み取り

テキスト ファイルを読み取るために必要なコードは 1 行だけです。 次の使用例は、[アプリケーション出力] ウィンドウにテキスト ファイルの内容を表示します。

var text = File.ReadAllText("TestData/ReadMe.txt");
Console.WriteLine(text);

XML シリアル化

完全な System.Xml 名前空間の操作はこの記事の範囲外ですが、次のコード スニペットのように StreamReader を使用して、ファイル システムから XML ドキュメントを簡単に逆シリアル化できます。

using (TextReader reader = new StreamReader("./TestData/test.xml")) {
      XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
      var xml = (MyObject)serializer.Deserialize(reader);
}

詳細については、System.Xmlシリアル化のドキュメントを参照してください。 リンカーに 関する Xamarin.iOS のドキュメント を参照してください。多くの場合、シリアル化するクラスに属性を追加 [Preserve] する必要があります。

ファイルとディレクトリの作成

このサンプルでは、クラスを Environment 使用して、ファイルとディレクトリを作成できる Documents フォルダーにアクセスする方法を示します。

var documents =
 Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments); 
var filename = Path.Combine (documents, "Write.txt");
File.WriteAllText(filename, "Write this text into a file");

ディレクトリの作成も同様のプロセスです。

var documents =
 Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var directoryname = Path.Combine (documents, "NewDirectory");
Directory.CreateDirectory(directoryname);

詳細については、System.IO API リファレンスを 参照してください

JSON のシリアル化

Json.NET は、Xamarin.iOS で動作し、NuGet で使用できる高パフォーマンスの JSON フレームワークです。 Visual Studio for Mac で NuGet の追加を使用して 、NuGet パッケージをアプリケーション プロジェクトに追加します。

Adding the NuGet package to the applications project

次に、シリアル化/逆シリアル化のデータ モデルとして機能するクラスを追加します (この場合 Account.cs)。

using System;
using System.Collections.Generic;
using Foundation; // for Preserve attribute, which helps serialization with Linking enabled

namespace FileSystem
{
    [Preserve]
    public class Account
    {
        public string Email { get; set; }
        public bool Active { get; set; }
        public DateTime CreatedDate { get; set; }
        public List<string> Roles { get; set; }

        public Account() {
        }
    }
}

最後に、クラスのインスタンスを Account 作成し、それを json データにシリアル化し、ファイルに書き込みます。

// Create a new record
var account = new Account(){
    Email = "monkey@xamarin.com",
    Active = true,
    CreatedDate = new DateTime(2015, 5, 27, 0, 0, 0, DateTimeKind.Utc),
    Roles = new List<string> {"User", "Admin"}
};

// Serialize object
var json = JsonConvert.SerializeObject(account, Newtonsoft.Json.Formatting.Indented);

// Save to file
var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "account.json");
File.WriteAllText(filename, json);

.NET アプリケーションでの json データの操作の詳細については、Json.NET の ドキュメントを参照してください

特別な注意事項

Xamarin.iOS と .NET のファイル操作は似ていますが、iOS と Xamarin.iOS はいくつかの重要な点で .NET とは異なります。

実行時にプロジェクト ファイルにアクセス可能にする

既定では、プロジェクトにファイルを追加した場合、最終的なアセンブリには含まれないため、アプリケーションでは使用できません。 アセンブリにファイルを含めるには、コンテンツと呼ばれる特別なビルド アクションでマークする必要があります。

ファイルをインクルードとしてマークするには、ファイルを右クリックし、Visual Studio for Mac で [Build Action Content]\(ビルド アクション > コンテンツ\) を選択します。 ファイルプロパティ シートでビルド アクションを変更することもできます。

大文字小文字の区別

iOS ファイル システムでは大文字と小文字が区別されることを理解しておくことが重要です。 大文字と小文字の区別は、ファイル名とディレクトリ名が正確に 一致する必要があることを意味します。README.txtreadme.txt は異なるファイル名と見なされます。

これは、Windows ファイル システムに慣れている .NET 開発者にとって混乱を招く可能性があります。大文字と小文字は区別されません。ファイルFILES、ファイルはすべて同じディレクトリを参照します。

警告

iOS シミュレーターでは、大文字と小文字は区別されません。 ファイル名の大文字と小文字の区別がファイル自体とコード内の参照とで異なる場合、コードはシミュレーターで引き続き動作する可能性がありますが、実際のデバイスでは失敗します。 これは、iOS の開発時に、実際のデバイスを早期に頻繁に展開してテストすることが重要な理由の 1 つです。

パス区切り記号

iOS では、パス区切り記号としてスラッシュ '/' が使用されます (これは、バックスラッシュ '\' を使用する Windows とは異なります)。

この混乱を招く違いがあるため、特定のパス区切り記号をハードコーディングするのではなく、現在のプラットフォームに合わせて調整するメソッドを使用 System.IO.Path.Combine することをお勧めします。 これは、コードを他のプラットフォームに移植できるようにする簡単な手順です。

アプリケーション サンドボックス

セキュリティ上の理由から、ファイル システム (およびネットワークやハードウェア機能などのその他のリソース) へのアプリケーションのアクセスは制限されています。 この制限は、アプリケーション サンドボックスと 呼ばれます。 ファイル システムに関しては、アプリケーションはホーム ディレクトリ内のファイルとディレクトリの作成と削除に限定されます。

ホーム ディレクトリは、アプリケーションとそのすべてのデータが格納されているファイル システム内の一意の場所です。 アプリケーションのホーム ディレクトリの場所を選択 (または変更) することはできません。ただし、iOS と Xamarin.iOS には、内部のファイルとディレクトリを管理するためのプロパティとメソッドが用意されています。

アプリケーション バンドル

アプリケーション バンドルは、アプリケーションを含むフォルダーです。 ディレクトリ名に .app サフィックスを追加することで、他のフォルダーと区別されます。 アプリケーション バンドルには、実行可能ファイルと、プロジェクトに必要なすべてのコンテンツ (ファイル、イメージなど) が含まれています。

Mac OS でアプリケーション バンドルを参照すると、他のディレクトリに表示されるアイコンとは異なるアイコンが表示されます ( また、.app サフィックスは非表示になっています)。ただし、オペレーティング システムの表示が異なるのは通常のディレクトリにすぎません。

サンプル コードのアプリケーション バンドルを表示するには、Visual Studio for Mac でプロジェクトを右クリックし、[Finder で表示] を選択します 次に、 アプリケーション アイコンが表示されている bin/ ディレクトリに移動します (下のスクリーンショットに似ています)。

Navigate through the bin directory to find an application icon similar to this screenshot

このアイコンを右クリックし、[パッケージ コンテンツの表示] を選択して、アプリケーション バンドル ディレクトリの内容を参照します。 次に示すように、内容は通常のディレクトリの内容と同じように表示されます。

The contents of the app bundle

アプリケーション バンドルは、テスト中にシミュレーターまたはデバイスにインストールされるものであり、最終的には App Store に含めるために Apple に送信されます。

アプリケーション ディレクトリ

アプリケーションがデバイスにインストールされると、オペレーティング システムによってアプリケーションのホーム ディレクトリが作成され、使用できるアプリケーション ルート ディレクトリ内に多数のディレクトリが作成されます。 iOS 8 以降、ユーザーがアクセス可能なディレクトリは アプリケーション ルート内に存在 しないため、ユーザー ディレクトリからアプリケーション バンドルのパスを派生させる(またはその逆) することはできません。

これらのディレクトリ、パスを決定する方法、およびその目的を次に示します。

 

ディレクトリ 説明
[ApplicationName].app/ iOS 7 以前では、これは ApplicationBundle アプリケーションの実行可能ファイルが格納されているディレクトリです。 アプリで作成するディレクトリ構造は、このディレクトリに存在します (たとえば、Visual Studio for Mac プロジェクトでリソースとしてマークしたイメージやその他のファイルの種類)。

アプリケーション バンドル内のコンテンツ ファイルにアクセスする必要がある場合は、このディレクトリへのパスをプロパティで NSBundle.MainBundle.BundlePath 使用できます。
ドキュメント/ このディレクトリを使用して、ユーザー ドキュメントとアプリケーション データ ファイルを格納します。

このディレクトリの内容は、iTunes ファイル共有を通じてユーザーが使用できるようになります (既定では無効になっています)。 UIFileSharingEnabled Info.plist ファイルにブール型キーを追加して、ユーザーがこれらのファイルにアクセスできるようにします。

アプリケーションがファイル共有をすぐに有効にしない場合でも、このディレクトリにユーザーから非表示にする必要があるファイル (共有する予定がない限り、データベース ファイルなど) を配置しないようにする必要があります。 機密ファイルが再メイン非表示になっている限り、ファイル共有が将来のバージョンで有効になっている場合、これらのファイルは公開されません (また、iTunes によって移動、変更、削除される可能性があります)。

このメソッドを Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments) 使用して、アプリケーションの Documents ディレクトリへのパスを取得できます。

このディレクトリの内容は iTunes によってバックアップされます。
ライブラリ/ ライブラリ ディレクトリは、データベースやその他のアプリケーションによって生成されたファイルなど、ユーザーが直接作成しないファイルを格納するのに適した場所です。 このディレクトリの内容は、iTunes 経由でユーザーに公開されることはありません。

ライブラリには独自のサブディレクトリを作成できます。ただし、システムによって作成されたディレクトリには、ユーザー設定やキャッシュなど、注意が必要なディレクトリが既にあります。

このディレクトリの内容 (Caches サブディレクトリを除く) は iTunes によってバックアップされます。 ライブラリで作成したカスタム ディレクトリがバックアップされます。
ライブラリ/環境設定/ アプリケーション固有の基本設定ファイルは、このディレクトリに格納されます。 これらのファイルを直接作成しないでください。 代わりに、クラスを使用します NSUserDefaults

このディレクトリの内容は iTunes によってバックアップされます。
ライブラリ/キャッシュ/ Caches ディレクトリは、アプリケーションの実行に役立ちますが、簡単に再作成できるデータ ファイルを格納するのに適した場所です。 アプリケーションでは、必要に応じてこれらのファイルを作成して削除し、必要に応じてこれらのファイルを再作成できるようにする必要があります。 iOS 5はこれらのファイルを削除することもできますが(ストレージが少ない状況では)、アプリケーションの実行中は削除されません。

このディレクトリの内容は iTunes によってバックアップされません。つまり、ユーザーがデバイスを復元した場合は存在せず、更新されたバージョンのアプリケーションがインストールされた後に存在しない可能性があります。

たとえば、アプリケーションがネットワークに接続できない場合は、Caches ディレクトリを使用してデータまたはファイルを格納し、優れたオフライン エクスペリエンスを提供できます。 アプリケーションは、ネットワーク応答の待機中にこのデータをすばやく保存および取得できますが、バックアップする必要はなく、復元またはバージョンの更新後に簡単に回復または再作成できます。
Tmp/ アプリケーションでは、このディレクトリに短期間だけ必要な一時ファイルを格納できます。 領域を節約するには、不要になったファイルを削除する必要があります。 オペレーティング システムは、アプリケーションが実行されていないときに、このディレクトリからファイルを削除することもできます。

このディレクトリの内容は、iTunes によってバックアップされません。

たとえば、tmp ディレクトリを使用して、ユーザーに表示するためにダウンロードされた一時ファイル (Twitter アバターや電子メールの添付ファイルなど) を保存できますが、一度表示すると削除される可能性があります (今後必要な場合は再ダウンロードされます)。

次のスクリーンショットは、Finder ウィンドウのディレクトリ構造を示しています。

This screenshot shows the directory structure in a Finder window

プログラムによる他のディレクトリへのアクセス

以前のディレクトリとファイルの例では、ディレクトリに Documents アクセスしました。 別のディレクトリに書き込むには、次に示すように ".." 構文を使用してパスを作成する必要があります。

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var library = Path.Combine (documents, "..", "Library");
var filename = Path.Combine (library, "WriteToLibrary.txt");
File.WriteAllText(filename, "Write this text into a file in Library");

ディレクトリの作成は次のようになります。

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var library = Path.Combine (documents, "..", "Library");
var directoryname = Path.Combine (library, "NewLibraryDirectory");
Directory.CreateDirectory(directoryname);

ディレクトリへのCachestmpパスは、次のように構築できます。

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var cache = Path.Combine (documents, "..", "Library", "Caches");
var tmp = Path.Combine (documents, "..", "tmp");

ファイル アプリとの共有

iOS 11 では、ファイル アプリが導入されました。これは、ユーザーが iCloud で自分のファイルを表示および操作できる iOS 用のファイル ブラウザーであり、それをサポートする任意のアプリケーションによって保存されます。 ユーザーがアプリ内のファイルに直接アクセスできるようにするには、Info.plist ファイルに新しいブールキーを作成し、次のように設定trueします。LSSupportsOpeningDocumentsInPlace

Set LSSupportsOpeningDocumentsInPlace in Info.plist

これで、アプリの Documents ディレクトリが Files アプリで参照できるようになります。 [ファイル] アプリで 、[個人用 i] に移動すると電話共有ファイルを含む各アプリが表示されます。 次のスクリーンショットは、FileSystem サンプル アプリ外観を示しています。

iOS 11 Files appBrowse my iPhone filesSample app files

iTunes を使用してユーザーとファイルを共有する

ユーザーは、次に示すように、ソース ビューで iTunes 共有 (UIFileSharingEnabled) エントリをサポートするアプリケーションをInfo.plist編集して作成することで、アプリケーションの Documents ディレクトリ内のファイルにアクセスできます。

Adding the Application supports iTunes sharing property

デバイスが接続されていて、ユーザーがタブを選択すると、これらのファイルにiTunesで Apps アクセスできます。たとえば、次のスクリーンショットは、iTunes 経由で共有されている選択したアプリ内のファイルを示しています。

This screenshot shows the files in selected app shared via iTunes

ユーザーは、iTunes 経由でのみこのディレクトリ内の最上位項目にアクセスできます。 サブディレクトリの内容は表示できません (ただし、コンピューターにコピーしたり、削除したりすることはできます)。 たとえば、GoodReader を使用すると、ユーザーが iOS デバイスで読み取ることができるように、PDF ファイルとPUBPUB ファイルをアプリケーションと共有できます。

ドキュメント フォルダーの内容を変更するユーザーは、注意しないと問題が発生する可能性があります。 アプリケーションでは、これを考慮し、ドキュメント フォルダーの破壊的な更新に対する回復性を備える必要があります。

この記事のサンプル コードでは、ファイルとフォルダーの両方をドキュメント フォルダー (SampleCode.cs) に作成し、Info.plist ファイルでのファイル共有を有効にします。 このスクリーンショットは、iTunes での表示方法を示しています。

This screenshot shows how the files appear in iTunes

アプリケーションおよび作成する カスタム ドキュメントの種類のアイコンを設定する方法については、イメージ の操作に関する記事を参照してください。

キーが UIFileSharingEnabled false の場合、または存在しない場合、ファイル共有は既定で無効になり、ユーザーは Documents ディレクトリと対話できなくなります。

バックアップと復元

デバイスが iTunes によってバックアップされると、アプリケーションのホーム ディレクトリに作成されたすべてのディレクトリが、次のディレクトリを除いて保存されます。

  • [ApplicationName].app – このディレクトリには書き込まないでください。このディレクトリは署名されているため、インストール後メイン変更しないでください。 コードからアクセスするリソースが含まれている場合がありますが、アプリを再ダウンロードして復元されるため、バックアップは必要ありません。
  • ライブラリ/キャッシュ – キャッシュ ディレクトリは、バックアップする必要のない作業ファイルを対象としています。
  • tmp – このディレクトリは、不要になったときに作成および削除される一時ファイル、または領域が必要なときに iOS によって削除されるファイルに使用されます。

大量のデータのバックアップには時間がかかる場合があります。 特定のドキュメントまたはデータをバックアップする必要がある場合は、アプリケーションで [ドキュメント] フォルダーと [ライブラリ] フォルダーを使用する必要があります。 ネットワークから簡単に取得できる一時的なデータまたはファイルの場合は、Caches または tmp ディレクトリを使用します。

Note

デバイスのディスク領域が非常に少ない場合、iOS はファイルシステムを "クリーン" します。 このプロセスでは、現在実行されていないアプリケーションのライブラリ/キャッシュと tmp フォルダーからすべてのファイルが削除されます。

iOS 5 iCloud バックアップの制限への準拠

Note

このポリシーは最初に iOS 5 で導入されましたが (これはずっと前のようです)、ガイダンスは現在もアプリに関連しています。

Apple は iOS 5 で iCloud Backup 機能を導入しました。 iCloud Backup を有効にすると、アプリケーションのホーム ディレクトリ内のすべてのファイル (通常はバックアップされていないディレクトリ (アプリ バンドル、アプリ バンドルCachestmpなど) を除く) が iCloud サーバーにバックアップされます。 この機能は、デバイスが紛失、盗難、または破損した場合に備えて、完全なバックアップをユーザーに提供します。

iCloud は各ユーザーに 5 Gb の空き領域のみを提供し、帯域幅を不必要に使用しないようにするため、Apple はアプリケーションが重要なユーザー生成データのみをバックアップすることを想定しています。 iOS データ ストレージ ガイドラインに準拠するには、次の項目に従ってバックアップされるデータの量を制限する必要があります。

  • ユーザーが生成したデータ、または再作成できないデータのみを Documents ディレクトリ (バックアップ) に格納します。
  • 簡単に再作成または再ダウンロードLibrary/Cachestmp できるその他のデータ (バックアップされず、'クリーン' である可能性がある) を格納します。
  • ファイルまたはtmpフォルダーに適Library/Cachesしている可能性があるが、"クリーン" にしたくない場合は、他の場所 (たとえばLibrary/YourData) に保存し、"バックアップしない" 属性を適用して、ファイルが iCloud バックアップの帯域幅と記憶域を使い切らないようにします。 このデータは引き続きデバイスの領域を使い切るので、可能な場合は慎重に管理して削除する必要があります。

'バックアップしない' 属性は、クラスを NSFileManager 使用して設定されます。 クラスが using Foundation 次のように呼び出 SetSkipBackupAttribute されていることを確認します。

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "LocalOnly.txt");
File.WriteAllText(filename, "This file will never get backed-up. It would need to be re-created after a restore or re-install");
NSFileManager.SetSkipBackupAttribute (filename, true); // backup will be skipped for this file

true保存SetSkipBackupAttributeされているディレクトリ (ディレクトリも含Documentsむ) に関係なく、ファイルはバックアップされません。 メソッドを使用して属性のクエリを GetSkipBackupAttribute 実行できます。次のように、メソッドを SetSkipBackupAttribute 呼び出して属性を falseリセットできます。

NSFileManager.SetSkipBackupAttribute (filename, false); // file will be backed-up

iOS アプリとアプリ拡張機能の間でデータを共有する

アプリ拡張機能はホスト アプリケーションの一部 (含まれているアプリではなく) の一部として実行されるため、データの共有は自動的に含まれないため、追加の作業が必要です。 アプリ グループは、さまざまなアプリがデータを共有できるようにするために iOS が使用するメカニズムです。 アプリケーションが適切な権利とプロビジョニングで適切に構成されている場合は、通常の iOS サンドボックスの外部にある共有ディレクトリにアクセスできます。

アプリ グループを構成する

共有場所は、アプリ グループ使用して構成されます。これは、iOS デベロッパー センターの [証明書]、[識別子]、[プロファイル] セクションで構成されます。 この値は、各プロジェクト の Entitlements.plist でも参照する必要があります。

アプリ グループの作成と構成については、アプリ グループ機能ガイドを参照してください。

Files

iOS アプリと拡張機能は、共通のファイル パスを使用してファイルを共有することもできます (適切なエンタイトルメントとプロビジョニングで適切に構成されている場合)。

var FileManager = new NSFileManager ();
var appGroupContainer =FileManager.GetContainerUrl ("group.com.xamarin.WatchSettings");
var appGroupContainerPath = appGroupContainer.Path

Console.WriteLine ("Group Path: " + appGroupContainerPath);

// use the path to create and update files
...

重要

返されるグループ パスが null、エンタイトルメントとプロビジョニング プロファイルの構成をチェックし、それらが正しいことを確認します。

アプリケーションのバージョンの更新

新しいバージョンのアプリケーションがダウンロードされると、iOS によって新しいホーム ディレクトリが作成され、新しいアプリケーション バンドルが格納されます。iOS は、次のフォルダーを以前のバージョンのアプリケーション バンドルから新しいホーム ディレクトリに移動します。

  • ドキュメント
  • Library

他のディレクトリもコピーされ、新しいホーム ディレクトリの下に配置される場合がありますが、コピーされる保証はないため、アプリケーションはこのシステム動作に依存しないでください。

まとめ

この記事では、Xamarin.iOS でのファイル システム操作が他の .NET アプリケーションと似ていることを示しました。 また、アプリケーション サンドボックスを導入し、それが引き起こすセキュリティへの影響を調べました。 次に、アプリケーション バンドルの概念について説明しました。 最後に、アプリケーションで使用できる特殊なディレクトリを列挙し、アプリケーションのアップグレードとバックアップ中の役割について説明しました。