👌

Univeral Linksの設定ファイル、apple-app-site-associationをローカルでテストする方法

2024/10/29に公開

ガラパゴス、サービス開発パートナー事業部のiOSエンジニアのコードヒヨアンです。久しぶりです!
最近ユニバーサルリンクを定義したときに、うまくいかなくて効率的にテストしながら設定を行う方法を模索していたところ、Appleのドキュメントが少々わかりにくい部分があったため、自分なりにまとめてみました。
皆さんの参考になれば幸いです!

AASA

UUniversal Linksを設定する際には、iOSアプリと連携するWebサイトにapple-app-site-association(以降、AASA)という設定ファイルを設置する必要があります。このファイルは、アプリとWebサイト間でリンクを適切に処理するためのもので、アプリが特定のURLにマッチするかどうかを定義します。アップルのドキュメンテーションで書き方の仕様がなく、例で説明されているだけですので、ちゃんと書けたか不安になりますね。

そのため想定通りに動くかの確認が重要ですが、設定が正しいかどうかをテストしたい時にどうしますか?
サーバーに設定ファイルをアップロードし、端末でテストURLを入力してアプリへの遷移を確認する方法もありますが、手間が多く、時間もかかります。

swcutil

そこでAppleが便利なツールを用意してくれています:swcutil。
この記事では簡単にこのツールを紹介したいと思います。

Appleのドキュメンテーションはこちらです。

swcutilを使用すると、主に次の2点を確認できます:

  • JSONを正常にダウンロードできるか
  • ローカルにあるAASAでURLがUniversal Linkとして認識されるかどうか

両方ともターミナルで行われます。

JSONを正常にダウンロードできるかの確認方法

ターミナルで下記のコマンドを入力します
$ sudo swcutil dl -d <ドメイン>
例: $ sudo swcutil dl -d example.com

  • 成功の場合はAASAの中身が表示されます
  • 失敗の場合はエラーが表示されます

これでファイルがちゃんと正しく置かれていてアクセスできることは確認できましたが、ファイルでアプリに転送させたいURLがちゃんと転送されるかの確認もしたいですね。

リンクが想定通りの処理されるかの確認方法

ターミナルで下記のコマンドを入力します
$ sudo swcutil verify -d <ドメイン> -j <テストしたいAASAファイルのパス> -u "<テストしたいURL>"
例:$ sudo swcutil verify -d example.com -j apple-app-site-association -u "https://example.com/test"

下記のAASAで試して結果を解説していきます:

{
    "applinks": {
        "details": [
            {
                "appIDs": [
                    "GALAPAGOS1.com.example.app",
                    "GALAPAGOS1.com.example.app2",
                ],
                "components": [
                    {
                        "/": "/test/*",
                    },
                    {
                        "/": "/test2/*",
                    },
                    {
                        "/": "/exclude/include/*",
                        "comment": "排除されている/excludeの中でもマッチさせる"
                    },
                    {
                        "/": "/exclude/*",
                        "exclude": true,
                        "comment": "排除"
                    },
                    {
                        "?": {"token": "token42"},
                        "comment": "クエリーでのテスト"
                    }
                ]
            }
        ]
    }
}

パターン1:https://example.com/test/hoge

 $ sudo swcutil verify -d example.com -j apple-app-site-association -u "https://example.com/test/hoge"
{ s = applinks, a = GALAPAGOS1.com.example.app, d = example.com }: Pattern "https://example.com/test/hoge" matched.
{ s = applinks, a = GALAPAGOS1.com.example.app2, d = example.com }: Pattern "https://example.com/test/hoge" matched.

コマンド実行するとappIDsで定義されているアプリごとに結果が表示されます。
今回のテストは二つ定義していますので二つの結果が出ています。
結果を見るとまず、{}に囲まれた部分があって、その後に今回テストされたパターンとテスト結果が表示されます。

{}の中にある部分は今回のテストについての情報です:

  • s = applinks: applinks内の設定を使用してテストしています。
  • a = GALAPAGOS1.com.example.app: テスト対象のアプリはGALAPAGOS1.com.example.appです。
  • d = example.com: ドメインexample.comでのテストです。

