SmartAppBannerを使って、UniversalLinkからのアプリ起動をコントロールする
最近FlutterアプリでURLからアプリを起動する機能を実装する必要があったのですが、Firebase Dynamic Linksのサービス終了予告通知があり、使えなくなってしまったので、仕方なく自前でiOSではUniversalLink、AndroidではAppLinksを自前で実装して、URLからアプリの特定のページを開くことができるようになりました
しかし1つ問題が発生しました
旧Twitter(X)に貼られたURLをタップしてiOSアプリを開こうとすると、
- Twitterではデフォルトでアプリ内ブラウザ(SafariViewController)を使って開かれる
- 短縮リンクが使われているので、ブラウザに遷移後にリダイレクトされる
- Universal Linkはリダイレクトされた時は発動しない
これらの理由から、タップしてもアプリに遷移しない現象が発生します
遷移先のWebサービスの画面側で適切にハンドリングすれば、アプリ内ブラウザからモバイルアプリを起動させることはできますが、自身が関わっているサービスはまだWebアプリはなく、LPしかない状態です
そのためWebアプリ側で細かなハンドリングをすることはできません
そこで今回、iOS側はSmartAppBannerを使用し、Web画面には大きく手を入れず、UniversalLinkからアプリを開くことができるようにしました
今回はこの実装の概要を紹介します
Smart App Bannerとは?
Smart App BannerはSafari(SFSafariViewControllerを含む)でアプリを開く、もしくはダウンロードの導線を自動で表示してくれるバナーです
↓のキャプチャの上部のもので、iPhoneを使っていたら、1度は見たことあると思います!
このバナーは以下の特徴があります
- 指定のアプリがストアからインストールされている場合は、アプリを開くボタンが表示
- TestFlightからインストールしている場合は、未インストール扱いになります
- インストールされていない場合、アプリをダウンロードするボタンが表示される
今回のポイントとして、ただアプリを開くだけではなく、UniversalLinkのURL情報を渡す必要があるのですが、それもパラメータとして設定できます
次にSmartAppBannerの実装方法やUniversalLinkのパラメータ情報を設定する方法を紹介します
Smart App Bannerの実装方法
Smart App BannerはSafariで表示するのでもちろんWebのコードを書く必要があります
Smart App Banner自体はheadの中に以下のタグを追加すればOKです
<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-argument=myURL">
myAppStoreIDの箇所にはアプリのIDを設定します
アプリのIDはAppStoreConnectのアプリのページのApple IDの項目などで確認できます
myURLの箇所はパラメーターとしてアプリに渡したいURLのパラメーター、UniversalLinkのURLなどをセットできます
UniversalLinkの設定方法などは以下のドキュメントを確認してください
自分のプロジェクトでは現在Vue.jsベースで作成されたLPをFirebase Hostingでホスティングしてます
さらに今回は固定のタグではなく、app-argumentのパラメータに動的なURLを設定する必要があります
そのため今回はheadの中でなく、mountedの中でタグを設定するようにしています
この処理をタグを表示したいページ(XのURLで開くページ)にセットします
mounted() {
if (process.client) {
let appArgument = '$UNIVERSAL_LINK_URL';
// Set meta tag dynamically
const metaTag = document.createElement('meta');
metaTag.name = 'apple-itunes-app';
metaTag.content = `app-id=${APP_ID}, app-argument=${appArgument}`;
document.head.appendChild(metaTag);
}
}
これをdeployして、SafariやSFSafariViewControllerからこのWebページを開くと、SmartAppBannerが表示されているはずです!
app-argumentのURLのアプリ側でのハンドリング方法
次にはアプリ側の話です
ただアプリを開くだけなら、特に対応は不要ですが、今回やりたいこととしては、UniversalLinkと同様に、特定の画面を開いたり、処理をすることが目的です
ただ一つ問題があります
既述の通り、Smart App Bannerはストアからダウンロードしたアプリでないと、ダウンロード済み扱いになりません
つまりこのアプリのURLのハンドリングをした処理を実装しても、Debugビルドのアプリはもちろん、Appleへのレビューを出す前に、TestFlight経由でインストールしたアプリで動作確認をすることもできません
公式ドキュメントにはapplication(_:open:sourceApplication:annotation:)を実装してハンドリングすると書いてありましたが、このメソッドのドキュメントにはdeprecatedって書いてありました
またFlutterの場合、どのようにすればハンドリングできるか、分かりませんでした
Flutterの場合、URL SchemeやUniversalLinkのハンドリングは、app_linksというパッケージを使って行います
このパッケージはiOS側は内部的に以下の2つを使って、ハンドリングしています
- Universal Link用
- func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]) -> Void) -> Bool {
- URL Scheme用
- public func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
今回は元々実装してあった上記のapp_linksの処理を実装して、ストアでのリリース後に動作確認することにしました
今回は幸いFlutterアプリ側でのハンドリングはこの実装でしっかりUniversalLinkで指定したページを開くことができました🎉
なのでiOSのネイティブアプリでもSwiftで上記のメソッドを実装すれば、うまくハンドリングできるかと思います
結果的に通常のUniversal Linkの動作確認をして、かつSmart App Bannerのapp-argumentにUniversal LinkのURLを正しく設定できていれば、正しく動作しそうです
これでXの投稿に記載したUniversalLinkのURLも、Smart App Bannerが表示されたアプリ経由でアプリの指定画面を開くことができるようになったし、しかもアプリがインストールされていなかったら、ダウンロード動線を自動で表示させることができるようになりました
まとめ
Smart App BannerはSafariやSFSafariViewControllerでしか表示できないが、今回の要件のX(Twitter)からのUniversalLinkを使ったアプリの起動という要件で、Webサイト側に大きな改修を追加せずに実装できるのは非常にありがたかったです
そもそものUniversal Linkの実装や設定が大変ですが、結果的にSmart App Bannerについてアプリ側では特別対応が不要だったので、その点もよかったです
Flutterを含めて、iOSアプリでX(Twitter)からUniversalLinkでアプリ起動をさせたい場合の実装の参考になればと思います!