モダンウェブフロントエンド勉強会を開催しました

こんにちは、クックパッドで最近はモバイルアプリを離れもっぱらウェブアプリを作っている @morishin です。

先日、社内で「モダンウェブフロントエンド勉強会」と題して React, Next.js, Core Web Vitals, SSR, CSR, SSG, ISR, SSR Streaming, React Server Component といったキーワードに触れつつ、昨今のウェブ開発事情について話をしました。せっかくなのでその内容の共有と、勉強会を開催した動機などを紹介したいと思います。

背景・動機

クックパッドのウェブアプリケーションは10年以上もの間 Ruby on Rails で開発されてきましたが、2020年から一部のページは Next.js のアプリケーションがホストするようになりました。具体的な構成については次の記事をご覧ください。

techlife.cookpad.com

Next.js を使うようになると全てのビューは React コンポーネントで記述し、スタイルも JavaScript で記述するようになり、これまでの Rails での開発とは大きく異なるものになりました。それに加えてパフォーマンスを意識する必要性も増し、React の再レンダリングの回数を気にしたり、SSR (サーバーサイドレンダリング) を活用したり、バンドルサイズを気にしたりしなければならなくなりました。Rails の時は気にしなくてよかったというわけではないですが、ブラウザで実行される JavaScript がぐっと増えたことや、開発者の工夫の余地が増えたことで、パフォーマンスについて考えることが増えたのを実感しています。

そんな中 React 18 や Next.js 12 という大幅なアップデートがやってきて、このアップデートの内容や背景を理解するには必要な前提知識が多すぎると思ったため、自分の理解を補強する目的も込みで勉強会を開催しようと考えました。

内容

というわけで内容としては「React 18 や Next.js 12 の新しい提案の裏にある動機を理解する」をゴールに置き、そのためにウェブ技術のこれまでの進化を順を追って紹介していくというものにしました。資料のみで恐縮ですがここに貼っておきますので、興味のある方はご覧ください。

資料 URL: https://static.cookpad.com/techlife/202205-web-frontend-study

喋った内容の情報源はこのあたりです。資料の内容が疑わしかったりわからないところがあればこちらの一次情報源もご参照ください。

社内勉強会

クックパッドではエンジニアがしばしば野良勉強会を開催していて、最近だとこれの他に GraphQL 勉強会をやったりしました。他の人がやっていたのだと「RBS 勉強会」「検索勉強会(社内の検索システムが対象)」「Production Ready GraphQL 輪読会」などが開催されているのを見かけました。勉強会がある会社って楽しいですよね。そう思って自分も積極的に開催しています。

クックパッドでは技術を学んだり好きな技術について早口で喋ったりするのが好きなエンジニアを募集しています!クックパッドのエンジニアと早口オタクトークをしたい方はカジュアル面談でもどうですか。下記の Meety からご連絡いただけます。(僕もいます)

meety.net

クックパッドで働くことに興味を持ってくださった方はこちらもご覧ください。

info.cookpad.com

XcodeでSwift Package Manager実用段階

こんにちは、モバイル基盤部のヴァンサン(@vincentisambart)です。

Swift Package ManagerはAppleがXcodeで公式にサポートしている唯一のパッケージマネージャーです。Xcode公式サポートの他に、Swift Package Manager形式でのみ提供されているswift-algorithmsswift-atomics、将来的に期待されているswift-async-algorithmsといった準標準ライブラリを利用できるようになるという大きなメリットがあります。

クックパッドiOSアプリ(以下クックパッドアプリ)で一部の依存パッケージをXcodeのSwift Package Manager対応を使って入れるようにしました。この導入で得たいくつかの知見をまとめました。

XcodeのSwift Package Manager対応

本来のSwift Package ManagerがSwiftプロジェクトの一部です。コマンドラインでswift packageで使えます。活用するプロジェクトの構成をPackage.swiftで定義します。