{}の後はパターンと結果が表示されます:

  • Pattern "https://example.com/test": パスが/testのパターンをテストしています
  • matched: /test/*パスが一致し、アプリへの遷移が可能です。

今回は一つ目のルール"/": "/test/*"でマッチしていますね。

パターン2:https://example.com/hoge/hoge

$ sudo swcutil verify -d example.com -j apple-app-site-association -u "https://example.com/hoge/hoge"
Input JSON did not match input URL.

このパスでマッチするルールをAASAで定義していないので、マッチするものがないという結果になります。

パターン3:https://example.com/test2

$ sudo swcutil verify -d example.com -j apple-app-site-association -u "https://example.com/test2/hoge"
{ s = applinks, a = GALAPAGOS1.com.example.app, d = example.com }: Pattern "https://example.com/test2/hoge" matched.
{ s = applinks, a = GALAPAGOS1.com.example.app2, d = example.com }: Pattern "https://example.com/test2/hoge" matched.

パターン4:https://example.com/exclude/hoge

$ sudo swcutil verify -d example.com -j apple-app-site-association -u "https://example.com/exclude/hoge"
Password:
{ s = applinks, a = GALAPAGOS1.com.example.app, d = example.com }: Pattern "https://example.com/exclude/hoge" blocked match.
{ s = applinks, a = GALAPAGOS1.com.example.app2, d = example.com }: Pattern "https://example.com/exclude/hoge" blocked match.

この例では"/exclude/*"でマッチしていますが、そこでexcludeを設定していますので、結果的にブロックされます。
blocked match: ルールによってブロックされました。

パターン5:https://example.com/exclude/include/hoge

sudo swcutil verify -d example.com -j apple-app-site-association -u "https://example.com/exclude/include/hoge"
{ s = applinks, a = GALAPAGOS1.com.example.app, d = example.com }: Pattern "https://example.com/exclude/include/hoge" matched.
{ s = applinks, a = GALAPAGOS1.com.example.app2, d = example.com }: Pattern "https://example.com/exclude/include/hoge" matched.

/exclude/でブロックされるはずのexclude/include/hogeですが、別でexclude/includeのルールを定義していて、それでマッチしています。

パターン6:https://example.com/query?token=hoge

$ sudo swcutil verify -d example.com -j apple-app-site-association -u "https://example.com/query/?token=token42"
{ s = applinks, a = GALAPAGOS1.com.example.app, d = example.com }: Pattern "https://example.com/query/?token=token42" matched.
{ s = applinks, a = GALAPAGOS1.com.example.app2, d = example.com }: Pattern "https://example.com/query/?token=token42" matched.

ここでは"?": {"token": "token42"} のルールでクエリーの内容でマッチしています

###おまけ
パスを処理している時に、上から順番に処理され、最初にマッチしたルールで処理が終わるため、componentsの項目を書く順番が大事です。
例えば、/excludeをブロックするルールを/exclude/include/*より先に定義した場合、パターン5では先に"/exclude/*"ブロック判定になってしまいます。

[...]
{
    "/": "/exclude/include/*",
    "comment": "排除されている/excludeの中でもマッチさせる"
},
{
    "/": "/exclude/*",
    "exclude": true,
    "comment": "排除"
},
[...]
sudo swcutil verify -d example.com -j apple-app-site-association -u https://example.com/exclude/include/hoge
{ s = applinks, a = GALAPAGOS1.com.example.app, d = example.com }: Pattern "https://example.com/exclude/include/hoge" blocked match.
{ s = applinks, a = GALAPAGOS1.com.example.app2, d = example.com }: Pattern "https://example.com/exclude/include/hoge" blocked match.

最後に

今回紹介したswcutilでAASAファイルのテストができますので、ルール定義時、実際サイトにおいて、developerモードで遷移するかどうかを確認するより、swcutilを活用すれば、様々なパターンを素早くテストでき、Universal Linksの実装が効率的に進められるでしょう。
今後は、swcutilを活用した自動テストスクリプトの構築にも取り組む予定です。

株式会社ガラパゴス(有志)

Discussion