【问题标题】:How can I get a list of repositories in the view model before the table view is refreshed?如何在刷新表视图之前获取视图模型中的存储库列表?
【发布时间】:2019-01-24 10:04:06
【问题描述】:

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

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

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

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

RepositoryService:

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

【问题讨论】:

    标签: swift multithreading mvvm grand-central-dispatch moya


    【解决方案1】:

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

    考虑代码:

    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. 
    }
    

    正如你在 cmets 中看到的,这里会发生什么:

    • 调用网络
    • 打印(3)
    • 完成 -> 重新加载表格视图
    • 带有数据的网络响应 -> 你什么都不做。

    你应该做什么......

    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将在网络调用开始后立即被调用,网络请求将没有时间返回数据。

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

    【讨论】:

      猜你喜欢
      • 2020-03-29
      • 1970-01-01
      • 2011-10-23
      • 1970-01-01
      • 1970-01-01
      • 2014-10-09
      • 2015-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多