ですが、今回話したいのはSwift Package ManagerのパッケージをXcodeのプロジェクトで使う時の話です。依存されているパッケージのプロジェクト構成がPackage.swiftで定義されていますが、メインのプロジェクトの構成がXcodeプロジェクト(xcworkspaceまたはxcodeproj)で定義されています。

Swift Package Managerがオープンソースであるのに対し、Xcode側の実装はオープンソースのSwift Package Managerの一部を使いながらもクローズドソースです。

プロジェクト構成の定義がPackage.swiftではないので、swift packageコマンドが使えません。Xcode内ではプロジェクト設定のPackage Dependenciesタブで依存パッケージを変更できます。

ターゲットの設定では、Build PhasesのLink Binary with Librariesに使いたいパッケージのライブラリを指定します。

また、XcodeのFile > Packagesメニューにキャッシュリセットやパッケージ更新用のコマンドがあります。

パッケージ自体はディレクトリ構造をSwift Package Managerの期待した構造に合わせると、Package.swiftが割りと作成しやすいと思いますが、もっと詳しく説明すると長くなるので今回はしません。1つだけ不自然な点を挙げると、なぜかPackage.swiftで未対応なOSを指定できないようです。platformsiOSだけを指定しても、macOSが未対応になるわけではなく、macOSに関して最低OSバージョンがデフォルトのものになるだけです。

最近までの流れ

以前からクックパッドアプリでSwift Package Managerを導入をしようとしていました。hiragramの記事で説明されているように2020年12月に試みましたが、クックパッドアプリで使える状態にまだ達していなかったため断念しました。

2021年12月リリースのXcode 13.2.1では、複雑な依存関係だと手元でのビルドに問題なかったが、AdHocやApp Store配布用のエキスポートが失敗していました。ですが、先月2022年4月にリリースされたXcode 13.3.1でXcode 13.2.1で起きていた問題が解消されて、クックパッドアプリでついに使えるようになりました。

社内ライブラリで使うには

最初に試したとき、GitHub.comなどに公開されているパッケージはXcodeのデフォルト設定でSwift Package Manager対応を利用してビルドできましたが、社内ライブラリではうまく動きませんでした。これだと導入のメリットがだいぶ限られてしまいます。

Xcodeが非公開レポジトリを取得するとき、デフォルト設定では、~/.sshに入ったシステム標準の設定が使われるのではなく、Xcode独自の仕組みが使われます。クックパッドでは、リモートで働くとき、VPNまたはSSHトンネル使う必要があります。開発者のマシンでシステムのSSHが既に設定されていますし、複雑なSSH設定はXcode独自の仕組みでできないので、XcodeがシステムのSSHの設定を使ってほしかったです。幸いなことに、このような設定があります。すべての開発者の手元で以下のコマンドを実行すればXcodeの設定を変えられます。

defaults write com.apple.dt.Xcode IDEPackageSupportUseBuiltinSCM YES

この件に関するネットでの記事を見ると、上記のコマンドの頭にsudoが入った記事もありますが、僕の試した限りでは逆にsudoが入っていると効果がありませんでした。YESの代わりに1を使っても問題ありません。

すべての開発者の手元で実行されるようにしなければいけないのは不便なので、プロジェクト作成、環境設定、依存パッケージインストールのようなスクリプトがあれば、そこでやるのがおすすめです。

# 現在の設定を確認する
# (`|| true`は`set -e`を使ってもエラーにならないため)
using_system_ssh=`defaults read com.apple.dt.Xcode IDEPackageSupportUseBuiltinSCM 2> /dev/null || true`
# まだ設定されていない場合のみ設定を変える
if [ "$using_system_ssh" != "YES" ] && [ "$using_system_ssh" != "1" ]; then
  defaults write com.apple.dt.Xcode IDEPackageSupportUseBuiltinSCM YES
fi

Package.resolved

