LoginSignup
35
20

More than 5 years have passed since last update.

Xamarin.FormsでのFirebase利用が捗る5つのライブラリを作ってみた

Last updated at Posted at 2018-12-04

この記事は、Xamarin Advent Calendar 2018の5日目です。

Firebaseすごく便利です。データベース、ストレージ、認証などなど、バックエンドで必要な機能が簡単に使えてしまいます。Xamarinでもライブラリが用意されており、利用することが可能です。ただ、そのライブラリというのが、iOS用とAndroid用に分かれており、Xamarin.Formsで使うには、DependencyServiceを使うなどしなければならず、ちょっと面倒でした。そこで、Xamarin.Formsでそのまま使えるようなライブラリを作成しました。

作成したライブラリ

Firebaseの5つの機能のライブラリを作成しました。NuGetで公開済みなのですぐに利用できます。
使い方は、それぞれのGitHubをみてください。(詳しい機能の説明はしていないので、公式のドキュメントも参照してください)

NuGet

GitHub

Plugin.CloudFirestore

Cloud Firestoreを利用するためのライブラリです。
データベースから取得した時のデータの型は、当然iOSとAndroidでは異なるので、共通の型にマッピングする機能を備えています。また、リアルタイムでのデータの更新は、Rxが利用できるようにしています。

Plugin.FirebaseAuth

Authenticationを利用するためのライブラリです。
Plugin.CloudFirestoreやPlugin.FirebaseStorageとは、セットで使うことになります。
また、各プロバイダーへの認証の機能はないので、Xamarin.Auhtなどを使ってください。

Plugin.FirebaseStorage

Storageを利用するためのライブラリです。
進捗のモニタリングやキャンセル、一時停止にも対応しています。

Plugin.FirebaseAnalytics

アナリティクスを利用するためのライブラリです。

Plugin.FirebaseCrashlytics

Crashlyticsを利用するためのライブラリです。
昨日の記事にも書かれてあるように、通常色々やらないといけないのをこれを入れるだけで大丈夫なようにもしています。

サンプルアプリ

これらのライブラリを利用したサンプルアプリも作成しました。画像を投稿して、リストに表示されて、いいねができる、ちょっとしたSNSアプリです。
メールアドレスとGoogleでのログインができるようにしています。
リストは、リアルタイム監視をしているので、誰かが投稿やいいねをすると自動で表示が更新されます。

f-miyu/XamarinFirebaseSample

スクリーンショット 2018-12-04 21.30.15.png スクリーンショット 2018-12-04 21.32.23.png

サンプルアプリを動かすには

サンプルアプリのGitHubには、GoogleService-Info.plistgoogle-services.jsonを含めていないので、動かすには各自でFirebaseの設定を行なってください。
以下で設定の仕方を説明します。

Firebaseプロジェクトの作成

Firebaseのコンソールで、[プロジェクトの追加]をクリックします。
プロジェクトの追加画面で、プロジェクト名を入力し、アナリティクスの地域を日本に設定、同意に
チェックをして、[プロジェクトを作成]をクリックします。

スクリーンショット 2018-12-04 1.11.11.png

トップの画面の、[iOS]ボタンをクリックします。

スクリーンショット 2018-11-26 2.17.28.png

iOSのバンドルIDを入力して、[アプリの登録]をクリックします。

スクリーンショット 2018-11-26 2.17.55.png

[GoogleService-Info.plistをダウンロード]をクリックします。

スクリーンショット 2018-11-26 2.18.05.png

GoogleService-Info.plistがダウンロードできたら、XamarinFirebaseSample.iOSプロジェクトのルートに追加します。また、ビルドアクションがBundleResourceになっていることを確認してください。

スクリーンショット 2018-12-04 2.26.21.png

次にAndroidの登録を行います。[アプリを追加]をクリックして、Androidを選択します。

スクリーンショット 2018-11-26 2.19.27.png

Androidのパッケージ名を入力します。また、サンプルアプリでは、Googleログインを行なっているので、SHA-1の入力も必要です。
Xamarin.Androidで使用する、debug.keystoreのSHA-1フィンガープリントを取得するには、ここを参考にしてください。
入力したら、[次へ]をクリックします。

スクリーンショット 2018-11-26 2.20.03.png

[google-services.jsonをダウンロード]をクリックします。

スクリーンショット 2018-11-26 2.20.10.png

google-services.jsonがダウンロードできたら、XamarinFirebaseSample.Androidプロジェクトのルートに追加します。また、ビルドアクションがGoogleServicesJsonになっていることを確認してください。

スクリーンショット 2018-12-04 2.57.30.png

Cloud Firestoreの設定

[Database]セクションで、[データベースの作成]をクリックします。

