UniversalLinksの設定方法とハマりポイント

# ユニバーサルリンクとは
Webサイトのリンクをクリックした時にiOSアプリを起動させるAppleが提供している機能のことです。
SafariまたはWebサイトを経由することなく、アプリ内のコンテンツに直接リンクすることができます。アプリがインストールされている場合は、アプリでユニバーサルリンクが開きます。インストールされていない場合は、デフォルトのWebブラウザでリンクが開きます。[^1]

# ユニバーサルリンクの仕組み
XcodeのAssociated Domainsで設定したドメインのapple-app-site-association(以下、AASA)が、アプリの初回ダウンロード時・アップデート時にAppleのCDNからダウンロードされることで、AASAに記載されている特定のパスからのみアプリを起動可能になるというものです。
図にすると以下のような感じでしょうか。
![スクリーンショット 2024-03-13 22.51.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425368/4ae74106-5f40-c242-c9e6-293a37527946.png)

# ユニバーサルリンクを使うための準備
ユニバーサルリンクを使うためには、サーバー側とアプリ側の両方の対応が必要になります。
それぞれに分けて説明します。

### サーバー側の準備
1. apple-app-site-associationというファイル名のJSONファイルを用意します。このファイル名は固定です。apple-app-site-association以外の名前にしてしまった場合は、ユニバーサルリンクが正しく動作しません。AASAの書き方は[Supporting associated domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains)を参照してください。[^2]
WWDCのセッション動画でもAASAの書き方についてキャッチアップできます。[^3][^4]
1. AASAを以下のURLにホストする
```
https://example.com/.well-known/apple-app-site-association
```
ファイルをサーバーに配置するときは、拡張子はつけずに格納してください。
また、AASAに対するアクセスについては、IP制限を解除する必要があります。
理由は、Appleが管理するCDNからAASAを取得するため、外部に公開する必要があるためです。
しかし、大人の事情で開発環境だけはIP制限を外したくないケースもあると思います。
IP制限がかかっている状態でユニバーサルリンクを実現する方法は、アプリ側の対応で紹介します。

### アプリ側の準備
1. Associated Domainsの追加
```applinks:<fully qualified domain>```の形式でドメインを指定します。
1. ユニバーサルリンクでアプリ起動した時のハンドリング
UniversalLinks経由でアプリ開いた時は、AppDelegateの以下のDelegateメソッドが呼ばれるので、任意の処理をここで書きます。
```AppDelegate.swift
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
            let url = userActivity.webpageURL else {
        return true
    }
    // 任意の画面に遷移させるなどの処理を書く
    return true
}
```
### IP制限がかかっていてもユニバーサルリンクを有効にする方法
develperモードを有効にすることで、開発環境でユニバーサルリンクを有効にできます。
```applinks:<fully qualified domain>?mode=developer```のようにドメインの後ろに```?mode=developer```をつけることで、develperモードを有効にすることができます。

以上の工程でユニバーサルリンクを実現させることができます。
ここまで一見、簡単そうに見えるのですが実装にするにあたってハマったポイントがあったので紹介します。

# ユニバーサルリンクを実装するにあたってハマったポイント
## Safariのアドレスバーに直接URLを入力してもユニバーサルリンクは機能しない
私はアドレスバーにURLを打ち込んで動作検証しており、全くユニバーサルリンクが機能しないという現象に遭遇しました。
こちらはドキュメントにガッツリ記載があり、Safariのアドレスバーに直接URLを打ってもユニバーサルリンクは動作しないよと書いてありました。（しかも日本語で！）
> Safariのアドレスバーに直接URLを入力しても、アプリが開くことはありません。Safariでは、この操作はダイレクトナビゲーションとして処理されます。ユーザーがドメインに直接移動してからそのドメインにとどまっている間は、サイトにはアプリを開くためのバナーが表示されます。[^1]

ドキュメントはちゃんと読みましょう。。。

## 配布用証明書で配るアプリはdevelperモードを有効にすることができない
「develperモード」という名前がついているので、言われてみればそうだよなという感じですね。
開発用の証明書でビルドされたアプリのみdevelperモードを有効にすることができます。
TestFlight経由で配布するアプリや本番用のアプリは、IP制限を外さないとユニバーサルリンクが機能しません。

## TestFlightで配布する場合は、すぐにAASAが端末にダウンロードされる訳ではない
AppStore経由でダウンロードしたアプリは、ダウンロード時やアップデートにAASAも一緒にダウンロードされるのですが、TestFlight経由に至ってはタイムラグがあるみたいです。
こちらに関しては調べてもこれと言った対策は特に見当たらないので、気長に待つしかなさそうです。
私が仕事でTestFlight配信したアプリを確認したところ、1時間くらいかかりました。

Developer ForumsでもTestFlight経由のアプリでユニバーサルリンクが動作しないという書き込みがあります。[^5]

## Safari以外がデフォルトブラウザの場合はユニバーサルリンクが機能しない可能性がある
私が手元で検証した感じだと、Safariをデフォルトブラウザにしてユニバーサルリンク起動→その後Safari以外をデフォルトブラウザにしてユニバーサルリンク起動の流れだと機能しました。
しかし、最初からSafari以外をデフォルトブラウザにしていると、特定ドメイン以外はユニバーサルリンクが機能しないようにOSが制御するようです。
> Apps that have the com.apple.developer.web-browser managed entitlement may not claim to respond to Universal Links for specific domains. The system will ignore any such claims. Apps with the entitlement can still open Universal Links to other apps as usual.[^6]

## 同一ドメインのブラウザ遷移だとユニバーサルリンクが機能しない
例えば、```https://example.com```というWebサイトを閲覧中に```https://example.com```をユニバーサルリンクの対象に設定しても動作しません。
> すでにSafariで閲覧している場合にユニバーサルリンクを使用してアプリを開くには、別のサブドメインを使用します。これが必要になる理由として、アンケートに回答する場合やサインインを実行する場合などがあります。ユニバーサルリンクのドメインが前のナビゲーションと同じである場合、Safariでは、ユーザーがブラウザでのナビゲーションを継続する意向であると推測します。[^1]

[^1]: [TN3155：ユニバーサルリンクのデバッグ](https://developer.apple.com/jp/documentation/technotes/tn3155-debugging-universal-links/)
[^2]: [Supporting associated domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains)
[^3]: [What's new in Universal Links - WWDC 2020 -](https://developer.apple.com/videos/play/wwdc2020/10098/)
[^4]: [What's New in Universal Links - WWDC 2019 -](https://developer.apple.com/videos/play/wwdc2019/717/)
[^5]: [Universal Link not working on TestFlight](https://forums.developer.apple.com/forums/thread/108339)
[^6]: [Preparing your app to be the default web browser](https://developer.apple.com/documentation/xcode/preparing-your-app-to-be-the-default-browser#Adhere-to-browser-restrictions)