I am on swift 4 and xcode 11.4
The app is loading a feed with potentially 100s or 1000s of items, let's say 500 items. The 500 items will be grabbed once using Amplify
's GraphQL
query, then each item will then load additional data. The data will populate the cells in a UITableViewController
. Ideally this process would happen in the following exact sequence:
query
500 itemsUITableViewController
UITableViewController
...
UITableViewController
So the user will see a "waterfall" of cells being rendered in the feed.
This seems like a use case that require some finer control of execution, which would need this: https://developer.apple.com/documentation/dispatch/dispatchgroup
I am new to Swift so this is a bit advanced for me. Provided is the stub for GraphQL
query, and class function that loads additional data, and the top level UITableViewController
. Please instruct how I would use 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")
}
}
}
}
If the sequence I am asking is not possible, I would also settle for query
500 items, load
additional data for each one, then rendering the cells. But either way, the cell should not render with empty data.
Your example is incomplete and doesn't compile so this is the short version
Declare loadData()
func loadData(completion: @escaping () -> Void) {
and make sure that completion()
is called in any case (this is crucial!) for example
default:
completion()
print("failed")
To use DispatchGroup
properly you have to call enter
inside the loop before calling the asynchronous task and call leave
in the completion handler of the task. At the end outside the loop implement 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()
}
}
}
To insert and update the items serially in order you need an asynchronous Operation
and a serial OperationQueue
. DispatchGroup
doesn't preserve the order.