Использование DispatchGroup или некоторой конструкции concurency для последовательной загрузки данных и заполнения ячеек в UITableViewController
Платформа:
Я использую Swift 4 и xcode 11.4
Пример использования и желаемое поведение
Приложение загружает фид, содержащий потенциально 100 или 1000 элементов, скажем, 500 элементов. 500 предметов будут взяты после использованияAmplify
с GraphQL
запрос, то каждый элемент будет загружать дополнительные данные. Данные будут заполнять ячейки вUITableViewController
. В идеале этот процесс должен происходить в следующей точной последовательности:
query
500 шт.- cell_1 загрузить дополнительные данные.
- cell_1 отображает данные и отображает их в
UITableViewController
- cell_2 загружает дополнительные данные.
- cell_2 отображает данные и отображает их в
UITableViewController
...
- cell_500 загрузить дополнительные данные
- cell_500 отображает данные и отображает их в
UITableViewController
Таким образом, пользователь увидит "водопад" ячеек, отображаемых в фиде.
Вопрос
Это похоже на вариант использования, который требует более точного контроля над выполнением, для чего это понадобится: https://developer.apple.com/documentation/dispatch/dispatchgroup
Я новичок в Swift, поэтому для меня это немного продвинуто. Предоставляется заглушка дляGraphQL
запрос и функция класса, которая загружает дополнительные данные, и верхний уровень UITableViewController
. Пожалуйста, проинструктируйте, как я буду использоватьDispatchGroup
.
class Feed: UITableViewController {
var dataSource: [FullItem] = []
override func viewDidLoad(){
super.viewDidLoad()
queryItem{ items
for item in items {
let itemInstanceWithMoreData = FullItem( id: item.id )
itemInstanceWithMoreData.loadFullData()
}
}
}
}
func queryItems( callBack: @escaping ([Item]) -> Void ){
_ = Amplify.API.query(from: Item.self, where: predicate) { (event) in
switch event {
case .completed(let result):
switch result {
case .success(let xs):
callBack(xs)
case .failure:
break
}
case .failed:
break
default:
break
}
}
}
class FullItem {
id: String
name: String?
init( id ){ self.id = id; self.name = "" }
func loadData(){
let _ = Amplify.API.query(from: FullItem.self, byId: self.id) { (event) in
switch event {
case .completed(let res):
switch res{
case .success (let musr):
if (musr != nil){
self.name = musr!.name
} else {
break
}
default:
break
}
default:
print("failed")
}
}
}
}
добавление
Если последовательность, о которой я прошу, невозможна, я бы также согласился на query
500 шт., load
дополнительные данные для каждого, а затем рендеринг ячеек. Но в любом случае ячейка не должна отображаться с пустыми данными.
1 ответ
Ваш пример неполный и не компилируется, поэтому это короткая версия
Объявить loadData()
func loadData(completion: @escaping () -> Void) {
и убедитесь, что completion()
вызывается в любом случае (это критично!) например
default:
completion()
print("failed")
Использовать DispatchGroup
правильно ты должен позвонить enter
внутри цикла перед вызовом асинхронной задачи и вызовомleave
в обработчике завершения задачи. В конце вне цикла реализоватьnotify
override func viewDidLoad(){
super.viewDidLoad()
let group = DispatchGroup()
queryItems { items
for item in items {
group.enter()
let itemInstanceWithMoreData = FullItem( id: item.id )
itemInstanceWithMoreData.loadData {
group.leave()
}
}
group.notify(queue: .main) {
self.tableView.reloadData()
}
}
}
Чтобы последовательно вставлять и обновлять элементы , вам понадобится асинхронныйOperation
и сериал OperationQueue
. DispatchGroup
не сохраняет порядок.