在嵌套完成处理程序之后执行代码



我正在编写一个 Safari 应用程序扩展,并希望在我的视图控制器中获取活动页面的 URL。

这意味着嵌套的完成处理程序用于提取窗口、获取选项卡、获取页面和访问其属性。 烦人但足够简单。 它看起来像这样:

func doStuffWithURL() {
var url: URL?
SFSafariApplication.getActiveWindow { (window) in
window?.getActiveTab { (tab) in
tab?.getActivePage { (page) in
page?.getPropertiesWithCompletionHandler { (properties) in
url = properties?.url
}
}
}
}
// NOW DO STUFF WITH THE URL
NSLog("The URL is (String(describing: url))")
}

明显的问题是它不起作用。 作为完成处理程序,它们在函数结束之前不会执行。 变量url将为 nil,并且在尝试获取 URL 之前将完成这些操作。

解决此问题的一种方法是使用DispatchQueue. 它可以工作,但代码真的很丑陋:

func doStuffWithURL() {
var url: URL?
let group = DispatchGroup()
group.enter()
SFSafariApplication.getActiveWindow { (window) in
if let window = window {
group.enter()
window.getActiveTab { (tab) in
if let tab = tab {
group.enter()
tab.getActivePage { (page) in
if let page = page {
group.enter()
page.getPropertiesWithCompletionHandler { (properties) in
url = properties?.url
group.leave()
}
}
group.leave()
}
}
group.leave()
}
}
group.leave()
}
// NOW DO STUFF WITH THE URL
group.notify(queue: .main) {
NSLog("The URL is (String(describing: url))")
}
}

需要if块才能知道我们没有处理零值。 我们需要确定完成处理程序将返回,因此在调用.enter()以最终返回零之前,需要.leave()调用。

我什至不能将所有丑陋隐藏在某种getURLForPage()函数或扩展中(添加某种SFSafariApplication.getPageProperties是我的偏好(,因为显然您无法从.notify块中的函数返回。

尽管我尝试使用queue.wait和不同的DispatchQueue创建一个函数,如以下答案中所述,以便能够使用 return...

https://stackoverflow.com/a/42484670/2081620

。对我来说,这并不奇怪,它会导致死锁,因为.wait仍在主队列上执行。

有没有更好的方法来实现这一目标? 顺便说一下,"要做的事情"是根据用户请求更新 UI,因此需要在主队列上。

编辑:为免生疑问,这不是iOS问题。 虽然类似的原则适用,但 Safari 应用扩展仅是 macOS 版 Safari 的一项功能。

感谢 Larme 在评论中的建议,我想出了一个隐藏丑陋、可重用并保持代码干净和标准的解决方案。

嵌套完成处理程序可以替换为SFSafariApplication类的扩展,以便在代码的主体中只需要一个。

extension SFSafariApplication {
static func getActivePageProperties(_ completionHandler: @escaping (SFSafariPageProperties?) -> Void) {
self.getActiveWindow { (window) in
guard let window = window else { return completionHandler(nil) }
window.getActiveTab { (tab) in
guard let tab = tab else { return completionHandler(nil) }
tab.getActivePage { (page) in
guard let page = page else { return completionHandler(nil) }
page.getPropertiesWithCompletionHandler { (properties) in
return completionHandler(properties)
}
}
}
}
}
}

然后在代码中它可以用作:

func doStuffWithURL() {
SFSafariApplication.getActivePageProperties { (properties) in
if let url = properties?.url {
// NOW DO STUFF WITH THE URL
NSLog("URL is (url))")
} else {
// NOW DO STUFF WHERE THERE IS NO URL
NSLog("URL ERROR")
}
}
}

相关内容

  • 没有找到相关文章

最新更新