【问题标题】:How to wait until data from network call comes and only then return value of a function #Swift如何等到来自网络调用的数据到来,然后才返回函数的值#Swift
【发布时间】:2022-01-19 22:50:05
【问题描述】:

我有一个服务类,它进行 api 调用并将数据存储到它的属性中。然后我的交互器类有一个方法,我想在其中进行服务类 api 调用以及何时存储数据 - 返回它。我尝试自己使用完成处理程序和调度组来处理这个问题,但是(我想我只是遗漏了一些东西)这不起作用。如果您能帮助我解决这个问题,我将不胜感激。提前致谢!

服务类:

class PunkApiService{

var beers = [Beer]()

func loadList(at page: Int){
    //MARK: - Checks is URL is valid + pagination
    guard let url = URL(string: "https://api.punkapi.com/v2/beers?page=\(page)&per_page=25") else {
        print("Invalid URL")
        return
    }
    //MARK: - Creating URLSession DataTask
    let task = URLSession.shared.dataTask(with: url){ data, response, error in
        //MARK: - Handling no erros came
        guard error == nil else {
            print(error!)
            return
        }
        //MARK: - Handling data came
        guard let data = data else{
            print("Failed to load data")
            return
        }
        do{
            let beers = try JSONDecoder().decode([Beer].self, from: data)
            self.beers.append(contentsOf: beers)
        }
        catch{
            print("Failed to decode data")
        }
    }
    task.resume()
}

和Interactor类(没有完成处理程序或调度组):

class BeersListInteractor:BeersListInteractorProtocol{
private var favoriteBeers = FavoriteBeers()
private var service  = PunkApiService()
//MARK: - Load list of Beers
func loadList(at page: Int) -> [Beer]{
    service.loadList(at: page)
    return service.beers
}

添加:我对完成处理程序的尝试

    var beers: [Beer]                                          
func loadList(at page: Int, completion: ()->()){
    service.loadList(at: page)
    completion()
    
}

func completion(){
    beers.append(contentsOf: service.beers)
}

loadList(at: 1) {
    completion()
}

【问题讨论】:

  • 您可以尝试使用完成处理程序吗?
  • 你“可以”使用某种类型的锁,但我认为在你的情况下这不是一个好主意,相反你应该使用闭包回调
  • @jnpdx 添加在描述中

标签: swift grand-central-dispatch completionhandler swift-concurrency


【解决方案1】:

我认为您在尝试使用完成块时走在正确的道路上,只是没有正确使用。

func loadList(at page: Int, completion: @escaping ((Error?, Bool, [Beer]?) -> Void)) {
    //MARK: - Checks is URL is valid + pagination
    guard let url = URL(string: "https://api.punkapi.com/v2/beers?page=\(page)&per_page=25") else {
        print("Invalid URL")
        completion(nil, false, nil)
        return
    }
    //MARK: - Creating URLSession DataTask
    let task = URLSession.shared.dataTask(with: url){ data, response, error in
        //MARK: - Handling no erros came
        if let error = error {
            completion(error, false, nil)
            print(error!)
            return
        }
        //MARK: - Handling data came
        guard let data = data, let beers = try? JSONDecoder().decode([Beer].self, from: data) else { 
            completion(nil, false, nil)
            return
        }
        completion(nil, true, beers)
    }
    task.resume()
}

这是loadList函数,它现在有一个completion参数,它将有三个参数,分别是可选的Error,代表获取数据成功或失败的Bool值,以及实际的@ 987654326@ 数组,包含数据(如果检索到的话)。

现在调用函数的方式如下:

service.loadList(at: page) { error, success, beers in
    if let error = error {
        // Handle the error here
        return
    }

    if success, let beers = beers {
       // Data was correctly retrieved - and safely unwrapped for good measure, do what you need with it
       // Example:
       loader.stopLoading()
       self.datasource = beers
       self.tableView.reloadData()
    }
}

请记住,完成是异步执行的,不会停止应用其余部分的执行。 此外,您应该决定是直接在 loadList 函数内还是在闭包内处理错误,如果您在函数内处理它,则可能删除 Error 参数。 其他参数也是如此:您可以决定只使用一个只有 [Beer] 参数的闭包,并且只有在正确检索和转换数据时才调用该闭包。

【讨论】:

  • 现在我知道我做错了什么,谢谢你的帮助!
  • @IgorŠevtšenko 没问题。如果您认为我的回答解决了您的问题,记得标记为解决方案!
【解决方案2】:

这就是async/await 模式的用途,描述为here。在您的情况下,两个 loadList 函数都是 async,第二个函数是 awaits 第一个函数:

class PunkApiService {
    func loadList(at page: Int) async { 
        // change function to await for task result 
        let (data, error) = try await URLSession.shared.data(from: url)
        let beers = try JSONDecoder().decode([Beer].self, from: data)
        ...
        return beers
    }
}

class BeersListInteractor: BeersListInteractorProtocol {
    func loadList(at page: Int) async -> [Beer]{
         let beers = await service.loadList(at: page)
         return service.beers
    }
}

看好解释here

【讨论】:

  • 比你多,我会试试的!
猜你喜欢
  • 2020-06-22
  • 1970-01-01
  • 1970-01-01
  • 2019-05-11
  • 2017-09-12
  • 2012-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多