Firebaseの学習したこと (アプリ側の開発言語は基本Flutter)
Firebaseつらつらとどういう新しいことと学んだかつらつら備忘録として書いていきます
以下の機能は個人的に使ったことがあります
- Remote Config
- Crashytics
- Firebase Analytics
- A/B テスト
使ったことがない機能メインで学習を進めて記載していきます
FlutterでのFirebaseの設定
FlutterFire を使って実現できる
iOS
- 公式サイトのステップ1 ~ 3を行う↓
Firebase iOS
Flutterプロジェクトの場合は
GoogleService-Info.plist
ファイルを ios/Runner/
直下に設置
- FirebaseのCoreライブラリをインストールする
pubspec.yaml
dependencies:
flutter:
sdk: flutter
firebase_core: "^1.1.0"
- アプリコードでFlutter Fireを初期化するコードを入れてみる
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
return runApp(ProviderScope(child: MyApp()));
}
- Firebaseのコンソール画面で確認する
RealTimeだと即時反映してくれるのでユーザー数の数とか出ていればおk
おまけ
iOSでのビルド時間かかるからこれをPodfireにかく
# ...
target 'Runner' do
pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '7.3.0'
# ...
end
Android
iOSとほぼ同様のことをする
- 公式サイトのステップ1~3.1 までを行う
Firebase Android
Flutterプロジェクトの場合は
google-servises.json
ファイルを android/app
直下に配置
- 各gradleファイルに以下を記載する
android/app/build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
apply plugin: 'com.google.gms.google-services' ←これを記載
~~~~~
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation platform('com.google.firebase:firebase-bom:27.0.0') ←これを記載
implementation 'com.google.firebase:firebase-analytics-ktx' ←これを記載
}
android/build.gradle
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.3' ← これを記載
}
- iOSを先に入れている場合はFlutterの初期化コードを入れているのでそのままビルド、入れていないならiOS の3を行う
- iOSの4と一緒
Firebase Authのコンソール
Users・・・ユーザーの管理するところ
Sign-in Method・・・ユーザーの管理するところ
Template・・・運用メールの送信内容とかカスタマイズできる
Usage・・・使用状況
Firebase Authでメアドのサインインを実装してみる
コンソールでメール/パスワードの部分を有効にする
なんか適当にアカウント作ってみる
Usersのユーザー追加
で作れる
適当にメアドとパスワードを入力
firebase_auth
をインストールする
dependencies:
firebase_auth: "^1.1.1"
適当にサインイン画面を作る
その画面でメアドとパスワードの入力を受け取る画面を作る
以下のメソッドにメアドとパスワードの文字列を渡してあげる
FirebaseAuth.instance.signInWithEmailAndPassword({required String email, required String password})
使用例
Future<bool> signIn(String email, String password) async {
try {
await _auth.signInWithEmailAndPassword(
email:email, password:password);
return true;
} catch (e) {
print(e);
return false;
}
}
認証が失敗したら例外投げられるのでそこでハンドリングしてあげる
Firebase Authの認証状態の確認
2パターンある
- authStateChanges()
FirebaseAuth.instance
.authStateChanges()
.listen((User user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
authStateChanges()
はStream型
こっちが推奨
サインインしてるならUserクラスが、サインインしていないならnullが返ってくる
なんかバグ?かわからないけどなぜかログインしている情報が2回流れてくるし、うまく実装できなかったのでやめた
issueにも同じ問題があり↓
issue
- FirebasenAuth.instance.currentUser
FirebasenAuth.instance.currentUser
を使う
User user = FirebaseAuth.instance.currentUser;
authStateChanges()と同様でUserクラスまたはnullが返ってくる
Firebase Authでのサインアップを実装してみる
サインインとおんなじ要領で
Future<UserCredential> createUserWithEmailAndPassword(
{required String email,
required String password}
)
というメソッドを使ってできる
Future<bool> signUp(String email, String password) async {
try {
await _auth.createUserWithEmailAndPassword(
email:email, password: password);
return true;
} on FirebaseAuthException catch (e) {
print(e);
return false;
} catch (e) {
print(e);
return false;
}
}
失敗したら例外が投げられる
番外編
ソーシャルログイン(Google,Apple, Twitterなど)ボタンを一から作るのは非常に手間なので、公開されてるパッケージを使うのがオススメ
flutter_signin_buttonかsign_button
がNull Safety対応してるし、評価高いのでこれらを使うのがオススメ
どっちがいいかは完全に好み
自分はサイズを選べれる sign_button
を選択
実装の仕方はすごい簡単で各ドキュメントに記載あるので割愛
Firebase AuthでSign in with Appleをやってみる (iOS)
コンソールの準備
ログインプロパイダでAppleの項目を有効にする
実装
- Xcodeでプロジェクトファイルに設定追加
Signing&Capabilities
で Sign in with Apple
を追加する
- 公式推奨のパッケージをインストールする
Sing in with Applenのパッケージ sign_in_with_apple
暗号化のパッケージcrypto
pubspec.yaml
dependencies:
crypto: ^3.0.1
google_sign_in: ^5.0.2
- 実装する
暗号化のコード書く
/// Generates a cryptographically secure random nonce, to be included in a
/// credential request.
String generateNonce([int length = 32]) {
final charset =
'0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._';
final random = Random.secure();
return List.generate(length, (_) => charset[random.nextInt(charset.length)])
.join();
}
/// Returns the sha256 hash of [input] in hex notation.
String sha256ofString(String input) {
final bytes = utf8.encode(input);
final digest = sha256.convert(bytes);
return digest.toString();
}
認可コードを書く
Future<bool> signInWithApple() async {
final rawNonce = NonceString.generateNonce();
final nonce = Sha256String.sha256ofString(rawNonce);
try {
// Appleのサインイン
final appleCredential = await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
nonce: nonce,
);
final oauthCredential = OAuthProvider('apple.com').credential(
idToken: appleCredential.identityToken,
rawNonce: rawNonce,
);
await _auth.signInWithCredential(oauthCredential);
return true;
} catch (e) {
print(e);
return false;
}
}
これをApple用のサインインボタン押した時に呼ぶとおk
Firebase AuthでSign in with Appleをやってみる (Android)
FlutterFireでは対応していないし、Firebase側での公式のネイティブSDKでも対応していない
やるならWeb経由でやるみたいだけどめんどいので一旦保留
Firebase AuthでGoogle Sign inをやってみる(iOS/Android)
ログインプロパイダでGoogleの項目を有効にする
Firebase Emulator をAuthで試す (挫折編)
環境設定
Firebase Local Emulator Suite
を見て、Firebase CLIの環境設定が必要と把握
npmでインストールすることにした
Local Emulator Suiteを入れる前に必要な環境設定
- Node.js バージョン 8.0 以降。
- Java バージョン 1.8 以降。
JavaはGoogle Sign Inの時にjdkのセットアップ関連クリアしていたのでNode.jsの環境設定をする
この記事に環境設定
nodebrewをインストール
homebrewは入っているので
$ brew install nodebrew
$ nodebrew -v
nodebrew 1.1.0
Node.jsの最新版をインストール
Fetching: https://nodejs.org/dist/v15.7.0/node-v15.7.0-darwin-x64.tar.gz
Warning: Failed to create the file
Warning: /Users/XXXX/.nodebrew/src/v15.7.0/node-v15.7.0-darwin-x64.tar.gz: No
Warning: such file or directory
curl: (23) Failed writing body (0 != 978)
download failed: https://nodejs.org/dist/v15.7.0/node-v15.7.0-darwin-x64.tar.gz
記事同様のエラーが表示されたため、以下のディレクトリを同様に作成
$ mkdir -p ~/.nodebrew/src
そして、インストール
$ nodebrew install-binary latest
Fetching: https://nodejs.org/dist/v16.1.0/node-v16.1.0-darwin-x64.tar.gz
########################################################################################################################################################## 100.0%
Installed successfully
確認
$ nodebrew list
v16.1.0
設定
$ nodebrew use v16.1.0
use v16.1.0
.zshrc
にパスを通す
export PATH=$PATH:$HOME/.nodebrew/current/bin
確認してどっちも表示されればおk
$ node -v
v16.1.0
$ npm -v
7.11.2
Firebase CLIをインストール
npmを使ってインストールする
$ npm install -g firebase-tools
確認
$ firebase -V
9.10.2
Firebase CLIでFirebase Emulatorの設定
プロジェクトにログインする
$ firebase login
? Allow Firebase to collect CLI usage and error reporting information? (Y/n)
YESでもNOでもどっちでもおk
コマンドを叩くとログイン画面がブラウザで開かれる
Firebaseプロジェクトに入ってるアカウントを選択して、認証を許可する
入れるとこんな感じでブラウザとターミナルで表示される
✔ Success! Logged in as <アカウント>
ログアウトする場合は以下のコマンド
$ firebase logout
ログイン状態だと作成したFirebaseプロジェクトを確認できる
$ firebase projects:list
初期化する
$ firebase init
対話型で聞かれるので
どのFirebaseプロジェクトか聞かれるので既存プロジェクトの場合は既存プロジェクト
まだ作ってない場合は新規で選択してあげる
各Emulatorのポート番号どうするか聞かれるのでデフォルトでいい場合はひたすらEnter押してあげる
Emulatorも使う場合はYES
初期化が成功すると以下の3つが生成される
- firebase.json
- .firebaserc
- .gitignore...
Emulatorの実行
$ firebase emulators:start
こんな感じでコンソールが表示
http://localhost:4000 にアクセスするとEmulator UIもこんな感じに
Authを試してみる
上のスクショにもあるように UIにAuthentication
の項目があるので、デバック用のメールアドレスとパスワードを作成する
iOSの場合はInfo.plistにローカル通信の設定と
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
FlutterのコードでFirebase SDK初期化後に以下を入れて実行
FirebaseAuth.instance.useEmulator('http://localhost:9099');
してみたが接続エラーでなぜかうまくいかず・・・
Crashlyticsの導入
FlutterFireのとFirebaseの公式リファレンスを基本参考に
やること
-
コンソール画面でCrashlyticsで両OSを有効にする
-
パッケージをインストール
dependencies:
flutter:
sdk: flutter
firebase_core: ^1.1.0
firebase_analytics: ^8.0.3
firebase_crashlytics: ^2.0.2
依存関係もあるから firebase_core
と firebase_analytics
も適切なバージョンでインストール
こんな感じの依存エラーが出たらiosのフォルダで
$ pod update
して依存関係を解消する
- 各OSで必要な設定をする
Android
gradleファイルに以下を記載
android/build.gradle
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.3'
// これ記載↓
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.1'
}
}
app/build.gradle
apply plugin: 'com.google.firebase.crashlytics'
iOS
Xcode開いて Build Phases
にCrashlyticsをSDKを初期化するスクリプトを追加
自動アップロードしてくれる仕組み?的に
Input Files
にDebug版とRelease版のDWARFフォルダとInfo.plistファイルを指定
*${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}
${INFOPLIST_FILE}
- ../build/ios/Debug-iphoneos/Runner.app.dSYM/Contents/Resources/DWARF/${TARGET_NAME}
- コードを記載してみる
ボタンを押したらクラッシュさせるかを確認するアラートダイアログを適当な画面で実装
void showAlertDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
'危険',
style: const TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
),
),
content: const Text(
'クラッシュさせますか?',
),
actions: <Widget>[
TextButton(
onPressed: () {
AppLogger.logger.d('crash');
FirebaseCrashlytics.instance.setCustomKey('test', 'crashTest');
FirebaseCrashlytics.instance.crash();
},
style: TextButton.styleFrom(
primary: Colors.red,
),
child: const Text('はい'),
),
TextButton(
onPressed: () => Navigator.pop(context),
style: TextButton.styleFrom(
primary: Colors.black,
),
child: const Text('いいえ'),
),
],
);
},
クラッシュ
FirebaseCrashlytics.instance.crash();
カスタムログ
FirebaseCrashlytics.instance.setCustomKey('test', 'crashTest');
自動収集をしない設定
await FirebaseCrashlytics.instance
.setCrashlyticsCollectionEnabled(false);
Flutterの例外もキャッチできるようにする
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
非同期をキャッチする
runZonedGuarded(() {
runApp(ProviderScope(child: MyApp()));
}, (error, stackTrace) {
FirebaseCrashlytics.instance.recordError(error, stackTrace);
});
- 確認する
クラッシュボタンを押す
↓
アプリを再起動する
↓
コンソール画面で確認する
こんな感じで両OSでてくる
Android
iOS
※ 罠
Xcode で [stop Stop running the scheme or action] をクリックして、アプリの初期インスタンスを閉じます。この初期インスタンスには、Crashlytics の動作を妨げるデバッガが含まれています。
この2つの現象のせいでiOS 14の実機でデバックモードで試してもなかなかクラッシュログが流れてこなかった
回避策
リリースモードで実行
$ flutter run --release
↓
クラッシュさせる
↓
再起動(再起動にログが送信される)
↓
コンソールで確認すると出てくるはず
うまくいかない&Tips
コマンド
- Xcodeの環境変数を確認する
# 例
$ xcodebuild -showBuildSettings | grep "SRC\|PROJECT"
- dSYMファイルを探す
mdfind -name .dSYM | while read -r line; do dwarfdump -u "$line"; done | grep <dSYMのUUID>
- アップロードする
$ ~<Podsフォルダがあるディレクトリまでのパス>/Pods/FirebaseCrashlytics/upload-symbols -gsp ~<GoogleService-Info.plistがあるディレクトリまでのパス>/GoogleService-Info.plist -p ios <dSYMがあるパス>
わからない
- コマンドでUUIDを指定しても特定のdSYMファイルが探しても見つからない
- 一部シンボルが不足している(Flutterのとか)
Performance
導入はすごい簡単
- SDKインストール
- 適当に実機またはシュミレータを動かしてアプリの操作をする 以下のこともついでにする(自動収集してくれるので)
- 通信処理のある機能
- バックグラウンド⇄フォアグラウンド交互に入る
インストール
dependencies:
flutter:
sdk: flutter
firebase_performance: ^0.7.0+2
適当に操作すると一定時間後こんな感じでコンソール画面が更新される
自動収集するもの
- アプリの起動時間(iOS/Android)
- アプリの画面のレンダリング(iOS/Android)(iOS/Android)
- ネットワークリクエスト
トレースの種類
- トレースの種類 アプリ開始トレース—ユーザーがアプリを開いてからアプリが応答するまでの時間を測定するトレース
- App-in-foregroundトレース—アプリがフォアグラウンドで実行され、ユーザーが利用できる時間を測定するトレース
- App-in-backgroundトレース—アプリがバックグラウンドで実行されている時間を測定するトレース
カスタムトレース作ってコードの入れることもできるけれども、特に何か測りたいものが現状ないので保留
気が向いたら追記する
FireStore関連で読んだもの
DB設計皆無なので、NoSQLといえど右も左もわからないのでとりあえず色々読んだ or 読んでいるもの
随時更新していく
本
[サーバーレス開発プラットフォーム Firebase入門] (https://www.amazon.co.jp/サーバーレス開発プラットフォーム-Firebase入門-掌田-津耶乃/dp/4798057754)
公式ドキュメント
記事
FireStore
Cloud Storage
Cloud Messaging
資料
以下を読んだ
- Firebase公式リファレンス
- Flutter Frieのリファレンス
- Flutter初心者がFCMを使ってプッシュ通知を受け取る〜設定編〜(2021/3/22版)
- Flutter初心者がFCMを使ってプッシュ通知を受け取る〜アプリ実装編〜(2021/3/22版)
- プッシュ通知に必要な証明書の作り方2021
- Firebase編7:Cloud Messaging
手順
-
iOSのプッシュ通知の証明書発行ならびにプロジェクトファイルの設定
これを見た→FlutterFireに書いてある説明 -
証明書でのp12ファイル作ったらFirebaseコンソールにp12ファイルをアップロード
-
実装する
必要なパッケージをインストールする
dependencies:
flutter:
firebase_messaging: ^10.0.0
flutter_local_notifications: ^5.0.0+4
Podfileでの依存関係が何かあれば、pod update
するなりして解決する
iOSの実装をする
通知のパーミッションダイアログの表示
表示のタイミングは機能によって変わるが、特に通知を生かしたアプリ内機能はなくプッシュ通知のみを想定だと起動時にパーミッションダイアログを表示
main.dart
に記載↓
// iOSのプッシュ通知の設定リクエスト
await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
フォアグラウンドで通知を表示する実装
main.dart
に記載↓
// iOS のプッシュ通知のフォアグラウンドの表示
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
バックグラウンドで通知を表示する実装
main.dart
に記載↓
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// アプリケーションコンテキスト外のIsolateで実行しているため
// ここでアプリケーションの状態やUIの更新はしてはダメ
// ドキュメントより
await Firebase.initializeApp();
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// バックグラウンド時
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
}
通知のバックグラウンドでの呼び出しは一番トップ上?のとこでないといけない(main.dartとか)
Androidでの通知の実装
FlutterFireではフォアグラウンドの表示は提供してないのでflutter_local_notifications パッケージを使う必要がある
チャンネルを指定するための AndroidNotificationChannel
のインスタンスを生成しておく
使い回しおkなのでconst
class NotificationChannel {
static const androidChannel = AndroidNotificationChannel(
'high_importance_channel',
'High Importance Notifications',
'This channel is used for important notifications.',
importance: Importance.max,
);
}
通知チャンネルを指定しておく
main.dart
に記載
// Androidのチャンネル指定
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(NotificationChannel.androidChannel);
デフォルトのFCMのチャンネルではなく、独自のチャンネルを使うようにするため、マニュフェストファイルに以下を記載
(あんまここAndroidの通知の仕様把握し切れておらず・・)
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id"
/>
FlutterLocalNotificationsPlugin
のインスタンスはどっかにグローバル変数で用意した
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
App系ウィジットを返すだけのapp.dart
ファイルに切り出す
例えば、どの画面でも通知を表示できるようにそのApp系ウィジットを返すbuildメソッド内で通知を表示する
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
final notification = message.notification;
final android = message.notification?.android;
final channel = NotificationChannel.androidChannel;
if (notification != null && android != null) {
print(notification.body);
FlutterLocalNotificationsPlugin().show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channel.description,
icon: 'launch_background',
),
),
);
}
});
バックグラウンドで通知を表示する実装
iOSと一緒
これをiOSの方で実装しておけば両OSともバックグラウンドで表示できる
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// アプリケーションコンテキスト外のIsolateで実行しているため
// ここでアプリケーションの状態やUIの更新はしてはダメ
// ドキュメントより
await Firebase.initializeApp();
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// バックグラウンド時
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
}
- Firebaseのコンソールでメッセージを送信してみる
新しい通知 > 適当に入力欄にタイトルと本文を埋める > ターゲットを両OS指定 (別のアプリをターゲットにする
でもう片方のOSを指定できる)
確認 > 公開 で送信できる
- 実機で確認する
iPhoneは実機のみでしかプッシュ通知の確認ができない
Androidも通知を送りまくってるとエミュレーターが重くなって真っ黒現象が多かったので実機で確認がオススメ
各OSとアプリの状態(フォアグラウンド&バックグラウンド)に合わせてこんな感じで出る
iOS
Android
Androidで通知の確認する際は端末の設定も確認して、適切でなければ以下を参考に設定を切り替える↓
InAppMessaging
すごい簡単
パッケージをインストールするだけ
dependencies:
flutter:
sdk: flutter
firebase_in_app_messaging: ^0.5.0+4
あとはコンソールでポチポチアプリ内メッセージを作成する
ざっと
- ダイアログのタイプ カード、モーダル、画像のみ、トップバナーの4種類
- 色設定 背景、テキストなど
- テキスト メッセージ、本文など
- 画像(URLで)
- ボタン
- テキスト
- 色
- タップ時の遷移先
なことができる
表示のされ方が以下なので
要件がうまく当てはまらない場合はInAppMessagingは適さないかも
例 アプリ起動時に毎回表示するダイアログ(次回以降表示しないボタン込みのやつ)
こんな感じででた
あとイベントログを仕込んでそのイベントをトリガーにして表示は可能かも?
トップ画面で表示したいけどログイン済みのみ場合出したい場合は
ログイン済みであることを識別できるイベントログを仕込んでそのログでトリガーすれば未ログイン時は出さないとかできるかも?