在刷新 table 视图之前,如何获取视图模型中的存储库列表?

How can I get a list of repositories in the view model before the table view is refreshed?

我正在尝试构建一个搜索 GitHub 存储库的应用程序。我正在使用 MVVM 和 Moya。当我调用搜索方法时,我会在服务器响应之前 table 重新加载视图。

所以应用流程应该是: 用户在搜索栏中输入搜索查询。在 SearchViewControllersearchBarSearchButtonClicked(_:) 调用我的方法进行搜索。该方法在 SearchViewModel 内。所以视图模型的方法触发 RepositoryService 然后 NetworkService.

我放了一些打印语句来查看执行顺序。在控制台中,我得到:2 3 4 (Here the table view is refreshing) 1。我尝试在不同的地方使用 GCD,我也尝试使用障碍。在一天结束时,在调用 print(1) 之前,table 视图仍在刷新。

SearchViewController:

extension SearchViewController: UISearchBarDelegate {
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        guard let query = searchBar.text, query.count > 2 else { return }
        viewModel.searchRepositories(withQuery: query) { [weak self] in
            guard let self = self else { return }
            print(4)
            print("Reloading...")
            self.tableView.reloadData()
        }
    }
}

SearchViewModel:

var repositories = [Repository]()

func searchRepositories(withQuery query: String, completion: @escaping () -> Void) {
    repositories = repositoryService.searchRepositories(withQuery: query)
    print(3)
    completion()
}

存储库服务:

private var repositories = [Repository]()

func searchRepositories(withQuery query: String) -> [Repository] {
    networkService?.searchRepositories(withQuery: query) { [weak self] repositories in
        guard let self = self, let repositories = repositories else { return }
        self.repositories += repositories
    }
    print(2)
    return self.repositories
}

网络服务:

func searchRepositories(withQuery query: String,
                        completionHandler: @escaping (([Repository]?) -> Void)) {
    provider?.request(.searchRepo(query: query)) { result in
        switch result {
        case .success(let response):
            do {
                let repositories = try response.map(SearchResults<Repository>.self)
                print(1)
                completionHandler(repositories.items)
            } catch let error {
                print(error.localizedDescription)
            }

        case .failure(let error):
            print(error)
        }
    }
}

你的问题基本上是你没有正确处理异步请求。

考虑代码:

var repositories = [Repository]()

func searchRepositories(withQuery query: String, completion: @escaping () -> Void) {
    repositories = repositoryService.searchRepositories(withQuery: query) // here we are going to create a network request in the background, which takes time. 
    print(3)
    completion() // when you call this, your network request is still trying, so when your tableView refreshes... the data hasn't returned yet. 
}

正如您在评论中看到的那样,这里将发生的事情是:

  • 调用网络
  • 打印(3)
  • 完成 -> 重新加载 Table 查看
  • 网络响应数据 -> 你什么都不做。

你应该做什么...

func searchRepositories(withQuery query: String, completion: @escaping (SomeResponseType?, Error?) -> Void) {
    repositories = repositoryService.searchRepositories(withQuery: query) { response in 
         completion(results, error)
    }
}

您已在别处遵循此模式,但您需要涵盖所有异步请求。

这里也一样:

func searchRepositories(withQuery query: String) -> [Repository] {
    networkService?.searchRepositories(withQuery: query) { [weak self] repositories in
        guard let self = self, let repositories = repositories else { return }
        self.repositories += repositories
    }
    print(2)
    return self.repositories
}

return self.repositories 将在网络调用开始后立即调用,网络请求将没有时间 return 数据。

有关该主题的一些有用资源: