LoginSignup
15
9

More than 1 year has passed since last update.

Universal Linksを用いたiOSのDeep Link対応

Last updated at Posted at 2021-12-23

iOS 13以降の Universal Links について紹介します。

Universal Linksとは

iOS 9からサポートされて、Apple が正式に提供したディープリンク技術です。
簡単にいうと、通常の HTTP リンクで設定すると、その URL を踏んだ時に、端末のデフォルトブラウザで Web サイトを開く代わりに、アプリを起動してページへ遷移することができます。
URL スキームと似ている機能ですが、主に下記の違いがあると思います。

  • Custom URL スキームが不要です。 hoge:// みたいなスキームではなく、 通常の HTTP リンクのスキーム https:// でアプリが起動できるので、 Web サイトとアプリ両方同じURLで遷移できます。
  • Web サーバーにアプリの起動条件を記載する apple-app-site-association を配置する必要がある。URL のパスやパラメータなど設定することで、アプリに遷移させたいリンクのみ指定可能です。
  • 直接にブラウザにURLを入力した場合、アプリが起動しません。同一ドメイン内で Web 回遊中の時にも、アプリが起動しません。

アプリ側の設定

Associated Domainsの設定

  • Signing & Capabilities の + Capability をクリックする
    スクリーンショット 2021-12-22 16.22.22.png

  • Associated Domainsを選択する
    スクリーンショット 2021-12-22 16.24.53.png

  • Associated Domains という項目が追加されてここでドメインを追加する
    スクリーンショット 2021-12-22 16.33.28.png

ドメインを追加する

フォーマットは applinks:<fully qualified domain> になります。サブドメインで対応する場合、サブドメインを含めて設定する必要があります。

Alternate Mode

iOS 14から、OSは直接 Web サーバーからではなく、 Apple の CDN を経由して apple-app-site-association をダウンロードします。 CDNへの反映は最大24時間かかるらしいです。また、IP 制限がある開発環境で、CDN からのアクセスできない場合もあるので、開発の際、Alternate Modeを developer に設定すると、CDNを経由せず 直接 Web サーバーから apple-app-site-association をダウンロードできます。
先ほど追加したドメインにパラメータ mode=developer を追加するだけで設定完了です。 applinks:example.com?mode=developer のようにになります。
この場合、テスト端末の Associated Domains Development を有効にする必要があります。
IMG_740D72C118F8-1.jpeg

なお、 MDM アプリの場合、 mode= managed で設定すると、 Apple の CDN も回避できるようです。
詳細は Associated Domains Entitlement ご確認ください。

ipa で確認する

アプリを配布する際、 ipa の設定は大丈夫だろう?と思ったら ipa を展開して確認しましょう。

unzip -d work hoge.ipa
codesign -d --entitlements - work/Payload/hoge.app

entitlements で設定された値が確認できます。
スクリーンショット 2021-12-22 19.58.00.png

アプリ側の設定がこれで完了です。

apple-app-site-association

iOS 13から apple-app-site-association のフォーマットが新しくなりました。これから紹介する apple-app-site-association は、 iOS 12 以前では発動しないので、ご注意ください

apple-app-site-association の書き方

Apple の公式ドキュメント applinks のサンプル json を見てみましょう。

{
    "applinks":
    {
        "details":
        [
            {
                "appIDs":
                [
                    "ABCDE12345.com.example.app",
                    "ABCDE12345.com.example.app2"
                ],
                "components":
                [
                    {
                        "#": "no_universal_links",
                        "comment": "フラグメント#no_universal_linksが付いている場合Universal Links発動しない",
                        "exclude": true
                    },
                    {
                        "/": "/buy/*",
                        "comment": "パスが/buy/からスタートしたURLはUniversal Links発動する"
                    },
                    {
                        "/": "/help/website/*",
                        "comment": "パスが/help/website/からスタートしたURLはUniversal Links発動しない",
                        "exclude": true
                    },
                    {
                        "/": "/help/*",
                        "?":
                        {
                            "articleNumber": "????"
                        },
                        "comment": "パスが/help/からスタートしたURL、かつパラメータarticleNumberが4桁の文字列の場合Universal Links発動する"
                    }
                ]
            }
        ]
    }
}

全体の説明

項目名 説明
applinks 固定オブジェクト
applinks.details Universal Linksのアプリ情報と発動条件の配列
applinks.details.appIDs 対象アプリの情報の配列  
フォーマットは <team id>.<bundle id> になる
applinks.details.components Universal Linksの発動条件の配列 
優先順位は上から適用される

components の説明

項目名 説明
"#": "" フラグメントの条件
"/": "" URLパスの条件
"?": { "": "" } パラメータの条件
"exclude": true trueで設定すると該当条件は発動しない
"comment": "" コメント
必須ではない

components の各種条件の組み合わせで細かく発動条件が設定できます。

SubstitutionVariables

iOS 13.5 から SubstitutionVariables という、代替変数の機能が追加されました。
$(alpha)$(digit) など固定の変数があり、こちらのように自前の定義を追加することも可能です。

{
  "applinks": {
    "substitutionVariables": {
      "food": [ "burrito", "pizza", "sushi", "samosa" ]
    },
    "details": [{
      "appIDs": [ ... ],
      "components": [
        { "/" : "/$(lang)_$(region)/$(food)/" }
      ]
    }]
  }
}

サーバー側の設定

apple-app-site-associationを作成したらそれを Web サーバーに配置します。ここではいくつのポイントがあります。

  • apple-app-site-association ファイルは拡張子なしで、 MIME タイプが application/json になっていること
  • .well-known ディレクタリー直下に apple-app-site-association ファイルが配置されていること
  • リダイレクトなしでhttps://<fully qualified domain>/.well-known/apple-app-site-association へアクセスできること

疎通確認

apple-app-site-association の設置が終わったら、正常にアクセスできるか確認しましょう。App Search API Validation Tool から確認も可能ですが、こちらの curl を叩くと CDN への反映状況などより詳細な情報を入手できます。

curl -v https://app-site-association.cdn-apple.com/a/v1/example.com

キャッシュの生存時間があとどのくらいあるかなどの情報も確認できるので、疎通確認の際非常に役立ちます。

スクリーンショット 2021-12-22 20.16.52.png

疎通確認が成功したら、設定したURLを踏むとアプリが起動できると思います。
ただし、 iOS では、アプリの新規インストールアップデート、またはシステムのタイミング(平均的に1週間1回らしい)でapple-app-site-associationが更新されるので、端末に最新のapple-app-site-associationが反映されるまでタイムラックがあります。

アプリの実装

最後に、アプリ側で受け取ったURLを処理します。
AppDelegateapplication(_:continue:restorationHandler:) が最初の入り口になります。

    func application(_ application: UIApplication, continue continueUserActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        // appLinksの場合、activityTypeがNSUserActivityTypeBrowsingWebになる
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return false }

        if canHandle(url) {
            // do something
        } else {
            // 処理できないURLはブラウザで開く
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
        }

        return true
    }

以上、 Universal Links の紹介でした。

15
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
9