Использование DispatchGroup или некоторой конструкции concurency для последовательной загрузки данных и заполнения ячеек в UITableViewController

Платформа:

Я использую 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

...

  1. cell_500 загрузить дополнительные данные
  2. 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 не сохраняет порядок.

Другие вопросы по тегам