107
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

iOS14でUniversal Links対応することになった人が見る記事

Last updated at Posted at 2021-05-25

Universal Linksとは

urlからアプリをシームレスに起動できる機能のことです。

ディープリンクとの大きな違いは確認のダイアログなしでアプリに遷移できることです。
UX上の利点や特徴などは以下の記事で詳しく解説されています。

モチベーション

今回Universal links対応をiOS側の担当者として調査から実装までやりました。
しかしこれが曲者で、

  • OSごとに対応方法が異なっていたり、
  • 事前に対策を打たないと特定の環境で動作確認できなかったり
  • 挙動にOS差分があって中々原因に気づけなかったはまりどころがあったり、

などなど結構ハマってしまいました。。。

そこで、同じようにiOS14からUniversal Linsに対応する方が苦しまなくてよいようにするため、以下の情報を記事にしました。

  • iOS14時点のUniversal Links対応手順
  • Universal Linksの実装に役立つテクニック

iOS14からのUniversal Links対応手順

1.apple-app-site-associationの作成

apple-app-site-associationとは、iOSアプリへの遷移の資格情報を記載するファイルです。

注意点

  • .jsonなどのファイル形式を指定しないこと。(ただのapple-app-site-association として作成する。)
  • iOS13以前と以降で書き方が異なるが、13以前のOSでUniversal Linksを使用したい場合は旧形式で記載すること。

記載方法

それでは中身を見ていきます。ざっくり以下の要素を記載する必要があります。

