これは2025年11月時点での情報となります
はじめに
App Links / Universal Linksの仕組みを利用することで、Webブラウザやメールアプリからシームレスにアプリを呼び出し、特定の機能や画面を開くことが可能になります。これはユーザビリティ向上に寄与する機能であり、今回Flutterで開発するアプリに実装しました。本記事では、その実装方法や直面した課題について紹介します。同じ課題に直面した方の参考になれば幸いです。
App Links / Universal Links とは
App Links/Universal Linksは、AppleやGoogleが提供する、ブラウザなどの他のアプリから安全にアプリを呼び出す機能です。ユーザーがリンクをクリックすると、リンクに対応するアプリがスマートフォンにインストールされている場合はそのアプリが起動し、インストールされていない場合はリンクに設定されたURLのWebページが表示されます。アプリがインストールされていない場合、ブラウザでアプリのダウンロードページを表示することが多いと思います。この仕組みでアプリが起動された場合、アプリはURLの内容を確認し、特定の機能を呼び出したり、ページを表示したりすることが可能になります。
(例)
https://example.link.com/home → アプリ起動後にホーム画面に遷移する
https://example.link.com/news?id=1 → アプリ起動後にお知らせのid1の記事の画面を表示する
リンクからアプリを起動する別の方法としてカスタムURLスキームがあります。カスタムURLスキームは、古くから存在するアプリ起動方法で、任意のウェブページや他のアプリから「myscheme://path」のような形式で簡単にアプリが起動できますが、セキュリティ上の問題があります。
-
衝突と横取りの危険性
カスタムURLスキームは、OS側で一意性が保証されていません。複数のアプリが同じカスタムURLスキーム(例:「myscheme://」など)を受け付けるように設定できてしまいます。 -
起動アプリの不確実性
起動リクエストが来た際、OSはどのアプリを起動するかを決定しますが、これは「最後にインストールされたアプリ」や「ユーザーに選択を委ねる」といった挙動になっています。 -
情報漏洩の危険性
この特性を利用し、悪意のあるアプリが正規のアプリを呼び出すためのリクエストを横取りし、URLに付与されている機密性の高い情報(ユーザーID、認証トークンなど)を容易に入手することが可能です。
App Links/Universal Linksは、従来のカスタムURLスキームが抱えていた問題を解決するため、リンク元となるウェブサイトに配置された設定ファイルを介して対象アプリの情報を確認し、悪意のある別のアプリがリンクを乗っ取ることを防ぐ、安全性の高い仕組みとなっています。
実装方法
- 検証環境: Xcode16.4, Android Studio Meerkat, Flutter 3.29.3
- App Links/Universal Linksを利用するために、Flutterライブラリ「app_links」を利用します
- App Links/Universal Linksを試行するサーバ環境としてFirebase Hostingの機能を利用します
サーバ側の対応
App Links/Universal Linksでは、それぞれ設定ファイルを作成し、Apple、Googleから参照できる場所に配置する必要があります。
設定ファイルの作成
作成例: AppLinks
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "example.link.com",
"sha256_cert_fingerprints": ["..."]
}
}
]
- ファイル名「assetlinks.json」を作成します
- このファイルには基本的にApp Linksで起動するアプリの定義を行います
- package_nameとアプリのビルドに使用する署名のハッシュ値を登録し、起動する対象のアプリを特定します
- App LinksのURLについてはアプリソースコード側に定義するため、このファイルには記載しません
作成例: Universal Links
{
"applinks": {
"details": [
{
"appIDs": [ "TEAM_ID.BUNDLE_ID" ],
"components": [
{
"/": "/home/*",
"comment": "Allows any URL with a path that starts with /home/"
},
{
"/": "/exclude/*",
"exclude": true,
"comment": "Disallows any URL with a path that starts with /exclude/"
}
]
}
]
}
}
- ファイル名「apple-app-site-association」を作成します
- このファイルには起動するアプリとUniversal Linksとして動作するURLの定義を行います
- appIDsに登録するIDは全世界で一意なので、起動するアプリが特定されます
- componentsにはUniversal Linksとして動作するURLの定義を行います
設定ファイルの配置
作成した「assetlinks.json」、「apple-app-site-association」ファイルはドメイン認証されているWebサーバ上のRootの「/.well-known」フォルダに配置します。今回はFirebase Hostingの機能を利用し、設定ファイルを配置しています。
ファイル配置パス
・[Root]/.well-known/assetlinks.json
・[Root]/.well-known/apple-app-site-association
アプリ側の対応
Android側設定
AndroidManifestに以下の設定を追加します。
【定義例】
<activity>
...
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<intent-filter android:autoVerity="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:host="example.link.com",
android:pathPrefix="/home",
android:scheme="https"/>
<data
android:host="example.link.com",
android:pathPrefix="/news",
android:scheme="https"/>
</intent-filter>
...
</activity>
- flutter_deeplinking_enabledをtrueに設定(FlutterコードでApp Links処理を行うための設定)
- App Linksの設定受信設定を追加
- autoVerify=“true”が設定されているので、OS側でアプリインストール時に自動的にURLの検証を実施します
- pathPrefixに値を設定すると対応したパスのリンクのみ動作するようになります。上記の例だと「
https://example.link.com/home/*、「https://example.link.com/news/*」のリンクが動作します。
iOS側設定
- Apple Developer Portalでの作業
- AppIDのAssociated Domainsの設定を有効にします
- iOSプロジェクト
- info.plistに「FlutterDeepLinkingEnabled=YES」を設定を追加します(FlutterコードでUniversal Links処理を行うための設定)
- Capabiliteis
- Associated Domainsの設定を追加
- Domainsに「applinks:<ドメイン>」を追加します(今回の例だと「applinks:example.link.com」)
- Associated Domainsの設定を追加
Flutterコードの追加
「app_links」ライブラリを利用したコード例を以下に記載します。
AppLinks appLinks = AppLinks();
applinks.uriLinkStream.listen((Uri? uri) {
if (!mounted) return;
var path = uri!.path;
if (path == "/home") {
// ホーム画面に遷移
} else if (path.contains("/news")) {
// お知らせ画面に遷移
}
});
動作検証と確認結果
-
動作検証方法
Firebase CLIを導入し、Firebaseプロジェクトを作成、Firebase Hosting上にApp Links/Universal Linksの設定ファイルと動作確認のために各リンクを設定したHTMLを配置しました。スマホに動作検証用のアプリをインストールし、スマホのブラウザで配置したHTMLを表示し、リンクをタップしてアプリが起動するか確認をしました。 -
結果
Androidアプリはリンクから起動できましたが、iOSアプリはブラウザ上でページ遷移してしまい、アプリが起動しませんでした。設定ファイルの反映に時間がかかっている可能性もあるため、時間をおいて再度確認をしましたが、動作は変わりませんでした。 -
原因
iOSのブラウザでは、Universal Linksを呼び出すWebサイトとUniversal Linksが同一ドメインの場合は動作しません。同一ドメインの場合は、通常のWebサイト内の遷移が行われるため、WebサイトとUniversal Linksはドメインを分ける必要があります。ユーザ体験を損なわないことを目的にAppleがこういった仕様にしているとのことです。 -
原因への対応と動作確認
Universal Linksと確認用のHTMLのドメインを分ける必要があるということで、簡単に対応するために、もうひとつFirebaseプロジェクトを作成しました。プロジェクトを変えることで、Hostingのドメインも新しく切り出されるため、App Links/Universal Links設定用と動作確認のためのHTML配置用でプロジェクトを分け、それぞれのプロジェクト配下のHostingにファイルを配置しました。
この対応を行うことでiOSアプリについても、リンクからアプリが起動することを確認できました。また正式な開発サーバ環境については、App Links/Universal Links用にサブドメインを切り、対応しました。
そのほかの発生した課題と対処
実装を進めるうちに以下の課題も発生したため、概要を記載します。
-
go_routerの利用
Flutterでの画面遷移にgo_routerを利用することは多いと思います。今回も開発を進める中で画面遷移にgo_routerを利用するようにしたところ、App Links/Universal Linksでアプリを起動した際に、go_routerで該当するURLが定義されていないとエラーが出るようになりました。調査したところ、App Links/Universal LinksでのURL呼び出しについてはgo_routerが自動で検知、対応する仕様であることがわかりました。元々想定していたapp_linksでのリンク起動処理も行うと2重で処理が行われてしまうため、app_linksは利用せず、go_router側に起動処理を実装することで解決しました。 -
Basic認証
正式な開発サーバ環境に仕組みを導入する際に、サーバにBasic認証がかかっており、外部からApp Links/Universal Linksの設定ファイルが参照できず、リンクが動作しなくなりました。これについては、設定ファイルのパスに関してBasic認証がかからないように対応を行い、解決しました。
おわりに
App Links/Universal Linksが動作するまでにいくつか課題はありましたが、無事にブラウザやメーラアプリからアプリを呼び出すことができました。App Links/Universal Linksは、他のアプリからシームレスにユーザをアプリの適切な機能へ誘導できる、非常に便利な機能です。本記事の内容が、これから実装に取り組む方のお役に立てば幸いです。
We Are Hiring!