パッケージマネージャーはユーザーが基本的にどういうパッケージのどういうバージョンを使いたいのか指定しますが、バージョンの指定は固定にできるとはいえ、メジャーバージョンが変わらなければ更新しても良いこともよくあります。

パッケージマネージャーは指定を満たすバージョンと明記されていないけど依存されているパッケージを解決します。Swift Package Managerでは、解決されたパッケージのリストとそれぞれのバージョンがPackage.resolvedというJSONファイルに入ります。全ての開発者がそれぞれのパッケージの同じバージョンを使わないとややこしいので、アプリはこのファイルをレポジトリに入れるのを強く推奨します。CocoaPodsでいうとPodfile.lock、CarthageでいうとCartfile.resolved、RubyGemsでいうとGemfile.lockと同じ役目です。

本来のSwift Package Managerでは、Package.resolvedPackage.swiftと同じディレクトリに入りますが、Xcodeではメインで使われているのがxcworkspaceかxcodeprojかによってディレクトリが少し違います。xcworkspaceの場合、MyProject.xcworkspace/xcshareddata/swiftpm/Package.resolvedですが、xcodeprojの場合、MyProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolvedです(もちろん「MyProject」は自分のプロジェクト名に置き換えます)。

xcworkspaceの場合、必ずxcworkspaceを開けるように気を使いましょう。xcodebuildもいつもxcworkspaceを指定するように。

Xcodeでパッケージがうまく取得できていない時

Xcodeでプロジェクトを開く時、必要であればXcodeがプロジェクトを取得しようとしますが、それが失敗する時はたまにあります。そういう時、XcodeのメニューにあるFile > Packages > Reset Package Cachesがおすすめです。

xcodebuild

Xcodeのユーザー体験はXcode自体のUIがメインですが、CIやスクリプトは基本的にxcodebuildを使います。xcodebuildとXcodeのSwift Package Manager対応絡みで気を使う必要のある点がいくつかあります。

SSH認証

Xcode自体と違って最近xcodebuildがデフォルトでシステムのSSH設定を使うようになっています。念のために明記したい場合、-scmProvider systemでできます(SCM=Source Code Management)。(逆にXcode独自の認証方法は-scmProvider xcodeで指定できます)

依存パッケージ解決

Package.resolvedがない場合、依存パッケージ解決がまず依存パッケージやバージョン指定を見てPackage.resolvedを生成してくれます。Package.resolvedの情報があれば、それに従って依存パッケージ解決が必要なパッケージバージョンを取得してくれます。

Xcodeでプロジェクトを開く時やxcodebuildでビルドをする時に必要であれば依存パッケージ解決が自動的に行われるが、xcodebuildで依存パッケージ解決だけをしたい場合以下のコマンドでできます。

xcodebuild -resolvePackageDependencies -workspace MyProject.xcworkspace -scheme MyProject

明確でありたければ-scmProvider systemを指定できますし、取得されたパッケージの保存先を-clonedSourcePackagesDirPathで指定できます。

キャッシュ

クラウドで動くCIはスピードを出すにはキャッシュをうまく活用するのが大事です。xcodebuildで依存パッケージの取得を簡単にキャッシュできます。上記に説明されたようにxcodebuildで依存パッケージ解決を実行して、-clonedSourcePackagesDirPathで指定されたディレクトリをキャッシュすれば良いです。その後xcodebuildでビルドコマンドなどを実行する際、改めて-clonedSourcePackagesDirPathで同じディレクトリを指定するのをお忘れずに。

Package.resolvedの更新

XcodeのメニューにFile > Packages > Update to Latest Package Versionsでパッケージを更新できますが、現時点でxcodebuildにこういう機能がありません。

クックパッドアプリは毎週自動的に全てのパッケージを更新してPRを出すCIジョブがあります。このジョブのためxcodebuildでパッケージを更新する方法が必要でした。