key valueに記載するもの
appIDs [teamID] + "." + [BundleID]を配列で記載 ABCDE12345.com.example.app
components 遷移を許可するパスの情報を記載 後述
webcredentials [teamID] + "." + [BundleID]を配列で記載 ABCDE12345.com.example.app
{
  "applinks": {
      "details": [
           {
             "appIDs": [ "ABCDE12345.com.example.app", "ABCDE12345.com.example.app2" ],
             "components": [
               {
                  "#": "no_universal_links",
                  "exclude": true,
                  "comment": "Matches any URL whose fragment equals no_universal_links and instructs the system not to open it as a universal link"
               },
               ...

   },
   "webcredentials": {
      "apps": [ "ABCDE12345.com.example.app" ]
   },

   ...

}


teamIDとbundleIDがBuildConfigurationごとに異なっている場合は、それら別に記載する必要があるので注意です。
僕はRelaseとDebugのteamIDを変える対応が漏れていて爆死しました。
apple-app-site-associationの記載ミスはファイルを修正してまた配置し直す作業が発生するため、修正時のリードタイムが大きくなります
入念にレビューしてもらってから配置しましょう。

components内の項目

key valueに記載するもの
/ 遷移を許可するパス /foo
# URLのフラグメント no_universal_links
? パラメータ { "articleNumber": "????" }
comment コメント(遷移には影響しない) "fooへ遷移を許可します。"
"components": [
    {
        "#": "no_universal_links",
        "exclude": true,
        "comment": "Matches any URL whose fragment equals no_universal_links and instructs the system not to open it as a universal link"
    },
    ...
]

覚えておきたいのは以下のポイントです。

  • "/"で記載する遷移を許可するパスはドメイン以降のパスを指定する。
    • 例えばhttps://example.com/fooの場合は/fooのみ記載する。
  • 全体で正規表現が使用できる。
    • ?はランダムな1文字を許可
    • *はランダムな文字列を許可

パラメータの詳しい指定方法は2019年のWWDCの動画が参考になります。

2.apple-app-site-associationをサーバーに配置する。

1で作成したapple-app-site-associationをUniversalLinksを起動させるURLのドメインのサイトに配置します。

配置するパスは以下のように.wellknown配下を指定する必要があります。

https://<fully qualified domain>/.well-known/apple-app-site-association

こうして配置されたapple-app-site-associationはアプリのインストール時にアプリ側から取得され、UniversalLinksに遷移の可否判定に使用されます。

iOS14以降のapple-app-site-association取得上の注意

iOS13までは.well-knownのパスにアプリ方直接apple-app-site-associationを取得しに行っていました。
しかしiOS14から取得経路が変わり、AppleのCDNを経由してapple-app-site-associationが取得されるようになりました。
図にすると以下にようになります。

この変更による問題点は、AppleのCDNがapple-app-site-associationを取得できない場合、UniversalLinksが機能しない ということです。

例えばStaging環境でIP制限を設けている場合だと、AppleのCDNからのアクセスを弾いてしまい、アプリがCDNからapple-app-site-associationを取得できず、UniversalLinksが機能しなくなってしまう事態が発生します。

これは後述するAssociated Domainsの設定でドメインにmodeを指定することで回避できます。
mode=developerを指定することで開発者の端末であればCDNを経由せずに直接ドメインの.well-knownにJSONを取得しに行くことができます。

Staging環境でもUniversalLinksのテストをしたい場合は、上記の設定によってテストが可能です。


※ ドキュメントにはmode=developerを指定する場合、「使用するデバイスでオプトイン」する必要があると記載されています。
なんのことかというと設計アプリのデベロッパ画面の以下の項目です。

 

(Appleのドキュメントはこういう細かいとこの記載が分かりにくいと思う。。。)


iOS14でのUniversalLinksの変更点については以下を参考にさせていただきました。

公式によるiOS14でのUniversalLinksの変更点の説明は2020年のWWDCの動画で確認できます。

3.Associated DomainsのCapabilitiesを追加

CapabilitesにAssociated Domainsを追加します。

ここには2でサーバーに配置したapple-app-site-associationを取得しにいくドメインを記載します。

以下の形式で追加します。

<service>:<fully qualified domain>

2で記載したCDNを経由せずにドメインにapple-app-site-associationを取得しに行く設定にするためには、ここでmodeを指定します。

Releaseビルドではmode=developer付きのstagingドメインを記載する必要はないので、BuildConfigurationごとにentitlementsを分ける対応になりそうです。

ドメイン記載時の注意

apple-app-site-associationを配置したドメインとAssociatedDomainsの記載は一致している必要があります。

例えばhttps://www.example.com/.well-known/apple-app-site-associationを配置したとします。

この時**wwwなしのapplinks:example.comを記載していた場合、iOS14ではUniversalLinksが機能しません**。

iOS13まではexample.comのようなルートのドメインのみapplinksに指定すれば、apple-app-site-associationの配置先がwww.example.comのようなサブドメインでも取得されました。
しかしiOS14ではapple-app-site-associationの配置先のドメインと一致しているAssocaitedDomainsがないとUniversalLinksは機能しなくなっていました。
ドキュメントの通り、ルートのドメインと遷移を許可したいサブドメインを指定していれば基本発生しない事象ですが、仮にiOS13まではルートのドメインのみAssociatedDomainsに指定してサブドメインからapple-app-site-associationを取得していた場合、iOS14以降は機能しなくなる恐れがあリますので注意が必要です。

4.UniversalLinks起動時のハンドリングを実装

UniversalLinksの起動を許可するパスのURLを開いた時、AppDelegateの以下のDelegateメソッドが呼ばれます。


func application(
    _ application: NSApplication,
    continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([NSUserActivityRestoring]) -> Void
) -> Bool {

    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, // ブラウザからの遷移か確認
        let incomingURL = userActivity.webpageURL, // URLを取得
        let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true) else {
        return false
    }
  
    return true
}

ここでtrueを返せばアプリが開きます。
受け取ったパスが遷移を許可したくないパスかどうか確認するにはuserActivityからURLを取得してそのURLを確認します。
URLにしてしまえばあとはComponentからパスを取得するなりパラメータを取得するなりなんなりできます。

ここまで対応すればUniversalLinksによるアプリの起動が可能になっていると思います。

UniversalLinksの実装に役に立ったテクニック

SimulatorでUniversalLinkの動作を確認する。

アプリをsimulatorにインストールした状態で以下のコマンドを実行します。
ブラウザで直接URLを書き込むことで確認もできますが、コマンド一つでPC上から確認できるので、こちらの方が確認が簡単でした。

xcrun simctl openurl booted {URL}

AppleのCDNから取得できるapple-app-site-associationを確認する。

これは以下のエンドポイントに対してGETのリクエストを送ることで確認できます。

GET https://app-site-association.cdn-apple.com/a/v1/{確認したいdomain}

こちらを参考にさせていただき、実際にapple-app-site-associationが取得できることを確認しました。

※ヘッダーにHOST=app-site-association.cdn-apple.comの指定が必要とのことですが、なしでも取得できました。

CharlesやProxyManでapple-app-site-associationの取得をキャプチャする際の注意

AppleのCDNに対するリクエストはCharlesなどの通信デバッグツールを噛ませるとapple-app-site-associationの取得に失敗します。

simulatorやデバイスに対して、CharlesのRoot証明書を信頼させていてもこれは不可能でした。。。
僕はAppleのCDN経由からapple-app-site-associationを確認したく、何度もCharlesを噛ませながらリクエストを送っていたんですが、この時間は徒労に終わってしまいました。
(もし同様に通信デバッグツールを使ってもCDNからのapple-app-site-associationをキャプチャできた方がいましたら環境や設定など教えていただけると嬉しいです。)

まとめ

Universal Links対応は開発の作業自体は少ないものの、サーバーチームとの連携、概念の理解、OSごとの対応手順差分・挙動差分などなど、ハマるポイントが結構あると思いました。
今後UniversalLinksに対応する方が僕と同じようなポイントでハマってしまわないようにこの記事がお役に立てば幸いです。

107
69
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
107
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?