スクリーンショット 2018-11-26 2.21.44.png

[有効にする]をクリックします。

スクリーンショット 2018-11-26 2.21.48.png

セキュリティルールの変更

認証しなければ、読み込みも書き込みもできないようにします。
[ルール]タブで、以下のように記述して、[公開]をクリックします。

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth.uid != null;
    }
  }
}

スクリーンショット 2018-12-04 19.50.42.png

複合インデックスの作成

サンプルアプリでは、自分の投稿を新しい順に取得しているため、複合インデックスの作成が必要です。
[インデックス]タブで、[手動でインデックスを作成]をクリックします。

スクリーンショット 2018-12-04 19.47.19.png

複合インデックスの作成画面で以下のように設定して、[インデックスの作成]をクリックします。

対象となるコレクション
items

フィールド

順番 フィールドのパス モード
1 OwnerId Ascending
2 Timestamp Descending

スクリーンショット 2018-12-02 16.06.09.png

今回は、手動で設定しましたが、まだ設定していない時に、複合インデックスが必要なクエリを行なったら、以下のような例外が発生します。そこのメッセージにあるアドレスを叩くことでも設定ができます。

スクリーンショット 2018-12-02 16.00.33.png

Authenticationの設定

[Authentication]セクションで、[ログイン方法]タブを選択します。
[メール/パスワード]を有効にして、[保存]をクリックします。

スクリーンショット 2018-12-03 23.26.34.png

[Google]を有効にし、メールアドレスを選択して、[保存]をクリックします。

スクリーンショット 2018-12-04 0.36.51.png

Storageの設定

[Storage]セクションで、[スタートガイド]をクリックします。

スクリーンショット 2018-11-30 18.01.14.png

セキュリティルールが表示されるので[OK]をクリックします。

スクリーンショット 2018-11-30 18.01.22.png

Crashlyticsの設定

[Crashlytics]セクションで、[Crashlytics を設定]をクリックします。

スクリーンショット 2018-12-04 22.24.55.png

[このアプリではCrashlyticsを初めて使用します]を選択して、[次へ]をクリックします。
この設定は、iOS、Androidのアプリごとに行う必要があります。

スクリーンショット 2018-12-02 22.13.20.png

プログラム側の設定

AppConstans

XamarinFirebaseSampleプロジェクトのHelpersフォルダのAppConstants.csを修正します。
ここには、Googleログインで使用するクライアントIDを設定します。

AppConstants.cs
public static class AppConstans
{
    public const string IosGoogleClientId = "";
    public const string IosReversedGoogleClientId = "";

    public const string AndroidGoogleClientId = "";
    public const string AndroidReversedGoogleClientId = "";
}

iOSの設定

iOSは、先ほどダウンロードしたGoogleService-Info.plistで確認できます。
IosGoogleClientIdは、CLIENT_IDキーの値を、IosReversedGoogleClientIdには、REVERSED_CLIENT_IDの値を設定します。

Androidの設定

Androidは、APIコンソールの認証情報ページで確認できます。OAuth 2.0 クライアント IDにあるタイプがAndroidであるクライアントIDをコピーして、AndroidGoogleClientIdに設定します。AndroidReversedGoogleClientIdには、その逆ドメインを設定します。

スクリーンショット 2018-12-04 16.06.40.png

iOSのURLスキームの設定

iOSプロジェクトのinfo.pistを開き、[詳細設定]画面にします。
[URLの種類を追加]をクリックして、[URL Schenes:]に先ほども利用したGoogleService-Info.plistREVERSED_CLIENT_IDの値を設定します。

スクリーンショット 2018-12-04 19.02.54.png

これで全ての設定が完了です。ビルドして動かしてみてください。

解説

サンプルアプリについて少し解説を行います。

Services

データの取得やログイン処理などのロジックは、Servicesフォルダ内のクラスで行なっています。ライブラリの使い方は、これらのクラスを見てもらえればいいです。
以下がその一覧です。

クラス 機能 利用Firebase 備考
AccountService アカウントの管理 CloudFirestore, Auth ユーザーデータの監視も行なっています
AuthService 認証 なし Xamarin.Authを利用した認証処理を行なっています
ContributionService アイテムの投稿 CloudFirestore StorageServiceを利用して画像アップロードをしています
EmailLoginService メールアドレスでのログイン なし AccountServiceを利用してログイン処理を行なっています
GoogleLoginService Googleログイン なし AccountService、AuthServiceを利用してログイン処理を行なっています
ItemListService アイテムリストの取得 CloudFirestore アイテムデータの監視を行なっています
ItemService アイテムデータの取得、更新 CloudFirestore, Analytics データの取得後、Analyticsでview_itemイベントを送信しています
SignupService サインアップ なし AccountServiceを利用してサインアップ処理を行なっています
StorageService 画像のアップロード Storage 画像をアップロードして、ダウンロードURLの取得を行なっています
UserItemListService 投稿アイテムリストの取得 CloudFirestore 複合インデックスが必要なクエリを実行しています

