AppDelegate和SceneDelegate实现Deep Links

9

我正努力实现深度链接,以便在应用程序中导航到帖子。这是一个较旧的项目,因此我必须添加 SceneDelegate 类。只有当应用程序处于活动或后台状态时,深度链接实现才有效。如果应用程序尚未加载,则深度链接将无法使用。我看过许多帖子和教程,但仍然不清楚原因。有人遇到类似问题吗?

AppDelegate 类中,我已添加了以下函数的链接处理实现:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {}

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

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {}

func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {}

SceneDelegate中,我实现了以下函数来处理链接:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {}

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {}

这些函数的实现看起来像这样:
let navigator = Navigator()
navigator.getDesination(for: url)


func getDesination(for url: URL){
    let destination = Destination(for: url)
    let ivc = InstantiateViewController()
    switch destination {
    case .post(let postID):
        ivc.openPostVC(id: postID, showComment: true, commentID: nil)
    case .user(let userID):
        ivc.openProfileVC(userID: userID)
    default:
        break
    }
}

enum Destination {
    case post(Int)
        
    case user(Int)
            
    case feed(String)
            
    case store
            
    case safari
            
    init(for url: URL){
        if(url.pathComponents[1] == "p"){
            self = .post(Int(url.pathComponents[2])!)
        } else if(url.pathComponents[1] == "user") {
            self = .user(Int(url.pathComponents[2])!)
        } else if(url.pathComponents[1] == "store") {
            self = .store
        } else if(url.pathComponents[1] == "s") {
            self = .feed(url.pathComponents[2])
        } else {
            self = .safari
        }
    }
}


func openProfileVC(userID: Int){
    let service = UserPool.shared.request(for: userID)
                    
    let storyboard = UIStoryboard(name: "Profile", bundle: nil)
    let profileVC = storyboard.instantiateViewController(withIdentifier: "ProfileView") as! ProfileViewController
    profileVC.userService = service
    profileVC.shouldNavigateToHome = true
    profileVC.shouldNavigateToHomeAction = {
        self.loadMainStoryboard()
    }
        
    let navigationVC = UINavigationController(rootViewController: profileVC)
    navigationVC.view.backgroundColor = .white
    
    if #available(iOS 13.0, *) {
        guard let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate else {return}
        sceneDelegate.window?.rootViewController = navigationVC
        sceneDelegate.window?.makeKeyAndVisible()
    } else {
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
        appDelegate.window?.rootViewController = navigationVC
        appDelegate.window?.makeKeyAndVisible()
    }
}

该网站的app-site-association文件如下,并已在Xcode中添加关联的域名:
{"applinks":{"apps":[],"details":[{"appID":"{my ID}","paths":["*"]}]},"webcredentials":{"apps":["{my ID}"]}}

这些函数的实现看起来像这样。为什么不展示实际的代码行呢? - El Tomato
长度,但如果有帮助的话,我可以发布它,尽管我认为不需要。 - ShedSports
@ShedSports - 当人们要求你这样做时,没有必要发布不必要的代码 :) - Fattie
4个回答

17

使用场景代理在 iOS 13 及更高版本中,您的应用可以在启动时像这样观察传入的通用链接事件:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    if let url = connectionOptions.userActivities.first?.webpageURL {
       // ... or might have to cycle thru multiple activities
    }
}

如果应用程序已经在运行中,则使用此选项:

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    if let url = userActivity?.webpageURL {
        // ...
    }
}

我有一个非常简单的可下载演示应用程序,它证明这确实有效。我不明白为什么有人声称它无效;也许问题在于不了解如何测试。


我已经实现了。 - ShedSports
我将尝试循环遍历userActivities以查找browsingWeb,虽然我几乎可以确定我以前尝试过它。 - ShedSports
1
可以在SceneDelegate中使用openURLContexts。func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {}https://developer.apple.com/documentation/uikit/uiscenedelegate/3238059-scene - charlemagne_vi

1

来自苹果文档

如果您的应用程序选择了场景,并且您的应用程序未运行,则系统会在启动后将URL传递给场景(:willConnectTo:options:)委托方法,并在应用程序在运行或挂起内存时将其传递给场景(:openURLContexts:)。

完整示例:

当应用程序终止时,在场景委托中:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let url = connectionOptions.urlContexts.first?.url
}

并且当应用程序在后台或前台时:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    let url = URLContexts.first?.url
}

0
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

//---------
//-------

 func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
  for context in URLContexts {
    print("url: \(context.url.absoluteURL)")
    print("scheme: \(context.url.scheme)")
    print("host: \(context.url.host)")
    print("path: \(context.url.path)")
    print("components: \(context.url.pathComponents)")
  }
 }

}

-2

我找不到答案,所以决定解决这个问题。我回退到仅使用 AppDelegate 的情况下,此时只有当应用程序处于活动状态或后台时深度链接才能正常工作。为了解决这个问题,我决定将 URL 存储在 UserDefaults 中。因此,在 didFinishLaunchingWithOptions 函数中,我添加了以下内容:

if let url = launchOptions?[UIApplication.LaunchOptionsKey.url] as? URL {
            UserDefaults.setURLToContinue(urlString: url.absoluteString)
        } else if let activityDictionary = launchOptions?[UIApplication.LaunchOptionsKey.userActivityDictionary] as? [AnyHashable: Any] {
            for key in activityDictionary.keys {
                if let userActivity = activityDictionary[key] as? NSUserActivity {
                       if let url = userActivity.webpageURL {
                        UserDefaults.setURLToContinue(urlString: url.absoluteString)
                    }
                }
            }
        }

这是我创建的UserDefaults扩展:

extension UserDefaults {
    
    class func setURLToContinue(urlString: String){
        UserDefaults.standard.set(urlString, forKey: "continueURL")
    }
    
    class func getURLToContinue() -> String? {
        return UserDefaults.standard.string(forKey: "continueURL")
    }
    
    class func removeURLToContinue(){
        UserDefaults.standard.removeObject(forKey: "continueURL")
    }
    
}

最后,在初始视图控制器的viewDidLoad函数中,我处理链接:
if let urlString = UserDefaults.standard.string(forKey: "continueURL") {
            let url = URL(string: urlString)!
            let navigator = Navigator()
            navigator.getDesination(for: url)
            UserDefaults.removeURLToContinue()
        }

Navigator类决定将哪个视图控制器推入导航栈

这之后一切都完美地运作了。


1
很抱歉我要坚持,但这是错误的。我有一个简单的例子,你可以下载并运行https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/bk2ch24p846UniversalLinksTest,它非常清楚地在启动时接收到通用链接。 - matt
@ShedSports 在 AppDelegate 方面没有问题,但是去掉 SceneDelegate 不是一个解决方案,因为它与问题本身相矛盾。 - Rajan Maheshwari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接