.NET MAUI が Visual Studio 2022 の正式版にアップデートされたので、適度にツールを作ってみます。のテストです。ちょっと、手元で入用な Firebase 接続確認のアプリを作る必要があってので、.NET MAUI を使ってアプリを作っていきます。
と、結構すんなりいく筈だったのですが、意外と難関だったので、備忘録として残しておきます。
Visual Studio 2022 で .NET MAUI プロジェクトを作る
ツールの対象として iPhone アプリになるので、iOS のみを対象としています。
.NET MAUI は Xamarin.Forms とは異なり、
- コードが .NET 6 ベースになる(内部的には mono だけど、NuGet のライブラリが .net 6 で揃えられる)
- 各プラットフォームは #if で切り替える
- 各プラットフォームはひとつのプロジェクトになる
となります。

ちょっと、ややこしいのは各プラットフォーム(iOS, Android、MacCtalyst、UWP)のコードがひとまとまりになって、ビルドをするたびに4つのプラットフォームを全部ビルドします。なので、
- ビルドに少々時間が掛かる
- 別プラットフォームでビルドできないコードを含めてしまうと、エラーがうっとおしい。
という状態に陥ります。
たとえば、モバイルアプリの場合は、iOS用とAndroid用しかいらないわけで Windows(UWP)のほうビルドエラーがでると面倒なことになる、のですが、どうするのかは微妙なところです。多分、*.csproj で TargetFrameworks を絞ればいいと思うのですが。
ひとまず、ビルドして実機で動かす
実は、「逆引き大全2022」と「.NET6本」の .NET MAUI の章は Visual Studio 2022 のプレビュー版を使っています。概ね今回の正式版と変わらないのですが、プレビュー版ではiOS シミュレータの動作が不安定であったり、iPhone 実機で動作できなかったりしました。
なので、さっそく実機で動かしておきます。


画面のほうは、実機ではなくシミュレータのものですが、Visual Studio 2022 からデバッグモードで問題なく動いています。
iOS 版を作るときは、Mac とのペアリング等もろもろの設定が必要のですが、これは Xamarin.Forms のときと同じです。
.NET MAUI と Xamarin.iOS が共存できない?
.NET MAUI プロジェクトを作るときの注意点ですが、Mac に .net 6 ベースの mono(かな?)を入れてしまうらしく、従来の Xamarin.iOS との共存ができません。正確には、
- Windows 上の Visual Studio から .NET MAUI のプロジェクトを作成し、Mac に接続する
- Windows 上の Visual Studio から Xamarin.iOS のプロジェクトを作成、Mac に接続する
ことができません。mac 上の mono のバージョンが異なるようで、Xamarin.iOS pうろジェクトが動きません。
ただし、
- Windows 上の Visual Studio から .NET MAUI のプロジェクトを作成し、Mac に接続する
- Mac 上の Visual Studio から Xamarin.iOS のプロジェクトを作成し、Mac 上で動かす。
ことは可能です。プロジェクトに設定されている mono のバージョン違いなのかもしれません。
Firebase に接続するための NuGet をインストール
Xamarin.Forms のときにどうだったか忘れてしまったのですが、Xamarin から Firebase に接続するための NuGet パッケージは、Android と iOS では別々のものが提供されています。コマンド自体は HTTP 接続だろうから、共通なのでは?と思ったけど暫くハマりました。
iOS 版の NuGet パッケージは以下になります。
https://github.com/xamarin/GoogleApisForiOSComponents
- Xamarin.Firebase.iOS.Core
- Xamarin.Firebase.iOS.CloudFirestore
- Xamarin.Firebase.iOS.Auth
の3つ入れておけば大丈夫です。
ちなみに Android 版のほうは「Xamarin.Firebase.Common」のように、「iOS」がないものを使うので注意が必要です。これ、なんらかの形でまとめてくれませんかね。Windows/UWPの場合は不明です。。。
サンプルコードは
にあるので、参考にしてください。
Xamarin.Firebase.iOS.CloudFirestore パッケージを入れた後でビルドに失敗する
Xamarin.Firebase.iOS.CloudFirestore パッケージを NuGet で入れると、.nuget フォルダーにパッケージがダウンロードされます。が、これがビルド時に失敗してしまいます。
パッケージには *.h ファイルが含まれてい、なんらかの形で参照をしているのですが、Windows の PATH の制限があって、これがエラーになってしまうのです。

