如何在处理通用链接之前等待 didFinishLaunchingWithOptions 完成?

Posted

技术标签:

【中文标题】如何在处理通用链接之前等待 didFinishLaunchingWithOptions 完成?【英文标题】:How to wait for didFinishLaunchingWithOptions to finish before handling universal link? 【发布时间】:2021-08-26 20:50:48 【问题描述】:

在我的 Swift ios 应用程序中,我在 AppDelegate 内部的 didFinishLaunchingWithOptions 中异步设置了初始视图控制器。现在我正在添加一个通用链接,这样如果您转到手机上的相应 URL,它将自动在应用程序中打开正确的页面(在这种情况下,它是一个密码重置 URL,如果正确,将打开密码重置表单应用内)。

我正在处理我的AppDelegate 中的application(_:continue:restorationHandler:) (continueUserActivity) 内的链接。

如果应用程序已经在运行,它可以工作,但链接处理发生在我的应用程序完成初始视图控制器设置之前,并且当didFinishLaunchingWithOptions 返回时,深层链接的视图控制器被关闭。

在我从continueUserActivity 展示视图控制器之前,如何确保应用程序已完成设置?在设置视图控制器之前,我不能/不应该阻止 didFinishLaunching 返回,因此该方法在初始视图控制器出现之前返回。

我知道我可以检查didFinishLaunchingWithOptions 中的用户活动字典,但continueUserActivity 仍然被调用(如果仅在应用程序运行时调用它会简单得多,就像didReceiveRemoteNotification 的工作原理一样)。是否有条件检查此方法内部以将链接处理推迟到didFinishLaunching

一些简化的UIApplicationDelegate代码来帮助解释:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 
    doSomeAsyncSetup(
        completion: 
            presentSomeViewController()
        
    )
    return true


func application(_: UIApplication, continue userActivity: NSUserActivity, restorationHandler _: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool 
    if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let url = userActivity.webpageURL,
        let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true),
        let params = components.queryItems,
        components.path == pathThatIExpect
    
            handleUniversalLink(params)
    
    return true

请注意,当应用选择加入 Scenes 但我的应用未使用该选项 (https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app) 时,区别似乎略有不同。

【问题讨论】:

【参考方案1】:

DispatchGroup 应该能够为您处理这个问题。

private let dispatchGroup = DispatchGroup()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 
    self.dispatchGroup.enter()
    doSomeAsyncSetup(
        completion: 
            presentSomeViewController()
            self.dispatchGroup.leave()
        
    )
    return true


func application(_: UIApplication, continue userActivity: NSUserActivity, restorationHandler _: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool 
    if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let url = userActivity.webpageURL,
        let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true),
        let params = components.queryItems,
        components.path == pathThatIExpect
    
            self.dispatchGroup.notify 
                handleUniversalLink(params)
            
    
    return true

在您的应用程序正在启动的情况下,调度组在didFinishLaunching 中输入,并在视图控制器出现后离开。 一旦调度组为空,notify 将执行用户活动代码;所以只有在你的视图控制器出现之后。

如果您的应用已经启动,调度组将为空,因为didFinishLaunching 未被调用,因此notify 将立即执行

【讨论】:

【参考方案2】:

您可以为此使用串行队列。例如:

serialQueue.async 
    self.presentSomeViewController()

serialQueue.async 
    self.handleUniversalLink(params)

【讨论】:

任务 2 将仅在 didFinishLaunching 之后启动,因此这将是解决该问题的最简单方法。 认为您刚刚遗漏了一个引用 serialQueue 的实例变量,例如 let serialQueue = DispatchQueue(label: "someLabel") 不幸的是它在这里并不能真正工作,因为它需要等待异步工作在doSomeAsyncSetup中完成

以上是关于如何在处理通用链接之前等待 didFinishLaunchingWithOptions 完成?的主要内容,如果未能解决你的问题,请参考以下文章

您如何等待主队列完成处理程序?

如何等待http请求在处理到angular 7中的下一步之前获得响应[重复]

如何不以编程方式处理一些通用链接?

如何等待进程终止以在批处理文件中执行另一个进程

如何在调用WS之前等待某事

如何在 J2me 中处理网络线程调用和等待进度?