シンクロ・フード エンジニアブログ

飲食店ドットコムを運営する、株式会社シンクロ・フードの技術ブログです

AppsFlyerのUDLを使ったディファードディープリンク実装で学んだこと

シンクロ・フード開発部モバイルアプリチームの横山です。
普段は、飲食店と飲食店で働きたい求職者を繋ぐ「求人飲食店ドットコム」アプリのAndroid開発を担当しています。

今回は、アプリにディファードディープリンクを実装した経緯と、調査・設計・実装を通じて得た知見をまとめます。同じような機能を検討している方の参考になれば幸いです。

実装の背景

弊社アプリではすでにUniversal Links(iOS)とApp Links(Android)によるディープリンクを実装していましたが、これらはアプリがインストール済みのユーザーにしか機能しないという制約があります。リンクを踏んだ時点でアプリが入っていなければ、Webページへのフォールバックで終わってしまいます。

今回、求人飲食店ドットコムの求人詳細画面に「アプリで開く」ボタンを追加することになりました。このボタンはアプリをインストールしていないユーザーにも利用してもらう想定です。 つまり、「未インストールのユーザーをApp Store / Google Playへ誘導し、インストール後に目的の画面へ遷移させる」 という仕組みが必要でした。この課題を解決するのがディファードディープリンクです。

ディファードディープリンク

ディープリンクの種類と選定理由

ひとくちに「ディープリンク」といっても、実装方式はいくつかに分類されます。

  • Custom URL Schememyapp://path のようなカスタムスキームでアプリを起動する方式です。シンプルな反面、アプリが未インストールの場合はエラーになるためユーザー体験上の課題があります。また、同じスキームを別のアプリが乗っ取れてしまう「URLスキームハイジャック」のような脆弱性もあり、現在は積極的に使われるケースは少なくなっています。

  • Universal Links(iOS)/ App Links(Android):通常の https:// URLでアプリを起動できるOSレベルの仕組みです。アプリ未インストール時はそのままWebページとして開くため自然なフォールバックが実現でき、現在のスタンダードといえます。弊社アプリもこの方式をすでに導入していました。

  • サードパーティ製SDK(AppsFlyer / Adjust等):SDKが提供するリンク機能を利用する方式です。ディープリンクとしての機能に加えて、インストール前後の計測やアトリビューションにも対応しており、マーケティング施策と連携しやすいのが特徴です。今回はもともとAppsFlyerのOneLinkを利用していたため、AppsFlyerが提供するUDL(Unified Deep Linking)を採用する方針となりました。

UDL(Unified Deep Linking)とは

UDLはAppsFlyerが提供するディープリンク取得APIで、従来は別々のコールバックで制御していた「Direct Deep Linking」と「Deferred Deep Linking」のAPIを、1つのインターフェースに統合した仕組みのようです。

  • Direct Deep Linkingは、アプリがすでにインストールされている状態でリンクから起動するケースです。
  • Deferred Deep Linkingは、未インストールの状態でリンクを踏んだユーザーがストアからインストールし、初回起動した際に遷移先を復元するケースです。コールバックはアプリ起動後に非同期で返ってくるため、「いつデータが返ってくるか」というタイミングの揺らぎへの対応が、実装上の重要な課題となります。

UDLの調査

UDLはSDKの初期化とともにコールバックを購読し、非同期でパラメータを取得する仕組みです。そのため、「いつデータが返ってくるか」というタイミングの揺らぎを考慮し、実装前に以下のユースケースに沿って挙動を検証しました。

  • パラメータの伝搬: リンクに付与した情報がストア経由でも欠落せずに受け取れるか、また Direct / Deferred で受け取れる値に差異がないか
  • コールバックのタイミング: Direct / Deferred でそれぞれで、どのタイミングでデータが返ってくるか
  • 「15分制約」の挙動: インストールから起動までのタイムリミットがどの程度厳密に運用されているか

調査を通じて浮き彫りになった課題は、「コールバックの即時性は必ずしも保証されない」という点でした。ネットワーク環境や端末の状態によって、数秒以内にデータが取得できることもあれば、タイムアウトで情報が返ってこないケースの可能性もあるようでした。このため、実装側では「情報が取得できなかった場合」を前提としたハンドリングが必須ということが分かりました。

また、実機検証の結果、ストアを経由する際(Deferred)には利用できるパラメータに制約があることも分かりました。 取得可否の詳細は以下の通りです。

パラメータ取得可否の検証結果

キー Direct Deferred 備考
deep_link_value ✅ 取得可 ✅ 取得可 管理画面で設定可
deep_link_sub1 ✅ 取得可 ✅ 取得可 管理画面で設定可
deep_link_sub210 ✅ 取得可 ✅ 取得可 管理画面で設定不可
deep_link_sub11 ✅ 取得可 取得不可 -
custom_key ✅ 取得可 取得不可 -

上記の内容は執筆時点(2026年3月)の調査結果です。AppsFlyerの仕様変更により情報が更新される可能性があるため、実装の際は必ず最新の公式ドキュメントをご参照ください。

実装にあたって考えたこと

