SwiftUIライフサイクルアプリのAppDelegateでユニバーサルリンクとして受信されるリンクを受け取れない問題の対応策
SwiftUIライフサイクルのアプリでFirebase Dynamic Linksを使用してユニバーサルリンクとして受信されるリンクを受け取ろうとしたのですが、受け取れなかったので調べてみました。
環境
- Xcode 14
- Firebase Apple SDK 9.6.0
- iOS 16.0
はじめに
Firebaseライブラリのインストール、Dynamic Linksの設定は完了している前提で進めていきます。
初期設定でSwiftUIライフサイクルで作成したアプリには、AppDelegate
がデフォルトで無い為、Firebaseのドキュメントに沿って追加しました。
import SwiftUI import FirebaseCore class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { FirebaseApp.configure() return true } } @main struct YourApp: App { // register app delegate for Firebase setup @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate var body: some Scene { WindowGroup { NavigationView { ContentView() } } } }
ユニバーサルリンクとして受信されるリンクを処理できない
Firebaseの公式ドキュメント iOS でダイナミック リンクを受信するにユニバーサルリンクとしてリンクを処理する方法が記載してあります。
application:continueUserActivity:restorationHandler: メソッドで、アプリがすでにインストールされている場合にユニバーサル リンクとして受信されるリンクを処理します。
しかし、SwiftUIライフサイクルアプリで下記のように実装しても、Dynamic Linkを踏んだ時にapplication:continueUserActivity:restorationHandler:
が呼ばれることはありませんでした
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { print("called application continue") if let url = userActivity.webpageURL { print("ユニバーサルリンクとして受信する:", url) } return userActivity.webpageURL != nil }
アプリ側の動きとしては、Dynamic Linkを踏んだ際にアプリに遷移しているので意図した動きはしていたので、遷移する際に踏んだDynamic Linkの情報は、application:continueUserActivity:restorationHandler:
では確認出来ませんでした。
SceneDelegateを使用して試してみる
調べてみると、こちらのstackoverflow内で、userActivity
はSceneDelegate
側で呼ばれるという記載があり、試してみました。
まずはSceneDelegate
を呼ばれるようにする為に、AppDelegate
に下記を追加しました。
AppDelegate
class AppDelegate: NSObject, UIApplicationDelegate { // ~省略 /// SceneDelegateを呼ばれるようにする func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { let config = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role) config.delegateClass = SceneDelegate.self return config } }
SceneDelegate
ユニバーサルリンクを受信して起動した時にSceneDelegate
のscene(scene:, userActivity:)
でリンクの受信を確認することにしました。確認の為、その他でリンクを受信できそうな箇所でもデバッグを試みてみました。
class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { print("called scene willConnectTo") if let url = connectionOptions.userActivities.first?.webpageURL { print(url) } } func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { print("called scene continue") if let url = userActivity.webpageURL { print(url) } } func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { print("called scene openURLContexts") if let url = URLContexts.first?.url { print(url) } } }
しかし、どのメソッドもDynamic Linkを踏んでアプリが表示される際に呼び出されることはありませんでした。
対応策
onOpenURL
を使用すると、受け取ることが出来ました。
今回は下記のようにApp
の箇所で使用しました。
@main struct ExampleApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate var body: some Scene { WindowGroup { ContentView() .onOpenURL { url in print("called onOpenURL:", url) } } } }
onOpenURL
onOpenURL
は、Viewが含まれているSceneまたはWindowのURLを受け取った時に呼び出されるハンドラーで、noteの箇所にも下記の記載がありました。
This method handles the reception of Universal Links, rather than a NSUserActivity.
NSUserActivityではなく、このメソッドはユニバーサルリンクの受信を処理します。
iOS 14からのメソッドになりますが、このメソッドを使用することでユニバーサルリンクを受信することが出来ました。
onOpenURLはDeferred Deep Linkでは呼ばれない
アプリがまだインストールされてない場合に、Dynamic Linkを踏み、App Storeのダウンロードページに遷移し、そこでアプリをインストール後初めて起動した時には、onOpenURL
メソッドは呼ばれませんでした。
Deferred Deep Linkの際には、onOpenURL
は呼ばれず、AppDelegate
のapplication(_:open:options:)
が呼ばれていたのでこのメソッド内で処理を行う必要があります。
終わりに
最終的には無事にリンクを受信することが出来てよかったです。
もし、「SwiftUIライフサイクルでもAppDelegateやSceneDelegateでリンクを受け取れる方法あるよ」という方がいましたら優しく教えていただければと思います。