[英]Executing code after nested completion handlers
我正在编写一个 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
块需要知道我们没有处理一个 nil 值。 我们需要一定的完成处理程序将返回,并因此.leave()
调用之前,我们可以调用.enter()
到后端向上为零。
我什至无法在某种getURLForPage()
函数或扩展中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")
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.