更新のコマンドがないとはいえ、少し強引ではありますが、方法がないわけではありません。Package.resolvedがなければ、依存パッケージ解決が最新パッケージを取りに行きます。試してみると、キャッシュがあれば最新のバージョンではなく以前と同じバージョンになってしまうのでxcodebuildがキャッシュを見に行かないようにする必要があります。

# Package.resolvedを消す
rm -f MyProject.xcworkspace/xcshareddata/swiftpm/Package.resolved
# 空っぽなテンポラリディレクトリを作成する
tmpcache=`mktemp -d`
# 依存パッケージ解決をやる。残っていたキャッシュが使われないために、キャッシュディレクトリに作ったばかりのテンポラリディレクトリを指定する
xcodebuild -resolvePackageDependencies -workspace MyProject.xcworkspace -scheme MyProject -scmProvider system -clonedSourcePackagesDirPath "$tmpcache"
# テンポラリディレクトリを消しておく
rm -rf "$tmpcache"
# Package.resolvedに指定を満たすパッケージの最新のバージョンが使われるようになったはず

XcodeGenを使う場合気をつけること

以前giginetが説明したようにクックパッドアプリのプロジェクト構成はXcodeGenで定義しています。

プロジェクト構成を直接xcworkspaceまたはxcodeprojで定義する場合、Xcode内で依存関係に変更を加えるとPackage.resolvedが自動的に更新されますが、XcodeGenのプロジェクト設定を変えるだけでPackage.resolvedが更新されません。依存関係に変更を加えて、XcodeGenを実行してから、Package.resolvedを更新するにはXcodeを開くかxcodebuildで依存パッケージ解決をするか、が必要がです。気をつけないとPackage.resolvedの変更が入っていないPRを出すリスクが出てしまいます。

また、最近クックパッドアプリでライセンス管理にLicensePlistを使用していますが、LicensePlistが見ているのもPackage.resolvedです。Package.resolvedが更新されていないとLicensePlistの生成したファイルもプロジェクトファイルに入った依存関係設定と一致しません。

プロジェクト生成のスクリプトではXcodeGenを実行してからLicensePlistを実行していましたが、上記の理由で、LicensePlistを実行する前にxcodebuildの依存パッケージ解決をするようにしました。パッケージ解決が既に完了している場合プロジェクト生成スクリプトの実行時間が2~3秒伸びるのは少し残念ですが、分かりにくい状態を避ける方が大事だと思います。

他のパッケージマネージャーとの絡み

クックパッドアプリはCocoaPodsとCarthage両方を使っていましたが、ライブラリが複数なパッケージマネージャーをサポートしている場合、ライブラリを導入するときはどれを使うべきか悩んでしまいます。特定なパッケージマネージャーに寄せた方が運用しやすいです。

最近Appleプラットフォーム開発界隈が依存関係をSwift Package Managerに寄せつつあるように感じるので、クックパッドアプリもできるだけSwift Package Managerに寄せることにしました。

別のパッケージマネージャーからSwift Package Managerに移行するとき、ライブラリがPackage.swiftを既に提供していると、アプリのプロジェクトに参照のやり方を変えるだけのはずですが、他の変更も必要になることがあります。

例えば、Carthageを使って作成されたxcframeworkからSwift Package Managerに移行したら、クックパッドアプリでimportを足す必要があったSwiftファイルがありました。モジュールの扱い方の違いで、Carthage版だとライブラリをimportするだけでFoundationやUIKitが暗黙的にimportされることがありました。Swift Package Manager版だとそんなことがないので、そのimportを明記する必要があります。

また、クックパッドアプリのようにアプリが複数のモジュールで構成されている場合、パッケージマネージャーを変えることで、どのモジュールがどのパッケージを参照するのか少し変える必要があることがあります。基本的にSwift Package Managerの場合、参照をもっと多くの箇所で明記する必要がありました。

