Password AutoFill のためのiOSアプリとドメインの関連づけが Firebase App Distribution ではうまくいかなかった

開発中のiOSアプリのログイン画面に **Password AutoFill** 機能を組み込んで実機でのテストしているとき、ログイン情報の候補がキーボードの QuickType バーに表示されないという現象で苦労しました。

結論としては [Firebase App Distribution](https://firebase.google.com/products/app-distribution?hl=ja) でアプリをインストールした時のみ、アプリとドメインとの関連づけがうまくいかないことが原因だったのですが、その調査に苦労したため過程をメモとして残します。

## はじめに
**Password AutoFill** とはiOS11から提供された、ログイン情報（ユーザーやパスワード）を自動入力する機能です。これをログイン画面に仕込んでおけば、Keychainなどのパスワードマネージャーに保存されているログイン情報を自動入力することができます。

以下は Twitter のログイン画面ですが、キーボード上部の QuickType バーに自動的に twitter.com 用のログイン情報候補が表示されているのがわかります。それをタップして生体認証 or パスコード入力を経ると、ログイン情報のテキストフィールドに自動的に入力されるという動作です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/e0ec8379-6de6-3bcb-b10d-8cc432687153.png" width=200>

これを実現するためには、大きく分類すると下記の２つについて対応する必要があります。

- ログイン情報を入力する UITextField の Content Type に `.username`, `.password` を指定する
- Associated Domains という仕組みで、ドメインとアプリの関連付けを行う

## Associated Domains の確認
今回起きていた **ログイン情報の入力時に QuickType バーに候補が表示されない** という現象は、ドメインとアプリの関連付けがうまくいっていない可能性が高いです。

この Associated Domains の設定は、アプリとそのドメインのサーバーの両方で設定が必要です。

- アプリの Identifier に対して Associated Domains を有効にする
- アプリの Capabilities 設定で Associated Domains に関連づけるドメインを指定する
- 関連づけるドメインのサーバーに関連づけるアプリの Identifier を指定した apple-app-site-association というファイルを配置する

これらの設定方法についての詳細は割愛しますが、これらのどこを確認しても誤りは見つかりませんでした。


## うまくいくケースといかないケースがある
確認中に、うまくいくケースといかないケースがあることに気づきました。それは、アプリのインストール方法によるものでした。

- :x: [Firebase App Distribution](https://firebase.google.com/products/app-distribution?hl=ja) から AdHoc アプリをインストール
- :o: [TestFlight](https://developer.apple.com/jp/testflight/) でアプリをインストール
- :o: [Xcode](https://apps.apple.com/jp/app/xcode/id497799835?mt=12) から実機に転送してデバッグ実行
- :o: [Apple Configurator](https://apps.apple.com/jp/app/apple-configurator-2/id1037126344?mt=12) で AdHoc アプリをインストール

つまり、Firebase App Distribution からのインストール時のみ上手くいかないのです。

## Shared Web Credentials Daemon の動作を追う

調べていくと、どうやらドメインとアプリの関連付けはアプリのインストール/アップデート時に行われるようです。これは iOS の中での **Shared Web Credentials Daemon** というプロセスが行うため、デバイスを Mac に接続した状態でアプリのインストールを行い、Mac のコンソールアプリケーションによりログを追っかけてみます。

### Apple Configurator で AdHoc アプリをインストールした場合

抜粋ですが以下のようなログが見つかりました。つまり、該当ドメインのサーバーに設置した apple-app-site-association ファイルを参照し、それを元にドメインとアプリIDを関連づけているのがわかります。先に `.well-known` ([RFC-5785](https://tools.ietf.org/html/rfc5785)) 配下を探しに行くようです。

```sh
swcd	Started request for domain 'example.com', URL 'https://example.com/.well-known/apple-app-site-association'
swcd	Added service 'webcredentials', appID 'TEAMID1234.com.example.someapp', domain 'example.com'
swcd	Skipping request for domain 'example.com' when already outstanding
swcd	Started request for domain 'example.com', URL 'https://example.com/apple-app-site-association'
swcd	Completing request for 'https://example.com/apple-app-site-association', status 0/0x0 noErr
swcd	Updated app ID 'TEAMID1234.com.example.someapp', domain 'example.com', flags 0x0 < > -> 0x2 < SiteApproved > on check
```

ちなみに、アプリを削除した時には次のようなログが出ており、関連づけも削除されているようで。

```sh
swcd	Removed service 'webcredentials', app ID 'TEAMID1234.com.example.someapp', domain 'example.com' by bundle ID 'com.example.someapp'
```

### Firebase App Distribution から AdHoc アプリをインストールした場合

抜粋ですが以下のようなログが出力されています。前のものと比べると２点違いがありました。

- `Added service 'webcredentials' ...` の直後に `Removed service 'webcredentials'` されてしまっている
- これが原因なのか `Updated app ID ...` のログがない

原因はわかりませんが、Firebase App Distribution からインストールした場合は、アプリとドメインの関連づけがうまくいかないようです。

```sh
swcd	Started request for domain 'example.com', URL 'https://example.com/.well-known/apple-app-site-association'
swcd	Added service 'webcredentials', appID 'TEAMID1234.com.example.someapp', domain 'example.com'
swcd	Skipping request for domain 'example.com' when already outstanding
swcd	Removed service 'webcredentials', app ID 'TEAMID1234.com.example.someapp', domain 'example.com' by bundle ID 'com.example.someapp'
swcd	Started request for domain 'example.com', URL 'https://example.com/apple-app-site-association'
swcd	Completing request for 'https://example.com/apple-app-site-association', status 0/0x0 noErr
```

## 参考文献
- [Apple Developer / Password AutoFill](https://developer.apple.com/documentation/security/password_autofill)
- [WWDC2017 / Introducing Password AutoFill for Apps](https://developer.apple.com/videos/play/wwdc2017/206)
- [Apple Developer / Supporting Associated Domains](https://developer.apple.com/documentation/safariservices/supporting_associated_domains_in_your_app)