Я пишу расширение приложения 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
будет равна нулю, и все будет сделано до того, как будет предпринята любая попытка получить 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
необходимы, чтобы знать, что мы не имеем дело с нулевым значением. Мы должны быть уверены, что обработчик завершения вернется, и поэтому .leave()
вызов, прежде чем мы сможем вызвать .enter()
, чтобы вернуться к нулю.
Я даже не могу похоронить все это уродство в некотором роде getURLForPage()
функция или расширение (добавление какого-либо вида SFSafariApplication.getPageProperties
было бы моим предпочтением), поскольку очевидно, что вы не можете вернуться из функции из блока .notify
.
Хотя я пытался создать функцию с использованием queue.wait
и другой DispatchQueue
, как описано в следующем ответе, чтобы иметь возможность использовать return…
{ ссылка }
… для меня неудивительно, что это вызывает тупик , поскольку .wait
все еще выполняется в главной очереди.
Есть ли лучший способ добиться этого? Кстати, «делом» является обновление пользовательского интерфейса по запросу пользователя, поэтому он должен находиться в главной очереди.
Редактировать: Во избежание сомнений, это не вопрос iOS. Хотя применяются аналогичные принципы, расширения приложений Safari являются функцией Safari только для macOS.