Yappli Tech Blog

株式会社ヤプリの開発メンバーによるブログです。最新の技術情報からチーム・働き方に関するテーマまで、日々の熱い想いを持って発信していきます。

ユニバーサルリンクの仕組みと実装方法

こんにちは、iOSエンジニアの西村です。

iPhoneでネットサーフィン中にウェブサイトのリンクをタップしたら、アプリが起動してコンテンツが表示された経験はないでしょうか?これはユニバーサルリンクという技術で実現されています。
本記事では、ユニバーサルリンクの基本的な仕組み、実装の流れ、そして開発時に役立つ情報について紹介していきます。

こちらは ヤプリ #1 Advent Calendar 2024 21日目の記事になります!


ユニバーサルリンクの仕組み

ユニバーサルリンクは特定のリンクをタップした場合、そのリンクに紐づくアプリがインストールされていればアプリを起動し、アプリがインストールされていなければそのままウェブサイトが開く仕組みです。

このアプリとウェブサイトの紐付けには Apple App Site Association (以下: AASA)ファイルを利用します。

AASA ファイルとは?

App と Web を関連付けるために使用される JSON 形式のファイルです。

どのように関連付けるか、条件を設定することができます。
AASAファイルはユニバーサルリンクとして利用したいWebサーバーに配置します。

AASAファイルは「Universal Links」をはじめ、
「Shared web credentials」や「Handoff」、「App Clips」などの設定もできます。

ユニバーサルリンクを使用するまでの流れ

AASAファイルがどのように利用され、ユニバーサルリンクが実現されるかのは下記の通りです。

  1. 【Webサーバー】 AASAファイルをユニバーサルリンクとして利用したいWebサーバーに配置します。
  2. 【Apple CDN】 Apple CDN が定期的にWebサーバーのAASAファイルをクロールします。
  3. 【iPhone端末】アプリインストール時に、Apple CDN にあるAASAファイルを取得します。
  4. 【iPhone端末】ユーザーがリンクをタップすると、AASAファイルを利用してユニバーサルリンクとして利用するかの判断を行います。


実装の流れ

ユニバーサルリンクの基本的な仕組みが理解できたと思いますので、次に実装について詳しく見ていきます。
今回は例として、example.comのドメインをユニバーサルリンクとして実装する想定で進めていきます。

サーバー側の対応

AASA ファイルの用意

まずは冒頭の説明でもありました、AASAファイルを用意します。
AASAの最小構成は下記の通りです。

{
  "applinks": {
      "details": [
        {
          "appIDs": [ "xxxxx.com.example" ],
          "components": [
            {
              "/": "/*"
            }
          ]
        }
      ]
   }
}
  • applinks
    • どのサービスについて設定するか定義します。
    • ユニバーサルリンクの場合は「applinks」、App Clip の場合は「appclips」などです。
    • 今回はユニバーサルリンクを設定したいので「applinks」としています。
  • details
    • 配列内で、アプリとそのアプリが扱うドメインのユニバーサルリンクのリストを定義していきます。
  • appIDs
    • ユニバーサルリンクの対象となるアプリを指定します。
    • 指定には「Application ID Prefix」と「BundleID」を組み合わせたIDを使用します。
    • {Application ID Prefix}.{BundleID} (「.」区切りでそれぞれ繋げる)
  • components
    • ユニバーサルリンクとして機能するURLのパターン条件を定義します。
    • どのパス、どのクエリ、どのフラグメントでアクセスされた場合にユニバーサルリンクとするのかを定義するパターンは以下表の通りです。

パターンの定義

複数のパターンを components 配列に指定でき、上から順に最初に一致したものが適用されます。

Key 説明 デフォルト値
/ パスとマッチするパターン *(全てにマッチ)
? クエリとマッチするパターンまたは辞書 *(全てにマッチ)
# フラグメントとマッチするパターン *(全てにマッチ)
exclude このパターンにマッチした場合は、ユニバーサルリンクとして機能しないようにするか false
comment パターンに関するコメントが付けられる(動作に影響はなし) -
caseSensitive 大文字と小文字を区別するか true
percentEncoded URLをパーセントエンコードするか true

詳しくは下記のドキュメントをご覧ください。
applinks.Details.Components | Apple Developer Documentation

iOS 14 から新しい書き方ができるようになっています。
新しい書き方と古い書き方を混在して定義すると、ユニバーサルリンクは機能しなくなるため注意が必要です。
TN3155: Debugging universal links | Apple Developer Documentation

AASA ファイルの配置

AASAが用意できたら、ユニバーサルリンクとしたいドメインが設定されたWebサーバーにファイルを配置していきます。