パッケージマネージャーとの絡みでのハマりどころといえば、XcodeのSwift Package Manager対応とCocoaPodsを併用している場合、XcodeでターゲットのBuild PhasesのDependenciesにSwiftパッケージが明記されていると、CocoaPods実行時に例外が発生してしまいます。CocoaPodsが使っているXcodeprojライブラリのバグです。修正はしましたが、現時点でこの修正が入ったXcodeprojのバージョンがまだリリースされていません(現時点で最新のリリースが昨年8月にリリースされた1.21.0)。ワークアラウンドとして、SwiftパッケージがターゲットのBuild PhasesのLink Binary with Librariesに入っていれば、Dependenciesに入っていなくても暗黙的に依存されるのでLink Binary with Librariesから消せば良いはずです。一応Xcodeprojの特定なコミットハッシュに依存するのも選択肢の1つです。

最後に

課題点がいくつかありましたが、それを越えればXcodeのSwift Package Manager対応が割りと便利だと思います。パッケージ作成はCocoaPodsと違ってスペックをどこかにアップロードする必要ありませんし、Package.swiftでパッケージ構成定義も楽です。

クックパッドアプリでは少しずつSwift Package Managerの利用を増やそうとしています。今週のリリースでCarthageを使わなくなってSwift Package Manager + CocoaPodsの構成になりました。

Android App Linksの複数ホスト対応と、Android App Links対応Webサイトに対して複数アプリを関連付けるための知見

こんにちは、クックパッド事業本部 買物サービス開発部の佐藤(@n_atmark)です。 普段はクックパッドiOS/Androidアプリ向けの買い物機能の開発に従事しています。

さて、タイトルにある通り、今回は Android App Links の話を書こうと思います。

付録: ディープリンク用語まとめ

この記事ではDeepLinkに関連する用語がいくつか登場するため、説明のためにできるだけわかりやすく図示してみました。

DeepLink関連用語の図示

Android App Links とは

Android App Linksの動作イメージ

アプリとウェブサイトURLの両方の所有権を証明することによって自動的にURLインテントをアプリにルーティングする仕組みで、特定のウェブページに関連づけられたアプリがインストールされている場合に、ウェブサイト URL から直接 Android アプリ内の対応コンテンツを開けるようになります。アプリがインストールされていない場合はブラウザ上で表示を継続します。

iOSでは同様の仕組みとして Universal Links があります。

Android App Linksを検証する

アプリとウェブサイトURLの両方の所有権を証明するために以下の手順が必要です。

  • アプリの自動リンク検証を有効にするために、AndroidManifest.xml でintent-filterに autoVerify=true を設定する (これによって intent-filter で使用されているURLドメインに属しているか検証されるようになる)
  • Webサイトとintent-filterの関係を宣言するために、Digital Asset Links JSONファイルを https://domain.name/.well-known/assetlinks.json のパスでホストする

詳しくはこちら Android アプリリンクを検証する | Android Developers

Android App Linksの複数ホスト対応と、Android App Links対応Webサイトに対して複数アプリを紐づけたくなった動機

現在私はクックパッドアプリの「買い物機能」の開発に従事しています。これは、生鮮食品ECサービス「クックパッドマート」のインフラを用いたものです。

一方で、クックパッドマートの利用に特化した専用アプリ(以下: マートアプリ)もリリースされています。

クックパッドアプリとマートアプリ

また、レシピサービスクックパッドおよび、生鮮食品ECサービスクックパッドマートそれぞれ cookpad.com および cookpad-mart.com というホスト名でWebページを提供しています。

cookpad.com と cookpad-mart.com

クックパッドアプリやマートアプリでは、アプリ外からの流入導線として、Android App LinksやFirebase Dynamic Linksを用いており、下のような経路でアプリを起動することができるようになっています。 *1

既存の外部流入フロー

ここで、クックパッドアプリの買い物機能のことを考えてみます。

Firebase Dynamic LinksやAndroid App Linksを設定する場合、実際にコンテンツを表示しているWebサイトのURLを利用することが多いはずです。

