はじめに
こんにちは。Androidエンジニアの佐藤です。
今回は、Adjust SDKを用いた「Deferred Deep Link(遅延ディープリンク)」の実装について紹介します。
実際に導入を進めるにあたり、色々と調査を行いましたが、意外にも具体的な実装に関する情報が少なく、手探りでの対応が必要でした。
そのため今回は、同じ実装をする方が迷わないよう、「実務でそのまま使えるHow-to」 として、設計からコードまで詳しく解説します。
この記事は、株式会社asken (あすけん) Advent Calendar 2025 の2日目の記事です。
まずは整理
本題に入る前に、各種リンクに関して、情報を整理します。
1. ディープリンク (Custom URL Scheme)
古くからある、アプリ独自のスキームを使用する方法です。
- 形式:
myapp://path/to/content - インストール済み: アプリが起動し、指定画面へ遷移します。
- 未インストール: エラーになるか、何も起きません。
- 課題: アプリが入っていないユーザーにとっては「壊れたリンク」となってしまい、体験が非常に悪いです。
2. Universal Links (iOS) / App Links (Android)
現在OS標準として推奨されている、HTTP(S)スキームを使用する方法です。
- 形式:
https://myapp.com/content - インストール済み: アプリが起動し、指定画面へ遷移します。
- 未インストール: Webブラウザで該当のWebページが開きます(フォールバック)。
- 課題: エラーにはなりませんが、Webページからストアへ誘導してインストールさせた場合、「元々どのページを見ていたか」という情報はインストールプロセスで失われます。 そのため、初回起動時はトップページ等が表示されることになります。
3. Deferred Deep Link (遅延ディープリンク)
今回採用したアプローチです。Adjustなどの計測ツールの仕組みを利用して実現します。
- 形式:
https://myapp.go.link/...?adj_t=...(Adjustの場合) - インストール済み: アプリが起動し、指定画面へ遷移します。
- 未インストール:
- Adjustサーバーを経由して、ユーザーをストアへリダイレクト。
- ユーザーがアプリをインストール&初回起動。
- Adjust SDKがリンク情報をサーバーから取得し、指定画面へ遷移。
- メリット: インストールという壁を越えて、ユーザーが見たかったコンテンツを確実に届けることができます。
課題:ディープリンクの限界とユーザー体験
上記で整理した通り、既存のディープリンク(Deep Link)は、アプリがすでにインストールされている場合に特定の画面へ誘導する便利な機能です。
しかし、アプリがインストールされていないユーザーがリンクをクリックした場合、アプリは起動せず、インストールも行われません。
手動でアプリを検索・インストールしたとしても、ユーザーは本来見たかった画面の情報(コンテキスト)を失った状態でトップ画面からスタートすることになります。
目的:ユーザー体験の向上
この課題を解決するのが、Deferred Deep Linkです。
技術選定:なぜ Adjust SDK なのか
この「遅延ディープリンク」を実現するツールとして、かつては Firebase Dynamic Links (FDL) が主流でしたが、FDLは2025年8月にサービス終了となりました。これにより、多くのアプリ開発者が代替手段への移行を迫られています。
代替案としては、Adjust、AppsFlyer、Branch などが挙げられますが、今回私たちは Adjust を採用しました。
理由はシンプルで、「askenでは以前から広告計測のためにAdjustを導入していたから」 です。
- 工数削減: すでにSDKが入っているため、新たなライブラリの導入コストが不要。
- データ一元化: 広告流入とディープリンク流入を同じプラットフォームで計測できる。
既存のアセットを有効活用し、ミニマムな実装で最大の効果(インストール後のUX向上)を狙うため、Adjustの遅延ディープリンク機能を利用することにしました。
- リンクをクリック(アプリ未インストール)
- アプリストアへリダイレクト
- アプリをインストール&初回起動
- 初回起動後、自動的に目的の画面(例:スペシャルコンテンツ画面)へ遷移
この仕組みは、ユーザーの離脱を防ぎ、コンバージョン率の改善に直結します。
Deferred Deep Link の全体処理フロー
Adjust SDKの遅延ディープリンクは、以下の手順で実現されます。
| Step | 処理内容 | 詳細 |
|---|---|---|
| 1. リンククリック | ユーザーが Adjust Link (https://myapp.go.link?adj_t=...) をクリック。 |
アプリ未インストール状態。 |
| 2. ストア遷移 | Adjustがユーザーを OS に応じたストアへリダイレクト。 | Adjustがクリック情報とデバイス情報を一時的に保持。 |
| 3. インストール&起動 | ユーザーがアプリをインストールし、初回起動する。 | |
| 4. リンク取得 | アプリ起動時、Adjust SDK が保持されていた遅延ディープリンク情報を取得。 | この処理は初回起動時のみ実行されます。 |
| 5. リンク保存 | アプリ側がリンク情報を受け取り、ローカルストレージに一時保存。 | ログインや初期設定完了後の処理に備えます。 |
| 6. ログイン/登録 | ユーザーがアプリ内のログインまたは新規会員登録を完了。 | |
| 7. リンク処理&遷移 | 登録完了後、保存されていたリンクを読み込み、指定の画面へ遷移させる。 | ダミー例: スペシャルコンテンツ、キャンペーンページなど。 |
| 8. データクリア | 画面遷移処理が完了した直後、保存したリンク情報をクリア。 | 不正な再実行を防ぐため、重要なステップです。 |
実装方針: Deep Link 永続化
Adjust SDKから取得したリンクを、ログイン完了後など別のタイミングで処理するために、永続化(データの保存) が必要になります。
以下、Androidベースにはなりますが、実装方針を解説します。 (※ iOSも同様にUserDefaultsなど使って実現することが可能です。)
1. データ永続化の設計
今回の実装では、アプリプロセスが終了してもデータが保持され、かつ簡単にアクセスできる SharedPreferences を採用します。
- ストレージ:
SharedPreferences - ファイル名:
deferred_deep_link_prefs - キー名:
deferred_deeplink - 値: URIの文字列表現 (例:
"myappscheme://special/123")
2. アプリ内での役割分担(例)
| ファイル名 | 役割 |
|---|---|
App.kt |
Adjust SDKからのディープリンク受信と、ハンドラーへの処理委譲。 |
DeferredDeepLinkManager.kt |
SharedPreferencesの操作(保存・取得・削除)を担う。ディープリンク永続化のロジック集中管理。 |
MainActivity.kt |
ログイン/新規登録完了後、保存されたリンクの有無をチェックし、画面遷移処理をキックする。 |
3. Deep Link の受け取りと保存 (App.kt / DeferredDeepLinkManager.kt)
App.kt で Adjust SDKからリンク情報を受け取った直後、DeferredDeepLinkManager を使ってすぐに SharedPreferences へ保存します。
想定されるリンク形式(このあたりは決めの問題ですね)
- パス形式:
myapp://feature/123 - クエリパラメータ形式:
myapp://feature?id=123
4. 処理後のデータクリア
不正なループや意図しない再実行を防ぐため、リンク情報は必ずクリアします。
- 処理成功時:
MainActivityで画面遷移処理が完了した直後。 - エラー発生時: ディープリンク処理中に例外が発生した場合。
Adjust 側の設定:カスタムリンクの作成
アプリ側の実装と並行して、Adjust管理画面でカスタムリンクを作成する必要があります。
カスタムリンクの作成:
用途に応じて新しいカスタムリンクを作成し、一意のトークンが発行されます。

URLの組み立て:
このトークンを元に、ユーザーにクリックさせるための最終的なURLを組み立てます。
- ベースURL: カスタムドメイン (例:
https://myapp.go.link) - トークン:
adj_t=カスタムリンクトークン(例:adj_t=xxxxxx) - ディープリンク本体:
deep_link=URLエンコードされたアプリ内URI(例:deep_link=myappscheme%3A%2F%2Ffeature%2F123)
完成形例:
https://myapp.go.link?adj_t=xxxxxx&deep_link=myappscheme%3A%2F%2Ffeature%2F123- ベースURL: カスタムドメイン (例:
実装例
Adjustからは、以下のような実装にて値を取得可能です。
取得した値は一次保存しておいて、アプリの任意のタイミングで抜きだして、特定画面へ遷移できるように実装します。
Android
// Deferred Deep Link のリスナーを設定 config.setOnDeferredDeeplinkResponseListener { uri: Uri? -> if (uri != null) { // 1. 取得したDeep Linkをログ出力 Log.d("Adjust", "Deferred Deep Link received: $uri") // 2. 永続化処理(SharedPreferencesへ保存) // ここでログイン後の遷移用にURLを保存しておく DeferredDeepLinkManager.save(applicationContext, uri.toString()) } // 3. falseを返すことで、SDKによる自動遷移を防止する return@setOnDeferredDeeplinkResponseListener false }
iOS
// Deferred Deep Link を受信した時に呼ばれるデリゲートメソッド func adjustDeeplinkResponse(_ deeplink: URL?) -> Bool { guard let url = deeplink else { return false } // 1. ログ出力 print("Deferred Deep Link received: \(url.absoluteString)") // 2. 永続化処理 (UserDefaultsなどへ保存) // ここでログイン後の遷移用にURLを保存しておく DeferredDeepLinkManager.shared.save(url: url) // 3. SDKによる自動遷移を防止するために false を返す return false }
テスト手順
Deferred Deep Link はアプリ初回起動時のみに動作する特性上、テスト前の準備が非常に重要です。
- アプリのアンインストール: 端末から対象アプリを完全に削除。
Adjust テストコンソールでデバイス削除: Adjustが保持しているデバイス情報をリセットします。
- 注意点: 削除後、1分程度待ってから次のステップに進むことを推奨します。

広告IDのリセット:
Android/iOS: 端末設定またはOSの仕組みを利用して、広告IDをリセットします。
Android:設定 → プライバシー管理 → 広告 からリセット

iOS:「アプリからのトラッキング要求を許可」をOFFにして停止 → 再度有効化でIDFAがリセットする

端末にAdjust Insightsというアプリを入れて確認することができます

Adjust Linkをタップ:
ブラウザで作成した Adjust Link をタップし、ストアにリダイレクトされることを確認。
本番ビルドでインストール:
Adjustの設定が本番環境のみである場合、必ず本番設定でビルドされたアプリ (TestFlight/AppTesterなど) をインストールします。
- ここは、それぞれの開発環境で若干異なります
起動&動作確認:
アプリを起動し、ログインまたは新規登録を完了した後、指定された画面に自動遷移することを確認します。
まとめ
この実装により、未インストールユーザーへのプロモーション効果を最大化し、シームレスなアプリ導入体験を提供できるようになります。
この Deferred Deep Link の仕組みを応用して、より複雑なマーケティングキャンペーンや、特定機能に対する離脱防止など、対応を検討してみてはいかがでしょうか。
採用について
askenでは、様々な技術要素に対してチャレンジしたいエンジニアを絶賛募集中です。
まずはカジュアルにお話しできればと思いますので、ぜひお気軽にご連絡ください!
https://hrmos.co/pages/asken/jobs