サーバーのルート直下に「.well-known」ディレクトリを作成し、「apple-app-site-association」というファイル名で配置します。
https://example.com/.well-known/apple-app-site-association でAASAにアクセスできる状態にします。

AASAファイルは JSON 形式ですが、.json の拡張子はつけないでください。

アプリ側の対応

Xcode で「Associated Domains」の設定する

Xcode で「TARGETS > Signing&Capabilities > +Capability」から「Associated Domains」を選択して追加します。

追加された「Associated Domains」の Domains に「applinks:<ユニバーサルリンクのドメイン>」を入力します。

「applinks:<ユニバーサルリンクのドメイン>」の末尾に「?mode=developer」を付けると、Apple CDN を介さずにWebサーバーから直接AASAを利用することができます。
この場合、合わせて端末の「設定 > デベロッパ」から「関連ドメインの開発」もONにする必要があります。

ユニバーサルリンクのハンドリング

ユニバーサルリンクでアプリに遷移してきた場合、遷移してきたURLを受け取ることができます。
パスやクエリから情報を取得して、表示したいコンテンツにアクセスさせるなどの対応が可能です。

下記コードを AppDelegate 内で定義して利用します。

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else {
            return false
        }
        // ここでURLに応じて処理をする
        return true
    }
}

App ID の Capabilities で「Associated Domains」を有効化する

https://developer.apple.com/account にアクセスして、App ID 設定画面に移動します。
ここから Capabilities の「Associated Domains」にチェックを入れてSaveします。

必要な方は Provisioning Profile なども適宜変更して下さい。

動作確認

ビルドしたアプリを検証端末にインストールして、ユニバーサルリンクが動作するか確認します。
メモ帳にユニバーサルリンクのURLを貼り付け、タップしてください。設定に問題がなければアプリが開きます。

ブラウザのアドレスバーに直接URLを入力しても、アプリは開きません。

もし開かないようであれば、リンクを長押しして表示されるオプションに「”〇〇”で開く」があるかを確認します。
あればオプションをタップしてアプリへの遷移を有効化し、なければ設定に誤りがある可能性が高いので見直してみるのが良いかと思います。

開発時に役立つ情報

ユニバーサルリンクの有効性確認

端末にインストールされているアプリとそのドメインがユニバーサルリンクとして有効かどうかを、設定アプリから簡単に確認することができます。 「設定 > デベロッパ 」のユニバーサルリンク項目にある「診断」を開き、確認したいドメインのURLを入力します。次の診断結果画面に、有効かどうかの判定が表示されます。

また sysdiagnose の「swcutil_show.txt」ファイルから、設定したユニバーサルリンクのドメインが含まれているかどうかでも判断することもできます。

設定 > デベロッパ 診断 結果

同一ドメインからのユニバーサルリンクについて

タップしたユニバーサルリンクのドメインが、現在表示しているウェブサイトと同一の場合、そのリンクはユニバーサルリンクとして機能しません。これは、ユーザーが引き続きブラウザでウェブサイトを閲覧したいと考えている可能性を考慮した挙動になっています。

もしユニバーサルリンクとしてアプリを開かなければいけない場合は、現在のウェブサイトとは異なるドメインを用意する必要があります。

AASA の更新タイミング

AASA が Apple CDN キャッシュされるタイミング

AASAファイルは公開してから、24時間以内に Apple の CDN にキャッシュされます。

下記URLの最後にユニバーサルリンクで設定しているドメインをつけてアクセスすることで、CDN にキャッシュされている内容を確認することができます。
https://app-site-association.cdn-apple.com/a/v1/<domain>

iPhone 端末の AASA 更新タイミング

アプリの「インストール/再インストール/アップデート」については即時更新されます。

TestFlight からのインストールの場合はすぐに更新されない場合があります。

すでにインストールされているアプリについては、約1週間に一度程度のタイミングで更新されます。
この次回更新タイミングについては、sysdiagnose を利用して確認することができます。

sysdiagnose を開いた後、「swcutil_show.txt」ファイルを開きます。 App ID で該当のリストを検索して、Next Check の項目を見ると次回の更新日時を知ることができます。

Service:              applinks
App ID:               xxxx.com.example
Domain:               example.com
User Approval:        unspecified
Site/Fmwk Approval:   approved
Last Checked:         2023-08-24 10:09:00 +0000
Next Check:           2023-08-18 21:00:19 +0000

最後に

ユニバーサルリンクに対応すると、ウェブとアプリがシームレスに繋がりより良いユーザー体験を提供することができます。意外と実装自体は簡単なので、試してみる価値はあるかもしれないですね!