iOS の Deep Link (Custom URL Scheme) を定義して別アプリから機能を呼び出す機会があり、30分もあれば実装できるやろと思っていたら事故ったので知見を書きます。
要約
- SceneDelegate があるときはそちらが呼ばれるから AppDelegate 側に書いた処理は動かない
- アプリが起動していないときは
scene(_:openURLContexts:)
が呼ばれず、scene(_:willConnectTo:options:)
を使わないといけない。このときはconnectionOptions.urlContexts
で url が取れる - 用語は正しく使おう。公式のドキュメントはしっかり読もう
事故の記録
たしか、AppDelegateに何か実装すればよかったんだよな……
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
debugPrint("openUrl \(url)")
return processLink(url)
}
→ アプリはきちんと起動したが、何やってもメソッド application(_:open:options:)
が呼ばれていない
iOS 13 から同一のアプリのウィンドウを複数開けるようになったので、その関連です。今回のアプリは SceneDelegate を持っていました。これが存在すると AppDelegate 側で呼ばれないメソッドが出てきます。
なるほど、scene(_:openURLContexts:) に書けばいいんだな
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
debugPrint("scene(_:openURLContexts:)")
guard let url = URLContexts.first?.url else {
return
}
debugPrint("openUrl \(url)")
let _ = processLink(url)
}
実装して意図したとおりに動いて一安心した後で、Xcodeの停止ボタンからアプリを落としてからもう一度 Deep Link からの起動を試す
→ scene(_:openURLContexts:)
が呼ばれないでそのままアプリが起動する
どうやらアプリが起動していないときの Deep Link は scene(_:openURLContexts:)
を呼ばないので scene(_:willConnectTo:options:)
で処理しないといけません。
scene(_:willConnectTo:options:) を使うのか……あれ? url どうやって持ってくるんだ?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
...
if let url = connectionOptions.urlContexts.first?.url {
debugPrint("openUrl \(url)")
let _ = processLink(url)
}
}
→ connectionOptions.urlContexts で取れます。
そもそもなんで事故ったのか
- 公式のドキュメントの下のほうに書いてあったのを見落とした
- 検索が雑だった
俗称の 『Deep Link』 で検索して出てきた記事を読むよりも、正式な名称の 『Custom URL Scheme』 で 「custom url scheme openurlcontexts not called」とやったり、「openurlcontexts not called」のようにきちんと名指ししたりすれば割と簡単に出てきました。疲れているとGoogle検索力が落ちますね……。
ここにたどり着くまでが長かった:
When the app is killed and opened by a custom URI, it doesn’t trigger
https://developer.apple.com/forums/thread/124826func scene(_:openURLContexts:)
but by the normal startup functionfunc scene(_:willConnectTo:options)
where connectionOptions contains your urlContexts.
とくに何も書いてない scene(_:openURLContexts:) https://developer.apple.com/documentation/uikit/uiscenedelegate/3238059-scene
公式のドキュメント Defining a Custom URL Scheme for Your App。きちんと書いてある
Handle Incoming URLs
の SceneDelegateでの実装コードまで読んだところでそのまま閉じていたのですが、よく見るとその下に
If your app has opted into Scenes, and your app is not running, the system delivers the URL to the
https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-appscene(_:willConnectTo:options:)
delegate method after launch, and toscene(_:openURLContexts:)
when your app opens a URL while running or suspended in memory.
と書いてありました。つらい。