我正在编写一个 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()
在我们可以调用 a 之前的调用.enter()
最终会返回为零。
我什至不能在某种getURLForPage()
函数或扩展中隐藏所有丑陋(添加某种SFSafariApplication.getPageProperties
是我的偏好),因为显然你不能从.notify
块内的函数返回。
虽然我尝试使用queue.wait
和创建一个函数DispatchQueue
,如下面的答案中描述的那样能够使用 return...</p>
https://stackoverflow.com/a/42484670/2081620
…对我来说并不奇怪它会导致死锁,因为.wait
它仍在主队列上执行。
有没有更好的方法来实现这一目标?顺便说一句,“要做的事情”是根据用户请求更新 UI,因此需要在主队列中。
编辑:为免生疑问,这不是 iOS 问题。虽然适用类似原则,但 Safari 应用程序扩展只是 macOS 版 Safari 的一项功能。