例えば、クックパッド買い物機能で販売しているクックパッドマートの商品などのコンテンツへの流入導線を設けたい場合、Webの商品ページは https://cookpad-mart.com/products/:id で提供しているため、バックエンドの構成を変えずにクックパッド買い物機能への流入導線を作ろうと思うとこのようになります。*2

クックパッドAndroidアプリの買い物機能を考慮した外部流入フロー

クックパッドアプリから見た時に「複数のホスト( https://cookpad.com および https://cookpad-mart.com ) に対するAndroid App Links対応」と、「Android App Links対応Webサイト (https://cookpad-mart.com) に対して複数アプリ (クックパッドアプリ、マートアプリ)を関連付ける」場合の挙動や実現方法について調べてみました。

複数のホストに対するAndroid App Links対応

例えば https://cookpad.com および https://cookpad-mart.com に対応する場合、その両方を intent-filter に追加する必要があります。

詳しくは複数のホスト用のアプリリンク機能をサポートする | Android アプリリンクを検証するに記載があり、基本的にこのドキュメントに沿って準備をすれば良いのですが、注意点があり

システムは、マニフェスト内のすべてのホストで条件に合致する Digital Asset Links ファイルが見つかった場合に限り、アプリを指定 URL パターンのデフォルト ハンドラとして確立します。 たとえば、次のインテント フィルタを持つアプリの場合、https://www.example.com/.well-known/assetlinks.jsonhttps://www.example.net/.well-known/assetlinks.json の両方で assetlinks.json ファイルが検出されなかった場合、検証は失敗になります。

という記述が日本語ドキュメントにあります。 例えばディープリンクに対応する場合も対応したディープリンクURLをintent-filterに記述する必要があるのですが、この表記を見るとintent-filterに追加されている全てのホストでAndroid App Links対応がされていない場合、Android App Linksの検証に失敗してしまうような記述があります。

上記のドキュメントの英語版であるSupporting app linking for multiple hosts | Verify Android App Linksを見ると

For example, an app with the following intent filters would pass verification only for https://www.example.com if an assetlinks.json file were found at https://www.example.com/.well-known/assetlinks.json but not https://www.example.net/.well-known/assetlinks.json

Digital Asset Linksが見つかったホストに関してのみ検証に成功するような文脈になっていて、日本語ドキュメントと文脈がやや違った記述をされています。

実際に試してみたところAndroid App Links対応されていないホストを追加した状態で、Android App Links対応されている https://cookpad.com の流入導線は正常に動作しており、英語版ドキュメントの記述が正しそうでした。

Android App Links対応Webサイトに対して複数のアプリに関連付ける

1つのウェブサイト ( https://cookpad-mart.com ) に対してマートアプリとクックパッドアプリ両方を関連づけるためにはDigital Asset Linksに二つ分のアプリ情報を記載する必要があります。こちらもドキュメントが用意されており、ドキュメントに沿って準備をしていきます。

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.cookpad.android.martec",
      "sha256_cert_fingerprints": [
        "FF:AE:BE:09:C7:A8:E3:D5:43:BD:89:21:2B:45:6D:28:DA:24:26:1A:24:88:70:DF:E1:0D:2D:AF:44:5E:BD:15"
      ]
    }
  },
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.cookpad.android.activities",
      "sha256_cert_fingerprints": [
        "14:D6:67:33:06:3D:79:53:31:35:3B:08:0C:C5:03:60:F6:96:8F:58:28:45:65:C5:73:6D:5F:89:C7:6D:2F:B8"
      ]
    }
  }
]

https://cookpad-mart.com/.well-known/assetlinks.json に上記のようなDigital Asset Linksを配置しました。ここでは専用アプリであるマートアプリを先頭にしています。

Android App Links対応Webサイトに対して複数のアプリに関連付ける知見

