Вместо ожидания вы должны просто создать локальный массив значений для добавления, а затем добавить их, когда это будет сделано:
func retrieveSnacks() {
var snacksToAdd: [Snack] = []
let group = DispatchGroup()
...
for url in urls {
group.enter()
fetchSnack(with: url) { result in
dispatchPrecondition(condition: .onQueue(.main)) // note, I’m assuming that this closure is running on the main queue; if not, dispatch this appending of snacks (and `leave` call) to the main queue
if case .success(let snack) = result {
snacksToAdd.append(snack)
}
group.leave()
}
}
// when all the `leave` calls are called, only then append the results
group.notify(queue: .main) {
self.snacks += snacksToAdd
// trigger UI update, or whatever, here
}
}
Обратите внимание, что приведенное выше не гарантирует, что объекты будут добавлены вОригинальный заказ. Если вам это нужно, вы можете использовать словарь для построения временных результатов и затем добавлять результаты в отсортированном порядке:
func retrieveSnacks() {
var snacksToAdd: [URL: Snack] = [:]
let group = DispatchGroup()
...
for url in urls {
group.enter()
fetchSnack(with: url) { result in
if case .success(let snack) = result {
snacksToAdd[url] = snack
}
group.leave()
}
}
group.notify(queue: .main) {
let sortedSnacks = urls.compactMap { snacksToAdd[$0] }
self.snacks += sortedSnacks
// trigger UI update, or whatever, here
}
}
Наконец, я мог бы предложить принять шаблон обработчика завершения:
func retrieveSnacks(completion: @escaping ([Snack]) -> Void) {
var snacksToAdd: [URL: Snack] = [:]
let group = DispatchGroup()
...
for url in urls {
group.enter()
fetchSnack(with: url) { result in
if case .success(let snack) = result {
snacksToAdd[url] = snack
}
group.leave()
}
}
group.notify(queue: .main) {
let sortedSnacks = urls.compactMap { snacksToAdd[$0] }
completion(sortedSnacks)
}
}
retrieveSnacks { addedSnacks in
self.snacks += addedSnacks
// update UI here
}
Этот шаблон гарантирует, что вы не перепутаете свой сетевой код с вашим кодом пользовательского интерфейса.
Я извиняюсь, что вышеприведенное несколько отреагировало из вашего фрагмента кода, но этого было недостаточнотам для меня, чтобы проиллюстрировать, как именно это будет выглядеть. Но, надеюсь, вышеприведенный пример иллюстрирует шаблон, и вы можете увидеть, как вы примените его к своей кодовой базе. Таким образом, не заблудитесь в деталях, но сосредоточьтесь на базовом шаблоне построения записей, которые нужно добавить в локальную переменную, и обновите только окончательные результаты в блоке .notify
.
FWIW, этосигнатура метода для метода, который используются вышеупомянутыми фрагментами для асинхронного извлечения рассматриваемых объектов.
func fetchSnack(with url: URL, completion: @escaping (Result<Snack, Error>) -> Void) {
...
// if async fetch not successful
DispatchQueue.main.async {
completion(.failure(error))
}
// if successful
DispatchQueue.main.async {
completion(.success(snack))
}
}