16
15

More than 3 years have passed since last update.

[iOS13]SceneDelegateでURL Schemeのリクエストを受取る(追記あり)

Last updated at Posted at 2019-11-22

概要

URL Schemeで他アプリと連携して、callbackで自分のアプリに戻ってくる処理を実装しています。
iOS12まではAppDelegateの下記メソッドでリクエストを受け取っていましたが、iOS13で受け取れなくなっていました。

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool

iOS13 application openUrl? |Apple Developer Forums

SceneDelegateを代わりに使用する」ということはすぐわかったのですが、
まだSceneDelegateを導入していなかったこともあり少々手こずりました...
内容としてはあまり多くないですが備忘録としてまとめます。

環境

  • iOS 13.2.3
  • Xcode 11.2.1
  • Swift5

実装

iOS12まで

これまでのURL Schemeのリクエスト受取はAppDelegateで以下のように実装していました。
アプリのURLを外部からアクセスされた時にapplication(_:open:options:)
が実行されます。
sourceapplicationでURLリクエストを送信したアプリのBundleIdを判定して、
どの画面の処理へ遷移させるか分岐させていました。

AppDelegate.swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{
    // 省略

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        guard let sourceApplication = options[.sourceApplication] as? String else {
            return false
        }

        if sourceApplication.hasPrefix("--他アプリのBundleId--") {
            // クエリパラメータを受けて色々処理
            // ...
        }

        return true
    }
}

iOS13から

1. SceneDelegateを呼べるように設定

SceneDelegateをそもそも用意していなかったので、自分はまずそこからやりました。
AppDelegate、Info.plistへの追記、SceneDelegateのベース作成は
下記の記事で分かりやすくまとめてありましたのでご参考ください!

【Xcode11】Storyboardを使わずコードだけで画面を生成する方法 - Qiita

2. SceneDelegateに追記

SceneDelegateのscene(_:openURLContexts:)が代わりに呼ばれるメソッドということで、ここに処理を書いていきます。

SceneDelegate.swift
import UIKit

@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate
{
    // ↓----以下追加----↓

    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)
    {
        // リクエストURLの取得
        guard let url = URLContexts.first?.url else {
            return
        }

        let sourceApplication = URLContexts.first?.options.sourceApplication
        // あれ....?

    }
}

あれ....。
AppDelegateと同じようにsourceApplicationを取得しようとしましたがなぜかnilで返ってくる...
うまくいかない。さてどうしたものか。

3. コールバックURLを識別できるものに変更

コールバックURLをユニークにすることで、レスポンスを受けた後にどの処理をするか判定できるだろう!
ということで例としてcall_testというホスト名をつけてみました。

Sample.swift
let callbackURL = URL(string: "myapp://call_test")!

4. SceneDelegateに改めて追記

call_testというホストでリクエストが来ているか判定するために、
URLComponentsからホスト名を取得します。

SceneDelegate.swift
@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate
{
    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)
    {
        // コールバックで来たURLの取得
        guard let url = URLContexts.first?.url else {
            return
        }

        // ↓----以下追加----↓

        // ホスト名の取得
        guard let components = URLComponents(string: url.absoluteString), let host = components.host else {
            return
        }

        if host == "call_test" {
            // クエリパラメータを受けて色々処理
            // ...
        }
    }
}

これでURL Schemeの対応ができました!

参考

追記(2019.12.17)

あとでわかったこと。
AppDelegateのapplication(_:open:options:)はiOS13以降も動いておりました...
動かなくなっていたと思ったのはココ↓

AppDelegate.swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{
    // 省略

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        // ↓コレ
        guard let sourceApplication = options[.sourceApplication] as? String else {
            return false
        }

        if sourceApplication.hasPrefix("--他アプリのBundleId--") {
            // クエリパラメータを受けて色々処理
            // ...
        }

        return true
    }
}

SceneDelegateでもそうでしたが、sourceApplicationが受け取れなくなっていただけのようです...
理由はまだ調査しきれておりませんが。
大変失礼しました...

16
15
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
15