💙

【Flutter】Deep Links(Universal Links)が効かない

2024/08/30に公開

TL;DR

原因は、RunnerProfile.entitlementsだけでなくRunner.entitlementsにも「Associated Domains」の設定追加が必要だったこと。

はじめに

FlutterアプリへのDeepLinks(Universal Links(ユニバーサルリンク))の導入で2日間くらい詰んだので、備忘する。
⭐️最後にUniversal Links導入用の見直しチェックリストもあるよ⭐️

前提

ナビゲーションはgo_routerを採用

内容

公式Doc通りに、iOSとAndroidそれぞれ設定を行った。
しかし、Androidはうまく機能するが、iOSはwebブラウザが開かれてapple-app-site-associationをホストしているサーバーにアクセスして404エラーになるだけで、アプリは起動しない。

何か設定に不備があるのだろうとFlutterとAppleの公式Doc、Qiita、Zenn、StackOverflowを漁りまくったが、どれを見直したり試したりしても解消されない。

ふとした時に、「そういえばentitlementsファイルが2個あるな〜〜〜」と思って中身をのぞいたところ、なんと差異があるやないか。どっちがiOSビルドで使われてるか知らんけどさすがによろしくないやろ。

Runner.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <!-- AppleSignInの要素しかない -->
	<key>com.apple.developer.applesignin</key>
	<array>
		<string>Default</string>
	</array>
</dict>
</plist>
RunnerProfile.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <!-- AppleSignInとAssociatedDomainsの2つの要素がある -->
	<key>com.apple.developer.applesignin</key>
	<array>
		<string>Default</string>
	</array>
	<key>com.apple.developer.associated-domains</key>
	<array>
		<string>applinks:exapmle.com</string>
	</array>
</dict>
</plist>

ってことで、Runner.entitlementsにもAssociated Domainsを追加して再ビルドしたらうまくいった。

Flutter公式Docの「Other editors」じゃなくて「Xcode」で設定した人はけっこう陥っているかも?
たぶんXcodeから設定するとRunnerProfile.entitlementsが新規作成されてそっちに設定が記述されるのかな?
iOSエンジニア→Flutterエンジニアの人だと容易に気づけるのかもしれない。(私はwebからの人なので2日間かかった)

見直しチェックリスト

2日間でいろんな見直しをしたので今後のためにもそれをリスト化した。

  • info.plistにFlutterDeepLinkingEnabledをtrueで追加したか
  • AppleDeveloperの対象IdentifiersのCapabilitiesにもAssociated Domainsのチェックは入っているか
  • AppleDeveloperのProfilesに対象アプリのプロビジョニングプロファイルはあるか?かつ、iOSになっているか?有効期限は切れていないか?
  • apple-app-site-associationはホスティングサーバーに「<ウェブドメイン>/.well-known/apple-app-site-association」の構造でデプロイされているか?
  • apple-app-site-associationファイルの拡張子は無いか?「.json」になってしまってないか?
  • ホスティングサーバーのapple-app-site-associationへリクエストした際のレスポンスのstatusCodeは200か?content-typeはapplication/jsonになっているか?
    • 「curl -v」で試行するとよい
    • application/jsonでない場合はホスティングサーバーの設定でjsonを返すように設定してあげる必要がある
      • Firebase Hostingを使っている場合はfirebase.jsonに下記を追記すればOK。Apacheはhttp.confかな?Nginxは知らん。
        firebase.json
        "headers": [
             {
               "source": "/.well-known/apple-app-site-association",
               "headers": [
                 {
                   "key": "Content-Type",
                   "value": "application/json"
                 }
               ]
             },
               // Androidも対応する場合は下記も設定しておく
             {
               "source": "/.well-known/assetlinks.json",
               "headers": [
                 {
                   "key": "Content-Type",
                   "value": "application/json"
                 }
               ]
             }
           ]
        
  • AppleのCDNはすでにapple-app-site-associationをキャッシュしてくれているか?
    • 下記コマンドを実行して200でファイル内容が返って来ればOK
      curl -v https://app-site-association.cdn-apple.com/a/v1/{ホスティングサーバーのドメイン}
      
  • テストしている環境はIP制限がかかってないか?
  • いろいろ設定を更新したあとは、アプリをアンインストール後に再インストールしてテストしているか?(シミュレータも新しくすると尚良し)
  • テストはブラウザにURLを直打ちしていないか?
    • ブラウザ直打ちは機能しないので、何らかのアプリでURLをタップするか、ターミナルから下記コマンドでシミュレータにURLを開かせるかでテストする
      xcrun simctl openurl booted {任意のUnivesalLinksのURL}
      

補足

  • GoRouterクラスはmain.dartから切り出して別ファイル化しててもOK
  • GoRouterクラスはProvider化して、runAppしているウィジェットでrouterConfig: ref.read(goRouterProvider),で設定しててもOK

参考

Discussion