Я пытаюсь изменить массив объектов, которые я извлекаю из асинхронного вызова, с результатами другого асинхронного вызова. В основном я получаю массив результатов, и есть поле messages
, которое возвращает nil из моего вызова сервера. Затем мне нужно сделать еще один вызов сервера с я бы каждого result
в цикле for, чтобы получить массив messages
. Затем я устанавливаю messages
на возвращаемое значение.
Я только что узнал об использовании ДиспетчерГрупп() около 20 минут назад от https://stackoverflow.com/a/48718976/3272438, так что терпите меня.
Проблема, которую я получаю, заключается в том, что когда я делаю print("C0: (собственное.разрешение)"), он распечатывает все элементы, включая только что добавленный messages
. Когда print("C2: (собственное.разрешение)") печатается в group.notify(...)
, все поля messages
выводят ноль. Я в тупике, и я перепробовал все, что мог придумать. Любая помощь приветствуется.
ОБНОВИТЬ При изменении следующего group.notify() никогда не вызывается
Service.getMessages(resultId: self.res![index].id, completionHandler: { (latestMessage, response, error) in
if let latestMessage = latestMessage {
self.res![index].messages = [latestMessage]
print("L2: \(self.res![index].messages)")
print("C0: \(self.res)")
}
group.leave() // continue the loop
})
ViewController.Swift
var res: [ResultDTO]?
override func viewDidLoad() {
super.viewDidLoad()
let group0 = DispatchGroup()
group0.enter()
Service.getResults { (results, response, error) in
guard var results = results else { print("PROBLEM"); return }
self.res = results
group0.leave()
}
group0.notify(queue: .main) {
print("in group0")
let group = DispatchGroup() // initialize
for index in 0..<self.res!.count {
group.enter() // wait
Service.getMessages(resultId: self.res![index].id, completionHandler: { (latestMessage, response, error) in
if let latestMessage = latestMessage {
self.res![index].messages = [latestMessage]
print("L2: \(self.res![index].messages)")
print("C0: \(self.res)")
}
})
group.leave() // continue the loop
}
group.notify(queue: .main) {
print("In group notify")
do {
print("C2: \(self.res)")
for index in 0..< self.res!.count {
print("L4: \(self.res![index].messages)")
}
// ... configure my view with the data
} catch {
print("Error: \(error)")
}
}
}
}
Да, я полагаю, это возможно. Мне придется заняться перепроектированием базы данных, но пока я хотел бы понять, почему этот, казалось бы, приличный код не будет работать.
Какая у вас настройка базы данных? Бессерверный NoSQL?
Использование базы данных Oracle и бэкенда Java с Hibernate.
У меня нет опыта работы с базами данных Oracle, поэтому я не знаю, как лучше всего их использовать. На вашем месте я бы потратил время на выяснение того, как упростить сбор данных. Можно ли сократить его до одного сетевого звонка? Почему не решаются денормализовать эти данные?
Мне нужно будет немного спланировать, как упростить поиск данных. Именно так мы его разработали, вместо того, чтобы возвращать все данные сразу, когда они не нужны, просто возвращать необходимые фрагменты информации. Код. Я попытался переместить его, но он не вводит group.notify()
и не печатает In group notify
.
Покажите, пожалуйста, определение getMessages
. Описанное поведение предполагает, что ваш getMessages
не закодирован должным образом.
Я бы упростил обработку ваших данных, используя одну группу диспетчеризации (поскольку вам нужна только одна):
class YourViewController: UIViewController {
private var results: [ResultDTO]?
override func viewDidLoad() {
super.viewDidLoad()
getResults()
}
private func getResults() {
Service.getResults { (results, response, error) in
guard let results = results,
results.count > 0 else {
return
}
let dispatchGroup = DispatchGroup()
for r in results {
dispatchGroup.enter()
Service.getMessages(resultId: r.id, completionHandler: { (latestMessage, response, error) in
if let latestMessage = latestMessage {
r.messages = [latestMessage]
}
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main, execute: {
self.results = results
})
}
}
}
Считайте это только отправной точкой. Если бы это был производственный код, я бы реализовал обработку ошибок и фоновую очередь (при условии, что данные возвращаются в основном потоке).
Пытаюсь реализовать и получаю следующую ошибку: Incorrect argument label in call (have 'queue:execute:', expected 'queue:work:')
with dispatchGroup.notify(...)
Вы используете notify(queue: DispatchQueue, work: DispatchWorkItem)
, когда вы должны использовать notify(queue: DispatchQueue, execute: () -> Void)
Или просто используйте завершающее замыкание, как вы сделали dispatchGroup.notify(queue: .main) { ... }
Я не уверен, что он попадает в dispatchGroup.notify(...)
, потому что, если я поставлю print("hello")
, ничего не будет напечатано.
Вы все еще не используете несколько групп отправки, не так ли?
Нет, я скопировал и вставил ваш код и получил только одну группу
Каков тип данных results
из первого сетевого звонка?
Пользовательская структура resultDTO
с { id, messages, timestamp, ... }
, и я просто хочу установить поле messages
. Если я поставлю операторы печати, они будут напечатаны везде, кроме как внутри dispatchGroup.notify(...)
results
— это массив resultDTO
, верно? Когда вы печатаете при отправке ввода и отправке отпуска, печатается ли одинаковое количество входов и выходов, и равно ли это количество результатов, возвращенных из первого сетевого вызова?
Да, это массив resultDTO. Начальный размер 20, при входе = 20, при выходе = 6. Я добавил к вашему ответу свои операторы печати. A распечатывает AAA: 0 AAA: 1 AAA: 2 AAA: 3 AAA: 4 AAA: 5 AAA: 6 AAA: 7 AAA: 8 AAA: 9 AAA: 10 AAA: 11 AAA: 12 AAA: 13 AAA: 14 AAA: 15 AAA: 16 AAA: 17 AAA: 18 AAA: 19
, а B распечатывает: BBB: 0 BBB: 1 BBB: 6 BBB: 4 BBB: 15 BBB: 18
Пока не повезло... Все еще не попал в notify
Тогда это почти наверняка ваши данные или сервер. Убедитесь, что все данные действительно существуют. Также в Service.getMessages
вернитесь, if let error = error { print(error) }
и посмотрите, возвращает ли сервер ошибку. Код правильный, и мы знаем, что он работает, потому что он повторяет цикл, совершая нужное количество сетевых вызовов. Он просто не возвращает то же количество результатов, что указывает на ошибку на стороне сервера.
Да, я только что понял, что это, скорее всего, серверная сторона, и я смотрю на это сейчас.
Есть ли причина, по которой вы не используете Google Firestore? Кстати, 20 последовательных обращений к базе данных, возможно, нарушают ограничение сервера, что является еще одной причиной для реструктуризации ваших данных, чтобы ограничить захват данных одним вызовом. Когда сообщения создаются, делайте две записи вместо одной, и тогда вы сможете получить все эти данные за один раз.
Мы только что выбрали собственную реализацию серверной части, а не Google Firebase или Firestore. Просто так все получилось. Да, определенно нужно реструктурировать данные, чтобы их было проще получить, чем несколько вызовов. НО, с другой стороны, я нашел свою проблему. Это случилось в моем вызове API. У меня есть метод автоматического декодирования данных в требуемый тип объекта, и если decode() не удалось, он просто не возвращается. Я добавил обработчик завершения(), и теперь он входит в блок notify
, но все еще не имеет messages
в объекте.
Кстати, уродливая архитектура базы данных, верно? Можно ли исправить архитектуру данных вместо того, чтобы прыгать через эти обручи на клиенте?