アイテムリスト

ホーム画面のアイテムリストは、アイテムの追加や削除を行うと自動で更新されるようになっています。また、最初に投稿アイテムを一気に取得するのではなく、下にスクロールして最後まで表示されると20件ずつ読み込むようになっています。
それらの処理は、ItemListServiceLoadAsyncで行なっています。最後のアイテムが表示された時なら、LoadAsyncを呼び出し、続きの20件を取得するようにしています。
実際にリアルタイム監視を行なっているのは以下の部分です。

ItemListService.cs
var query = _firestore.GetCollection(Item.CollectionPath)
                      .OrderBy(nameof(Item.Timestamp), true)
                      .EndAt(new long[] { _lastTimestamp });

query.ObserveAdded()
     .Where(d => _items.FirstOrDefault(i => i.Id == d.Document.Id) == null)
     .Subscribe(d => _items.Insert(d.NewIndex, d.Document.ToObject<Item>()))
     .AddTo(_disposables);

query.ObserveModified()
     .Select(d => d.Document.ToObject<Item>())
     .Subscribe(item =>
     {
         var targetItem = _items.FirstOrDefault(i => i.Id == item.Id);
         if (targetItem != null)
         {
             item.CopyTo(targetItem);
         }
     })
     .AddTo(_disposables);

query.ObserveRemoved()
     .Select(d => _items.FirstOrDefault(i => i.Id == d.Document.Id))
     .Where(item => item != null)
     .Subscribe(item => _items.Remove(item))
     .AddTo(_disposables);

ObserveAddedで追加、ObserveModifiedで変更、ObserveRemovedで削除の監視を行なっています。
アイテムデータには、追加された時間であるTimestampを持っています。_lastTimestampは、その表示されている最後のアイテムのTimestampなので、EndAtで最後より新しいアイテムデータを監視していることになります。
ちなみに、Timestamplong型で、DateTime.Now.Ticksを与えています。時間ならDateTime型でもいいのではないかと思いますが、それだとうまく動かないことがあります。なぜなら、NSDateJava.Util.Dateから、DateTimeに変換するときに微妙に誤差が出てしまい、データベース上に持っている値と異なることがあるからです。なので、誤差の出ることがないlong型を使うようにしています。

データ監視の解除

Prismでは、ページが破棄された時にはDestroyが呼ばれるので、監視の解除はこのタイミングで行えばいいです。
ただ、Androidでは、バックボタンでアプリを終了した場合は、Destroyが呼ばれないので、監視をバックグラウンドで続けたままになってしまいます。これはよろしくないので、MainActivityOnDestroyで解除処理を走らせています。(AppクラスのDestroyが解除処理です。)

MainActivity.cs
protected override void OnDestroy()
{
    base.OnDestroy();

    App.Destroy();
}

トランザクション

いいねした時のいいね数の更新は、取得している値をインクリメントしていますが、そのまま普通に更新してしまうと、同時にいいねされた時などにデータの不整合が起きる可能性があります。このような場合に対応するために、Firestoreにはトランザクションの機能があります。
ItemServiceLikeAsyncで以下のように利用しています。

ItemService.cs
var success = await _firestore.RunTransactionAsync(transaction =>
{
    var document = _firestore.GetCollection(Models.Item.CollectionPath)
                             .GetDocument(Item.Value.Id);

    var item = transaction.GetDocument(document).ToObject<Item>();

    if (item == null)
        return false;

    item.LikeCount++;
    transaction.UpdateData(document, item);

    return true;
}).ConfigureAwait(false);

Analyticsのscreen_viewイベント

Analyticsは、画面遷移が発生すると自動的にUIViewControllerActivityのクラス名でscreen_viewイベントを送るのですが、Xamarin.Formsではうまく機能しません。iOSでは、遷移は同じクラスのViewControllerが作られるので、画面の判別がつきません。Androidは、1つのActivityでViewを切り替えているだけなので、イベント自体送られません。仕方ないので、サンプルアプリでは、OnAppearing時に手動でイベントを送るようにしています。(OnNavigatedToでもいいのですが、最初に表示される画面がうまく設定されなかったためOnAppearingにしました)

ViewModelBase.cs
public virtual void OnAppearing()
{
    var typeName = GetType().Name;
    CrossFirebaseAnalytics.Current.SetCurrentScreen(typeName, typeName);
}

まとめ

Xamarin + Firebaseはいいぞ!

明日は、@ktz_alias さんです。よろしくお願いします!

35
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
20