🧑‍🚒

React NativeでFirebase Dynamic Linksを使ったシェア機能を実装する

2023/04/26に公開

はじめに

こんにちは、株式会社カナリーの中野です。
私たちは現在「Canary」というお部屋探しのアプリを作っています。
https://canary-app.jp/

Canary の中には物件の共有機能があります。
物件ページから共有ボタンを押すと共有リンクが作成され、作成されたリンクを開くと、アプリで該当のページが開く機能です。

3 年ほど前に実装された機能ですが、さまざまな問題を抱えておりました。

既存のシェア機能の問題点

リンク先は 3 年ほど前に作成された古い LP で、その LP 内で振り分け処理(アプリを開くのか、アプリのストア画面に飛ぶのか、LP をそのまま表示するのか)をしていました。
そしてアプリ側は React Native の Linking を使ってクエリパラメーターを見てナビゲート先を判断するというおれおれ実装だったのですが、以下のような問題がありました。

不具合が多かった

バックグラウンドでは機能するがフォアグラウンドでは機能しない、リファクタすると動かなくなった、みたいなことが多発していました。

メンテナンス性が悪い

3 年以上前に前任者が作った古いコードなので、まず実装を辿ることから始まるのですが、おれおれな処理を追うのは大変で、不具合を見つけても解決までに時間がかかっていました。

URL が長い

物件 ID をそのままクエリパラメータとして渡していたので URL がとても長くなっていました。

dev 環境がなかった

開発時の dev 環境では機能せず、とはいえ現状のコードを dev 環境でも確認できるようリファクタするモチベーションも上がらず、prod でしか確認できないというよくない状態が続いていました。

体験がよくなかった

リンクをスマホで開き、アプリが入っていなかった場合はストアの画面に遷移されるのですが、リンクを共有されたユーザーの視点で考えると、すぐにお部屋の情報を見たいはずであり、この体験はあまりよくないのではと感じました。
Canary にはWeb 版があるので、アプリがない場合はブラウザを起動し、Web でお部屋の情報が表示されると良さそうです。

Firebase Dynamic Links でシェア機能をリニューアル

上の問題点を解決するべく「シェア機能リニューアルプロジェクト」が立ち上がりました。
あらためて仕様を練り直し、その仕様を実現できるかつ上の問題点を解決できる実装方法を探したところ、Firebase Dynamic Links で実装するのが良さそうという結論にいたりました。

この結論に至った理由

  • React Native 用の公式ドキュメントもあり、実装もシンプルそう
  • サービスに乗っかればメンテナンスも楽になりそう
  • 別の用途でアプリ内に dev 用と prod 用の Firebase プロジェクトをすでに追加していたので、それぞれに Dynamik Links の設定を追加するだけで環境の問題は解決しそう
  • アプリがない場合は代わりのページを用意する、という実装もシンプルに書けそう
  • 短縮 URL を手軽に作れる
  • 無料

あらためて仕様の確認

  • 共有する側
    • 物件ページの「シェアボタン」をタップでリンクを作成
  • 共有される側 - アプリがある場合 - リンクをタップするとアプリが起動し、指定の物件ページまでナビゲートされる - アプリがない場合(または PC のブラウザで開いた場合)- リンクをタップするとブラウザが立ち上がり、Web で物件ページを開く

    シェアボタン

実装

環境

  • expo: 46.0.16(bare workflow)
  • react-native: 0.69.6
  • @react-native-firebase/dynamic-links: 14.12.0

Firebase コンソールからリンク用のドメインを作成する

独自ドメインを使用する方法もありますが、今回は無料のカスタム サブドメインである page.link を使用して作成します。

コンソールの Dynamic Links のページの「URL 接頭辞の追加」から希望のドメインを入力して作成します。

  • 今回は canaryshareroom と入力し canaryshareroom.page.link というドメインを作成しました
  • dev 用のプロジェクトでも同様に名前を決めてドメインを作成します

iOS の設定

iOS で Dynamic Links を機能させるには Xcode の設定が必要です。
このドキュメントの手順にしたがって進めるだけなのですが、注意点としては

  • Firebase コンソールで作成したドメインを Associated Domains に追加しますが、必ず applinks: の接頭辞を付ける必要があります
  • dev, prodなど環境を分けている場合はそれぞれに追加が必要です

    Signing & Capabilities

Android の設定

この手順を参考にSHA-256 fingerprintを作成して Firebase コンソールにてアプリに追加します。
すでに auth など別サービスを利用していて追加済みの場合はスキップできます。