ここで一つ気になる点があります。 https://cookpad-mart.com に対してクックパッドアプリとマートアプリ二つを紐づけた状態で、両方のアプリがインストールされている場合一体どのような挙動になるのでしょうか。 私たちとしては、あくまでもクックパッドマートのWebサイトなので、マートアプリがインストールされている場合、常にマートアプリが優先度高く開かれる状態になってるのが望ましいと考えており、理想の挙動になっているかどうか気になりました。

挙動の確認のために、クックパッドアプリ/マートアプリがそれぞれインストールされている状態でマートのWebページを開いてみました。

また、Android 12以降ではAndroid App Linksのドメイン検証に関しての変更が入っているため、Android 12未満とAndroid 12以降でそれぞれ試してみました。

詳しくは ウェブ インテントの解決 | Android 12

Android 12未満 Android 12以降
マートアプリのみインストール マートアプリが開く マートアプリが開く
クックパッドアプリのみインストール クックパッドアプリが開く 開かない
クックパッドアプリ→マートアプリの順でインストール マートアプリが開く マートアプリが開く
マートアプリ→クックパッドアプリの順でインストール クックパッドアプリが開く マートアプリが開く

ここまでの結果より

  • Android 12未満だと、一番直近インストールされたアプリがデフォルトで開く
  • Android 12以降だとクックパッドアプリのみインストールされている状態でもクックパッドアプリが開かない(assetlinks.jsonの一番先頭に記述されたアプリのみがデフォルトでウェブインテントを紐づけられる権限を持っている)

ということが分かりました。

また、Android 12未満において直近インストールされたアプリがデフォルトで開くのですが アプリ > (ウェブインテントが紐づいた)アプリ詳細 > 規定で開く > このアプリ対応のリンクを開く「毎回確認」 にした状態で、

Android App Links対応されているページを開くと、該当ページを開くアプリケーションの選択ダイアログが表示されます。

ここでウェブインテントを紐づけたいアプリを選ぶことで、ユーザー設定によって紐づけるアプリを変えられることも確認できました。

Android 12以降の端末に関しては、クックパッドアプリのみインストールされている状態でも「サポートされているリンク」はデフォルトでオフになっていました。

これを、明示的にオンにした場合のみクックパッドアプリが開くようになることが確認できました。

また、クックパッドアプリでサポートされているリンクで cookpad-mart.com をオンにした場合でも後からマートアプリを入れると、 cookpad-mart.com に対するウェブインテントの紐付けはマートアプリに移ることが確認できました。

Android 12以降に関しては原則 assetlinks.json の記述順に優先度が割り当てられていそうなことが分かりました。

まとめ

今回は Android App Links の複数ホスト対応と、Android App Links対応Webサイトに対して複数アプリを関連づけるための知見について紹介しました。

こういったケースは実際はそう多くは発生しないはずで、私たちも結果として今回の方法は採用しなかったのですが、調査した結果として分かった開かれるアプリの優先度や条件などは知見としてなかなか世間に出回っていないはずなので、今後似たようなケースに遭遇したエンジニアの役に立てれば幸いです。

最後になりますが、クックパッドではAndroidエンジニアを大募集しています。

カジュアル面談や学生インターンシップなども随時実施していますので、ぜひお気軽にご連絡ください。

info.cookpad.com

【宣伝】6/6 19:30より、買い物機能のAndroid開発に携わっているエンジニア2名が雑談形式で話すイベントを開催します。

cookpad.connpass.com

*1:実際には2022年5月時点ではクックパッドマートアプリではAndroid App Linksを使っていませんが、説明を簡単にするために記載しています

*2:例えば https://cookpad.com/kaimono/products/:id のようなルーティングを用意して https://cookpad-mart.com/products/:id へのリダイレクトを実装したり、https://kaimono.cookpad.com/products/:id のようなAndroid App Linksに対応したサブドメイン上にルーティングを用意してhttps://cookpad-mart.com/products/:id へのリダイレクトを実装するなどの方法を取れば、今回のようなケースは発生しない。