Using `DispatchGroup` or some concurency construct to load data and populate cells in `UITableViewController` sequentially

青春壹個敷衍的年華 提交于 2020-06-29 03:45:55

问题


Platform:

I am on swift 4 and xcode 11.4

Use case and desired behavior

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:

  1. query 500 items
  2. cell_1 load additional data.
  3. cell_1 render data and display in UITableViewController
  4. cell_2 load additional data.
  5. cell_2 render data and display in UITableViewController

...

  1. cell_500 load additional data
  2. cell_500 render data and display in UITableViewController

So the user will see a "waterfall" of cells being rendered in the feed.

Question

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")
            }
        }
    }
}

addendum

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.


回答1:


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.



来源:https://stackoverflow.com/questions/61431734/using-dispatchgroup-or-some-concurency-construct-to-load-data-and-populate-cel

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!