URLの命名規則

ディファードディープリンクで渡すURLは、一度決めたら変更しにくい性質上、設計には慎重になりました。 今回アプリに渡す必要がある情報は、画面識別子・エリア情報・求人詳細ID・職種情報の4つです。ただし、これらの情報の性質は均一ではありません。画面識別子・エリア情報・求人詳細IDは遷移先の画面を決定するために必須の情報である一方、職種情報は絞り込み条件として画面に渡す付加的な情報です。後者はパラメータの種類や組み合わせが変わりやすく、将来的に増減したり仕様が変わったりする可能性もあります。こうした性質の違いを踏まえ、どのようにURLに乗せるかを以下の3案で検討しました。

案1:パラメータ分割方式

  • AppsFlyerの標準フィールド(deep_link_valuedeep_link_sub110)に各値を個別に割り振る方式
  • パラメータの意味がURL上で明確になる反面、sub10 までという上限があり将来的にパラメータが増えた際に対応できなくなるリスクがある
  • 「sub1はエリア情報」「sub2は職種情報」といったマッピング定義を厳密に管理し続ける必要があり、運用コストの面でも懸念があった

案2:URL埋め込み方式

  • deep_link_value にWebの完全なURLをエンコードして丸ごと格納する方式
  • 個数制限を気にせず済み、WebのURL構造をそのまま流用できる点は魅力的だった
  • アプリ独自の画面(Webに対応するURLが存在しない画面)を開きたい場合にダミーURLの設計が別途必要になる点が課題として残った

案3:ハイブリッド方式(採用)

  • deep_link_value に画面識別子、deep_link_sub1 に職種情報を含むWebの完全URL、deep_link_sub2 にエリア情報、deep_link_sub3 に求人詳細IDを入れる方式
  • アプリは deep_link_value だけを見れば遷移先を即座に判断でき、必須の求人詳細IDとエリア情報はそれぞれ deep_link_sub2deep_link_sub3 で確実に受け取れる。一方、職種情報など付加的な絞り込み条件は、WebのURLにクエリパラメータとして存在しているため、そのURL自体を deep_link_sub1 に格納することで、OneLinkのパラメータを増やすことなく柔軟に渡せる
  • URL生成ロジックが若干複雑になるデメリットはあるものの、拡張性と可読性のバランスが最もよいと判断し採用

議論を行い最終的に決まったパラメータの構成ルールは以下のとおりです。

パラメータキー 役割 値の例
deep_link_value 画面識別子 shopDetail など
deep_link_sub1 職種情報を含むURL https://example.com/kanto/work/100245?param={職種情報}...
deep_link_sub2 エリア情報 kanto
deep_link_sub3 求人詳細ID 100245
deep_link_sub4〜10 未使用 -

非同期での情報取得のタイミング

実装で最も苦労したのは、ディファードディープリンクの情報を非同期で取得するタイミングの制御です。

UDLのコールバックはアプリ起動後に非同期で返ってくるので、アプリの初期化処理との兼ね合いが厄介でした。 スプラッシュ画面にタイムアウト時間を設けて取得できなければコールバックを破棄する案なども検討しましたが、最終的には2つのタイミングでコールバックを受け取る方針に落ち着きました。コールバック受信時点でのアプリの状態に応じて処理を切り替えることで、データの到着が早くても遅くても、スムーズに画面遷移を繋げられるようになりました。

パターン1:スプラッシュ画面中にコールバックが返ってくるケース

  • スプラッシュ画面表示中にUDLから deep_link_value 等を取得
  • 取得した情報を解析し必要情報を端末に保存
  • ウォークスルーをスキップし、指定された求人詳細画面へ直接遷移

パターン2:スプラッシュ画面中にコールバックが返ってこないケース

  • コールバックが間に合わなかった場合、通常通りウォークスルーへ遷移
  • 求人一覧(TOP)画面に到達したタイミングで改めてUDL情報の取得を購読し、deep_link_value 等を受け取る
  • 遷移確認ダイアログをユーザーに表示し、同意が得られれば指定された求人詳細画面へ遷移する

なお、リンクを踏んでから15分以上経過して起動した場合は、UDLによる情報復元は行われず通常のアプリ起動として処理されます。

処理

まとめ

今回の開発を通じて、チームとしてディファードディープリンクを含むディープリンク周りの実装経験を得ることができました。Universal LinksやApp Linksといったシンプルなディープリンクとは異なり、ディファードはSDKの非同期処理やOS・端末ごとの挙動の差異など、実際に手を動かして初めてわかる複雑さがありました。そういった泥臭い部分を含め、チームで一つひとつ検証しながら実装を進めたことは、今後の開発においても大きな財産になると感じています。

また、ネット上には、SDKの繋ぎ込み方の記事はあっても、「URLの構成をどう設計したか」や「起動後の具体的な処理」にまで踏み込んだ事例は意外と少ないと感じました。

この記事が、同じようにディープリンクの実装で悩み、試行錯誤しているエンジニアの方々にとって、解決のヒントや一助となれば幸いです。
最後までお読みいただき、ありがとうございました。