【Firebase】個人メモ【iOS / SwiftUI】

## 実行環境
・Xcode Version 13.2.1

# Firebase プロジェクト
#### 開発環境と本番環境の分け方
Firebaseプロジェクトに設定するバンドルIDを本番用プロジェクトは本番のもの、開発用プロジェクトは後ろに-devなどを付けたものにし、Xcode側は開発中は以下のようにしておけば（良くないけど）ひとまず問題なさそう。（プロジェクト名は-prod/-devなど）
・バンドル ID：本番のもの＋-devなどに変更する
・GoogleService-Info.plist：本番用のものをリネームしておき、開発用のをGoogleService-Info.plistとしておく

# パッケージ
#### パッケージのインストール
Project > Package Dependenciesからインストール。
一部のライブラリをインストール後別のライブラリを追加する場合は、以下参照。
https://dev.classmethod.jp/articles/spm-add-new-package-product/
※サービスに対応するライブラリの一覧（公式）：
https://firebase.google.com/docs/ios/setup?hl=ja

# Remote Config

```
import FirebaseRemoteConfig
-----

@State var testFlagBool = false //@StateでもUserDefaultsでもなんでも用途に応じて
-----

let remoteConfig = RemoteConfig.remoteConfig()
let settings = RemoteConfigSettings()
settings.minimumFetchInterval = 0 //テストの場合
remoteConfig.configSettings = settings

remoteConfig.fetchAndActivate() { (status, error) -> Void in
    if status == .successFetchedFromRemote || status == .successUsingPreFetchedData {
        self.testFlagBool = remoteConfig.configValue(forKey: "testFlagBool").boolValue
    } else {
        print("Error: \(error?.localizedDescription ?? "No error available.")")
    }
}
```
# Authentication（匿名認証）
#### uid
iOSの場合、明示的に記述しない限り、端末に割り当てられるuidはアンインストールしてもずっと同じ。

#### Cloud FunctionsでのCustom Claim操作
```
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();

//uidに対してカスタムクレームとしてroleとfamilyIdを付与する関数
exports.setCustomClaim = functions.https.onRequest((request, response) => {
	functions.logger.info("setCustomClaim start uid: " + request.query.uid + ", role: " + request.query.role + ", familyId: " + request.query.familyId);
	admin.auth().setCustomUserClaims(request.query.uid, {role: request.query.role, familyId: request.query.familyId}).then(() => {
		response.send("OK");
		functions.logger.info("setCustomClaim end uid: " + request.query.uid);
	})
});

//uidに対してカスタムクレームを削除する関数
exports.deleteCustomClaim = functions.https.onRequest((request, response) => {
	functions.logger.info("deleteCustomClaim start uid: " + request.query.uid);
	admin.auth().setCustomUserClaims(request.query.uid, {role: null, familyId: null}).then(() => {
		response.send("OK");
		functions.logger.info("deleteCustomClaim end uid: " + request.query.uid);
	})
});
```
Custom Claim操作後に情報を取得する際は、以下を使用し最新の情報を取得すること。
```
authResult?.user.getIDTokenResult(forcingRefresh: true)
```
#### Realtime Databaseのルール例
```
{
  "rules": {
    "test": {
      "$uid": {
        ".read": "auth != null", //他のユーザーもreadする場合
        // ".read": "auth != null && auth.uid == $uid", //自身のデータのみreadする場合
        //".read": "auth.uid == 'hogehoge1234567890abcdefghijklmn'", //特定のuidのみreadする場合
        ".write": "$uid === auth.uid", //writeは自身のみの場合
        //".write": false, //writeはしない場合
        //".write": "auth.token.role == 'admin' && auth.token.familyId == $uid" //カスタムクレーム使用
      }
    }
  }
}
```
#### Storageのルール例
```
service firebase.storage {
	match /b/{bucket}/o {
  	match /{familyId}/{path=**} {
  		allow read: if request.auth.token.familyId == familyId;
  		allow write: if request.auth.token.familyId == familyId || request.auth.uid == familyId;
    }
  }
}
```