📘

[@angular/fire] Firebaseを9系にアップデートする

2022/04/12に公開

Firebase9系がリリースされ、tree-shakingに対応しました。

参考:

https://qiita.com/qrusadorz/items/166aa1948dca8bd046a0

それに伴って、tree-shakingできるように記法の破壊的変更がありました。網羅してご紹介するのは難しいですが、 Firebase Authenticationの利用範囲でどのようにアップデートすればいいかをご紹介します。

何も考えないアップデート

ng update @angular/fire firebase

一応はこれでアップデートできます。ただ、どのように変わったかを確認すれば、import文の呼び出し元が @angular/fire/** から @angular/fire/compat に変わってることが確認できるかと思います。 「compat」は「互換性」という意味ですが、後方互換のための呼び出し元を用意してるだけなので、これだと パッケージのバージョンはあがりますが、tree-shakingに対応できません。 ついでにいうと、compatがいつまでも用意されるわけではないので、延命処理だと思ってください。

ちゃんとアップデートする

tree-shakingに対応するためには記法を変更する必要があります。

Moduleへのインストール

+ import { getApp, provideFirebaseApp, initializeApp } from '@angular/fire/app';
+ import {
+   getAuth,
+   provideAuth,
+ } from '@angular/fire/auth';

  @NgModule({
    declarations: [AppComponent],
    imports: [
      BrowserModule,
      IonicModule.forRoot(),
      AppRoutingModule,
-     AngularFireAuthModule,
+     provideFirebaseApp(() => initializeApp(environment.firebase)),
+     provideAuth(() => getAuth()),
      ...
    ],

まず、「AngularFireAuthModule」という大きなModuleは廃止になって、Firebaseのアカウントをインストールするための provideFirebaseApp と、Authまわりの provideAuth を分けて書くようになりました。

ただ、このままだと、Capacitor iOSでAuthまわりの不具合が起こります:
https://github.com/angular/angularfire/issues/3087

なので、そのCapacitorでネイティブで利用する場合はその分岐を用意します。Capacitorの isNativePlatform() でプラットフォームを判定できるので、以下のようにします。

import { getApp, provideFirebaseApp, initializeApp } from '@angular/fire/app';
import {
  getAuth,
  indexedDBLocalPersistence,
  initializeAuth,
  provideAuth,
} from '@angular/fire/auth';

...

    provideFirebaseApp(() => initializeApp(environment.firebase)),
    provideAuth(() => {
      if (Capacitor.isNativePlatform()) {
        return initializeAuth(getApp(), {
          persistence: indexedDBLocalPersistence,
        });
      } else {
        return getAuth();
      }
    }),

ネイティブプラットフォームの場合は、 indexedDBLocalPersistencepersistence にセットしてインストールします。

ログインまわりのコードの書き方

まず、 AngularFireAuth をDIしていたものが、 @angular/fire/auth からの Auth をDIするようにします。

- import { AngularFireAuth } from '@angular/fire/compat/auth';
+ import { Auth } from '@angular/fire/auth';

   constructor(
-    private afAuth: AngularFireAuth,
+    private afAuth: Auth,

重要な点はそこだけで、あとは AngularFireAuth 以下に生えていたメソッドを直接呼び出して、第一引数に Auth をセットして使うのが原則になります。

例えば、 authState だと以下のようになります。

+ import { authState } from '@angular/fire/auth';
...
-    this.afAuth.authState.pipe(...
+    authState(this.afAuth).pipe(...

this.afAuth に生えてたメソッド authState から、 authState を直接呼び出すようにします。そして、第一引数に this.afAuth を入れる形ですね。1割もないぐらい例外となるメソッドはなくはないですが、9割以上はほぼこのように修正できます。

以下、いくつかの修正例をご紹介します:

-    return this.afAuth.authState;
+    return authState(this.afAuth);
-   await this.afAuth.createUserWithEmailAndPassword(email, password)
+   await createUserWithEmailAndPassword(this.afAuth, email, password).catch(
-    await this.afAuth.signInWithEmailAndPassword(email, password)
-      .catch(async (error) => {
+    await signInWithEmailAndPassword(this.afAuth, email, password).catch(
-    await this.afAuth.sendPasswordResetEmail(email).catch(async (error) => {
+    await sendPasswordResetEmail(this.afAuth, email).catch(async (error) => {
-        await this.afAuth.sendPasswordResetEmail(email);
+        await sendPasswordResetEmail(this.afAuth, email);
-    await user.sendEmailVerification().catch(async (error) => {
+    await sendEmailVerification(user).catch(async (error) => {

簡単ですよね。それではまた。

Discussion