リンクの作成

まずコードを示してから 1 つずつ解説します。

dynamicLinks.ts
import dynamicLinks from '@react-native-firebase/dynamic-links';

// これら3つはdev/prodに応じて切り替わる様にしている
const domainUrlPrefix = // 今回Firebaseコンソールから作成したリンク用のドメインが入る
const bundleId = //com.xxx.yyy
const canaryWebPrefix = // WebのURL

export const buildDynamicLink = async (path: string) => {
  const canaryWebChintaiRoomsPageUrl = `${canaryWebPrefix}${path}`;
  const dynamicLink = await dynamicLinks().buildShortLink({
    link: canaryWebChintaiRoomsPageUrl,
    domainUriPrefix: domainUrlPrefix,
    android: {
      packageName: bundleId,
      fallbackUrl: canaryWebChintaiRoomsPageUrl,
      minimumVersion: 'xxx',
    },
    ios: {
      bundleId: bundleId,
      appStoreId: 'xxxxxxxxxx',
      fallbackUrl: canaryWebChintaiRoomsPageUrl,
      minimumVersion: 'x.x.x',
    },
    navigation: {
      forcedRedirectEnabled: true,
    },
  });
  return dynamicLink;
};
  • buildShortLink()
    • このメソッドを使うことで短縮 URL が作成されます
    • たとえばここをbuildLinkに置き換えると短縮されない長いリンクになります
  • fallbackUrl
    • アプリがない(または PC で開いた)場合に飛ばす Web の URL を指定します
  • minimumVersion
    • この Dynamic Links をリリースするバージョンを指定します
    • こうすることで、指定したバージョン未満のアプリでリンクを踏んだ場合、store のページに遷移させることができます
  • forcedRedirectEnabled
    • リンクを開いた場合の遷移方法は 2 つあり、1 つは間にプレビューページを用意する方法、もう 1 つは直接遷移させる方法です。このオプションで選ぶことができます。
    • 今回は直接遷移させたかったのでtrueにしています。

呼び出し側(シェアボタンの実装箇所)ではこのように使っています。

// idには共有する部屋のidが入る
const link = await buildDynamicLink(`/chintai/rooms/${id}/`);

リンクからアプリを開く

バックグラウンド

こちらもまずコードを示してから 1 つずつ解説します。

dynamicLinks.ts
export const getInitialLink = async () => {
  const initialLink = await dynamicLinks().getInitialLink();
  return initialLink;
};

export const handleDynamicLink = (link: FirebaseDynamicLinksTypes.DynamicLink) => {
  const url = link.url;

  // ここでナビゲーションや必要な処理を書く
};
呼び出し側のファイル
const link = await getInitialLink();
if (link) {
  handleDynamicLink(link);
}
  • dynamicLinks().getInitialLink();
    • これで Dynamic Links を受け取ります。
  • handleDynamicLink
    • リンクを受け取った後の処理は後述するフォアグラウンドでも同様に使いたいので、handleDynamicLinkという関数にまとめて使いまわせるようにしています。
  • 呼び出し側
    • Canary ではアプリ起動時の分岐ロジックをまとめたファイルがあるのでそこで呼び出していますが、もっとシンプルな場合はドキュメントに倣ってApp.tsx内でuseEffectするか、専用の hooks を新しく作ってもよいと思います。

フォアグラウンド

dynamicLinks.ts
export const useDynamicLinkForeground = () => {
  useEffect(() => {
    const unsubscribe = dynamicLinks().onLink(handleDynamicLink);
    return () => unsubscribe();
  }, []);
};
App.tsx
const App = () => {
  useDynamicLinkForeground();

  ...
};

アプリがすでに起動している場合はとくに複雑な分岐ロジックがなかったので、hooks を新たに用意してApp.tsx内で使っています。

動かしてみる

シェアボタンを押すと以下のような短縮 URL が作成されます。

https://canaryshareroom.page.link/Chcs68W4rbzgXZw68

アプリがインストールされた状態でリンクを開くと・・・

アプリで物件ページを開けました!

ではアプリをアンインストールしてリンクを開くと・・・

Web で物件ページを開けました!
fallbackUrlがちゃんと機能しているようです

まとめ

今回は Firebase Dynamic Links を使ってシェア機能をリニューアルしましたが、ドキュメントが充実し実装もシンプルで、問題点にあったメンテナンス性も解決できたと思います。

Canary Tech Blog

Discussion