Firebase DynamicLinks を動的に変化するパラメータで使う

#背景
アプリのシェアなどでリンクからアプリを起動させる場合、DeepLinkパラメータを渡したいことも多いと思いますが、対象のアプリが未インストールの場合は色々手間があったと思います。

[FirebaseDynamicLinks](https://firebase.google.com/docs/dynamic-links/)(以下***FDL***)とはFirebaseが提供している、OSやアプリのインストール状況などから遷移先を制御できるリンクです。***FDL***ならば、未インストールの時にストアに遷移してインストール→アプリ起動した場合でもDeepLinkパラメータを保持することができます。

また、今後googleは今までの[URL短縮サービス](https://developers.googleblog.com/2018/03/transitioning-google-url-shortener.html)をやめて、***FDL*****に置き換えていく**との声明も発表した[(参考リンク)](https://developers.googleblog.com/2018/03/transitioning-google-url-shortener.html)ようで、重要になるんじゃないかと勝手に思ってます。
(既存の短縮リンクは引き続き機能するとのことですのでそこはひとまず安心です)

***※2019年9月26日追記
Firebase Summitにて`Shorten URLs`サービスが発表されましたのでFDLはこの目的のみの役目は終えたようです…
まぁこっちは多機能すぎて逆に広まらなかったのかなぁと思います。***

この記事では、以前業務で使った経験から、***FDL***について動的にパラメータを渡して生成する方法を紹介します。
Firebaseへの登録などのところは割愛します。

#***FDL***の仕様
## 基本的な動き
まず改めて説明しますが、***FDL***は、一つのリンク(同一URL)でandroid iOS両方に対して以下のようなことができます。

- アプリインストール済みならDeepLink起動
- アプリ未インストールならOSごとのストアに遷移
- ストアに遷移する場合、OSごとに行き先をストアから変更
- ストアに遷移してインストール、その後に普通に起動した場合でもDeepLink起動
- FirebaseAnalyticsと連携してデータが取れる

それぞれのアプリはFirebaseのプロジェクトに登録しておく必要があります。
また、確認した限り、***FDL***はこれらの挙動を

- Android -> Google Play 開発者サービスとの連携
- iOS -> iOS9以上は`ユニバーサルリンク`、9未満は`カスタムスキーム起動`

によって可能にしているようです。
ただ、記事の本題と外れるのですが、以前iOS担当者に確認したところ`10`or`11`でも`ユニバーサルリンク`or`カスタムスキーム`どちらで来る場合もあるとのことで、iOSアプリでは両方受け取れるように実装が必要だったとのことです(今では改善されているかもしれません)。
この辺はここでは詳しく紹介しませんので、公式の **Receive DynamicLinks** ドキュメント[(Android)](https://firebase.google.com/docs/dynamic-links/android/receive)[(iOS)](https://firebase.google.com/docs/dynamic-links/ios/receive)を参照してください。

あとAnalyticsとの連携も詳しく実装しなかったので公式の紹介に留めます。
[**Dynamic Links アナリティクス データを表示する**](https://firebase.google.com/docs/dynamic-links/analytics)
ざっと見た感じだと、同じリンクが何回クリックされたかとか、各リダイレクト先の参照回数などが取れるようですので、例えばツイッターにリンクをツイートしたら

**インストール済み(かそうでない)Android/iOSユーザが何回リンクを開いた**

みたいなデータが取れると思います。アプリのGrowthに有用そうです。


##パラメータの説明
簡単に各種パラメータを説明します。[公式の手動作成ページ](https://firebase.google.com/docs/dynamic-links/create-manually)のほぼ丸写しですが、ちょっと前に業務で実際に使ってみてこうだった、みたいなこともあったので一応載せます。
名前も以前ドキュメントに載ってたんですが、今は載ってないようです…

|名前|キー名|使用OS|内容|個人的追記
|:-----------------|:-----------------:|:------------------|:---|:------
|Link | link | `Android` `iOS` `desktop` | アプリに渡されるURLです。DeepLinkとして使うパラメータを入れられます。HTTP(S)スキームである必要がありますが、別の***FDL***は指定できません。|`dfl` がなかった時はデスクトップではこれしか開けませんでした。また、OGPで参照されるURLでもありました。
|(DesktopFallbackLink?) | dfl | `desktop` | デスクトップで開くURLです。DeepLinkとして使うパラメータを入れられます。HTTP(S)スキームである必要があります、また、`link`と違い別の***FDL***を指定できます。|私が業務で使用した当時は存在しなかったのですが、最近になって追加されたもののようです。ただ、デバッグで`Param 'dfl' is deprecated. Replace with 'ofl'.`と出てきた時期が一瞬あったのでちょっと今後の動向を追いたいと思います。
|Android APP | apn | `Android` | Androidアプリのパッケージ名です。
Custom app location | al | `Android` | 任意のカスタムスキームを使うことのできるリンクです。DeepLinkとして使うパラメータを入れられます。このパラメータの追加は任意ですがこれが存在している場合、Androidにおいては `link` より優先されて使用されます。|このパラメータは私が業務で使用した当時はあったんですが現在のドキュメントからは削除されているようです。現在は`link` をAndroid iOS用にしてデスクトップ用を`dfl`で別にする形になりました。そのプロジェクトで現状でもandroid専用のDeepLinkとして機能はしているので使えるとは思いますが、新規での使用はやめたほうがいいかもしれません
|Android Fallback link | afl | `Android` |アプリがインストールされてない時に開かれるAndroid用リンクです。これがなくてもデフォルトでストアの該当ぺージに遷移しますが、他に飛ばしたい場所がある場合に指定します。
|Minimum version code (Android) | amv | `Android` | リンクを開くことのできる最低バージョンコードです。アプリのバージョンがこれより低い場合、ストアに遷移します。|このストア遷移もフォールバックリンクの影響を受けます。
|iOS App | ibi | `iOS` |iOSアプリのバンドルIDです。
|iOS Custom Scheme | ius | `iOS` |アプリのカスタムURLスキームです。|`://` 以前の部分のみ指定してください。
|App Store ID | isi | `iOS` | iOSアプリのAppStore IDです。
|iOS Fallback Link | ifl | `iOS` | アプリがインストールされてない時に開かれるiOS用リンクです。これがなくてもデフォルトでストアの該当ぺージに遷移しますが、他に飛ばしたい場所がある場合に指定します。
|iPad App | ipbi | `iOS` | iPad用アプリのバンドルIDです。
|iPad Fallback Link | ipfl | `iOS` |iPad用の、アプリがインストールされてない時に開かれるリンクです。これがなくてもデフォルトでストアの該当ぺージに遷移しますが、他に飛ばしたい場所がある場合に指定します。
|Minimum version code (iOS) | imv | `iOS` | リンクを開くことのできる最低バージョン番号です。Androidと違い、処理方法はアプリ側で書く必要があります。
|(debug?) | d |  | 1を入れると実際に機能するリンクではなく、各種環境のリンクのリダイレクト先のフローチャートが表示されるリンクになります。**このパラメータは後述の省略したものに追加でつけることができます。**|

他にも広告用パラメータとかあるんですが、私が使ったことないのでここでは省きます。
以前は`link`パラメータを基にOGPが出てたんですが、2018/3/31現在出なくなっているようです…ここは今後の動向を追いたいですね。

#***FDL***の作成方法
本題です。
まずは短縮していない***FDL***の一例を見てみます。各パラメータ内のURLはパーセントエンコーディングしておきましょう。

一例
`https://{Firebaseから発行されたapp_code}.app.goo.gl/
?link={http(s)://で始まるリンク、主にアプリ用のDeepLink}
&apn={Androidアプリパッケージ名}
&ibi={iOSアプリバンドルID}
&isi={iOSアプリストアID}
&ius={iOSアプリをフックするカスタムスキーム}
&dfl={デスクトップ用リンク}`

(必要なパラメータはその都度、使用目的に応じて精査してください。)
動的に作るだけならおそらく`link`の中身を変えるだけで済みますが、このままではリンクが長いので短縮したいところです。

背景でも述べたように、短縮***FDL***はFirebaseConsole上で手動で作成できます。

<img width="1244" alt="スクリーンショット 2017-01-03 22.02.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/153777/22063546-2fde-0408-9ca0-b29572791332.png">

ただ、動的に変わるDeepLinkパラメータを付与したいので、手動作成は現実的ではないです。(2018/03/31現在、`dfl`もつけられない)

##短縮APIを使用する
そこで[DynamicLinks公式短縮API](https://firebase.google.com/docs/reference/dynamic-links/link-shortener)を利用します。
私が業務で使用した当時は自プロジェクトのサーバにアプリからパラメータを送って、上述のAPIをサーバ側から叩いて、生成したリンクをサーバからアプリに渡す、という流れで実装しました。

APIへのパラメータの渡し方は二手法があり、以下に[公式のREST作成ドキュメント]((https://firebase.google.com/docs/dynamic-links/rest))から引用します。

***各パラメータを個別に書く方法***

```
POST https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key={api_key}
Content-Type: application/json

{
  "dynamicLinkInfo": {
    "dynamicLinkDomain": "abc123.app.goo.gl",
    "link": "https://example.com/",
    "androidInfo": {
      {
        "androidPackageName": "com.example.android"
      }
    },
    "iosInfo": {
      {
        "iosBundleId": "com.example.ios"
      }
    }
  }
}
```

***長いURLをそのまま書く方法***

```
POST https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=api_key
Content-Type: application/json

{
   "longDynamicLink": "https://abc123.app.goo.gl/?link=https://example.com/&apn=com.example.android&ibi=com.example.ios"
}
```

{api_key}にはFirebaseConsole上の設定ページで確認出来る、プロジェクトのwebAPIキーを挿入します。

<img width="972" alt="スクリーンショット 2017-01-03 22.24.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/153777/9cfb84a2-bbc6-6779-83ca-4ddd96651557.png">

また、デフォルトもしくは`"suffix": { "option": "UNGUESSABLE" }`と付けるとドメインの後が17文字の英数字の形に短縮されます。
`"suffix": { "option": "SHORT" }`とすると4文字以上かつユニークになる最低限の文字数で短縮されます。

また、短縮APIのレスポンスには`"previewLink"`というものも付随してきます。これは末尾に`&d=1`が追加されているものですが、パラメータ一覧で説明したようにこのパラメータがあると通常の処理の代わりに、以下のようなパラメータを基にした環境別のプレビューフローチャートが表示されます。

<img width="720" alt="スクリーンショット 2017-01-03 23.05.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/153777/7baf7e02-3eaa-8d00-b1ae-72f13a46be4f.png">

各リンクテキストに実際に開かれるURLが入っていますので、非常に有益だと思います。

##アプリ側で作る
私が業務で使用した当時はなかったのですが、現在はアプリ側のSDKからでも生成できるようです…
こちらは実装経験がないので公式リンクだけ載せる形にします
[Android公式](https://firebase.google.com/docs/dynamic-links/android/create)
[iOS公式](https://firebase.google.com/docs/dynamic-links/ios/create)

どんどん便利になっててびっくりしますね…
ただ、私の参加したプロジェクトではサーバ上の処理によるパラメータ付与が必要だったので、どっちみちサーバに移譲してたかなとは思います。
この辺はユースケースに依存すると思います。

#まとめ
***FirebaseDynamicLinks***について、自分が以前業務で携わった経験から記事を書かせていただきました。
Firebaseというとやはり*RealtimeDatabase*とか*Storage*、*Authentication*が有名で、***FDL***全然使われてないイメージがあるんですよね…OS跨がない限りそれぞれもう似た仕組み(Androidだとintentスキーム)があるからって言われれば確かにそうなんですが、広告パラメータとか使わずに**ストア遷移->インストールを経由してもDeepLink残る**って実際便利だと思うんです。

最後になりますが、この記事が皆様にとって***FDL***ライフの手助けとなれば幸いです。お目汚し失礼しました。