我正在编写一个 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")
}
}
}