Использование `DispatchGroup` или некоторой конструкции параллелизма для последовательной загрузки данных и заполнения ячеек в UITableViewController - PullRequest
0 голосов
/ 25 апреля 2020

Платформа:

Я на Swift 4 и XCode 11,4

Вариант использования и желаемое поведение

Приложение загружает канал с потенциально 100 или 1000 элементов, скажем, 500 предметов. 500 элементов будут получены один раз с помощью запроса Amplify GraphQL, затем каждый элемент будет загружать дополнительных данных. Данные будут заполнять ячейки в UITableViewController. В идеале этот процесс должен происходить в следующей точной последовательности:

  1. query 500 элементов
  2. cell_1 загружать дополнительные данные.
  3. cell_1 отображать данные и отображать в UITableViewController
  4. cell_2 загрузить дополнительные данные.
  5. cell_2 отображать данные и отображать в UITableViewController

...

cell_500 загрузить дополнительные данные cell_500 отображать данные и отображать в UITableViewController

Таким образом, пользователь увидит «водопад» ячеек, отображаемых в фиде.

Вопрос

Это похоже на случай использования, который требует более точного контроля выполнения, для которого понадобится следующее: https://developer.apple.com/documentation/dispatch/dispatchgroup

Я новичок Свифт, так что это немного продвинутый для меня. Предоставляется заглушка для запроса 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")
            }
        }
    }
}

addendum

Если последовательность, которую я спрашиваю, невозможна, я также согласился бы на query 500 пунктов, load дополнительные данные для каждого, затем рендеринг клеток. Но в любом случае, ячейка не должна отображаться с пустыми данными.

1 Ответ

2 голосов
/ 25 апреля 2020

Ваш пример неполон и не компилируется, поэтому это короткая версия

Объявите loadData()

func loadData(completion: @escaping () -> Void) {

и убедитесь, что completion() вызывается в любой случай (это крайне важно!), например

default:
    completion()
    print("failed")

Для правильного использования DispatchGroup необходимо вызвать enter внутри l oop перед вызовом асинхронная задача и вызов leave в обработчике завершения задачи. В конце снаружи орудие l oop 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 не сохраняет заказ.

...