.NET MAUI から Firebase を利用する

.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の場合は不明です。。。

サンプルコードは

https://github.com/xamarin/GoogleApisForiOSComponents/tree/main/samples/Firebase/CloudFirestore/CloudFirestoreSample

にあるので、参考にしてください。

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 のドキュメントに無事データが入っています。

カテゴリー: 開発 パーマリンク