ひとつの解決先は
https://github.com/xamarin/GoogleApisForiOSComponents/issues/555
にある通り、環境変数 NUGET_PACKAGES を設定して「c:\Nugets」のように PATH が短くなるように工夫します。
ですが、これは根本的な解決策にはなりません。おそらく Visual Studio 2022 がビルドに使っているターミナルが cmd ベースなのが問題でしょう。試しに、Powershell を立ち上げて、独自に「dotnet build」とすると無事に長い PATH でエラーになる問題は解決されます。

Xamarin.Firebase.iOS.* パッケージが設定しようといている *.h ファイルは最初の1回だけで、あとはキャッシュが使われるようなので、NuGet パッケージの設置した後に一回だけ「dotnet build」あるいは「dotnet restore」しておくとよいです。
ちなみ、Visual Studio 2022 上でビルドエラーになる現象は、何度か発生します(不定期です)。この場合も、Powershell 上で dotnet build すると直ります。
Firebase に登録するコードを書く
- Firebase.Core.App.Configure で初期化
- Document を作成して、SetData で登録
すれば OK です。この部分は、Xamarin.iOS の頃と同じ筈なのですが、Xamarin.iOS と Firebase の組み合わせを使っている人が少なくて、探すのに苦労しました。Android のほうはそれなりにあるので、大丈夫だと思います。
private void MainPage_Loaded(object sender, EventArgs e)
{
#if IOS
Firebase.Core.App.Configure();
#endif
}
/// <summary>
/// Firebaseに接続
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnClickConnect(object sender, EventArgs e)
{
#if IOS
message.Text = "登録開始";
var store = Firebase.CloudFirestore.Firestore.SharedInstance;
var coll = store.GetCollection("contacts");
var doc = coll.CreateDocument();
var dic = new Dictionary<object, object>();
dic.Add("device_id", "0000");
dic.Add("name", "test");
var time = Firebase.CloudFirestore.Timestamp.Create(
(long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds, 0);
dic.Add("update_at", time);
doc.SetData(dic);
message.Text = "登録しました " + DateTime.Now.ToString();
#endif
}
#if で囲ってあるのは、コードビハイドの MainPage.xaml.cs で記述しているので、iOS しかビルドが通らないためです。この部分は適宜 Android/iOS 共通のクラスを作っておいて、内部で #if で分けるのがベターです。Android と iOS で名前空間が違うので、Xamarin.Forms のときのようにインターフェースを駆使するよりも、#if でビルド時に切り分けてしまったほうが楽です。
時刻は、Firebase 側で Timestamp 型を扱うため、Firebase.CloudFirestore.Timestamp に変換して使います。
GoogleService-Info.plist を配置する
Firebase からダウンロードした「GoogleService-Info.plist」をプロジェクト内に配置させます。
これが苦労して2時間ぐらいかかりました。
GoogleService-Info.plist は、プロジェクトのルート(Plaftforms/iOS の下ではない!)において、手作業で、BundleResource を記述します。

<ItemGroup>
<BundleResource Include="GoogleService-Info.plist" Condition="Exists('GoogleService-Info.plist')" />
</ItemGroup>
Visual Studio 2022 のプロパティでは、「BundleResource」を設定できません。日本語の「埋め込みリソース」は EmbeddedResource に変換されているので、多分 .NET MAUI の *.target あたりのバグじゃないかなと。
Android の場合は「GoogleService-Info.json」なので、拡張子が違ってダブらないのですが、Windows 版はどうだったかな、と。ファイル名がダブル場合は困ることになるので、今後問題になりそうです。
ただし、コードでファイル名を指定したりパラメーターを独自に設定したりする方法があります。
Cloud Firestore のルールを設定しておく
動作させた最初では、Firebase に登録ができなくて、.NET MAUI & Firebase の不具合では?と悩んでいたのですが、ルールがきつい設定になっていました。
ひとまず、動作確認をしたいときは、以下のようにルールを緩く設定しておくと便利です。
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /contacts/{id} {
allow read, write
}
}
}
実運用になると、request.auth でユーザー権限を調べて uid のチェックをしたほうがいいですよね。実際のアプリのほうはそうなっています。
実行する
アプリを実行するとこんな感じ。

Firebase のドキュメントに無事データが入っています。
