[.NET MAUI] アプリにバンドルしたファイルの読み込み
入力用リストから引き続き、リスト生成に使用する車種のデータをハードコーディングせず、アプリケーションにバンドルしたファイルから読み込むようにする。
ファイルはプロジェクトフォルダ配下の「/Resources/Raw/cartype.json」として作成しておく。.NETのJSONパーサーではトレイリングコンマ(配列の最後の要素の「,」)はダメ。
[
{ "Name": "コンパクト" },
{ "Name": "ミニバン" },
{ "Name": "セダン" },
{ "Name": "ワゴン" },
{ "Name": "SUV" },
{ "Name": "スポーツ" },
{ "Name": "商用" }
]
ここから先は対応するOSによって異なってくる部分だが、MacCatalystであればアプリ実行時のカレントディレクトリがxxx.app/
となる。ビルドした際に出力されるxxx.appはファイルシステム上、フォルダと同様の扱いが可能であり、Finderでbin/Debug/net8.0-maccatalyst/maccatalyst-x64/xxx.app
を右クリックして「パッケージの内容を表示」してみるとxxx.app/Contents/Resource/Raw/cartype.json
と辿ることができる。よってstring json = File.ReadAllText("./Contents/Resources/Raw/cartype.json");
とすればバンドルしたファイルを読み込むことが可能。
今回はiOSにも対応させたいが、iOSではビルドしたxxx.app/
配下のフォルダ構造が異なり、xxx.app/Raw/cartype.json
となる。MacCatalystとiOSのフォルダ構造の差異を吸収するため、NSBundleクラスを使おう。
NSBundle.MainBundle.ResourcePath
でResources
フォルダに対応するxxx.app/
内のフォルダパスを取得できるので、ここから辿ったRaw/cartype.json
ファイルを読み込み、CarTypeクラスのリストにパースして、あとはListViewのItemSourceに突っ込むだけ!
using Foundation;
using System.Text.Json;
...
private void MainPage_Loaded(object sender, EventArgs e)
{
string filePath = NSBundle.MainBundle.ResourcePath;
filePath = Path.Combine(filePath, "Raw");
filePath = Path.Combine(filePath, "cartype.json");
string json = File.ReadAllText(filePath);
var lst = JsonSerializer.Deserialize<List<CarType>>(json);
CarTypeList.ItemsSource = lst;
// 車種のリストを非表示にする
CarType_Unfocused(sender, e);
}
今回はMacCatalystとiOSのみなのでNSBundleで差異を吸収できたが、WindowsやAndroidにも対応させる場合はFileSystem.OpenAppPackageFileAsyncを使う必要がある。
上記ページに書いてある「ReadTextFile」メソッドをそのままパクる。
public async Task<string> ReadTextFile(string filePath)
{
using Stream fileStream = await FileSystem.Current.OpenAppPackageFileAsync(filePath);
using StreamReader reader = new StreamReader(fileStream);
return await reader.ReadToEndAsync();
}
そして、NSBundleおよびFile.ReadAllText使っていたファイル読み込みの処理を下記の通り変更する。
+ private void MainPage_Loaded(object sender, EventArgs e)
- private async void MainPage_Loaded(object sender, EventArgs e)
{
+ string json = await ReadTextFile("Raw/cartype.json");
- string filePath = NSBundle.MainBundle.ResourcePath;
- filePath = Path.Combine(filePath, "Raw");
- filePath = Path.Combine(filePath, "cartype.json");
- string json = File.ReadAllText(filePath);
条件付きコンパイル
自前で作ったReadTextFile
関数を使えば全OSに対応できるはずだけど、あえて#記号で始まる条件付きコンパイルを使用して、MAC+iOSとWindows+Androidに両対応する。
+#if IOS || MACCATALYST
using Foundation;
+#endif
...
// 車種リストの生成(本来は初めてリスト表示をした際に生成すべき)
+#if IOS || MACCATALYST
string filePath = NSBundle.MainBundle.ResourcePath;
filePath = Path.Combine(filePath, "Raw");
filePath = Path.Combine(filePath, "cartype_ver1.json");
string json = File.ReadAllText(filePath);
+#else
+ string json = await ReadTextFile("cartype_ver1.json");
+#endif
var lst = JsonSerializer.Deserialize<List<CarType>>(json);