如何等到来自网络调用的数据到来,然后才 return 函数的值 #Swift

How to wait until data from network call comes and only then return value of a function #Swift

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

服务class:

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

和交互器class(没有完成处理程序或调度组):

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

这就是 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

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

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值或获取数据失败,以及包含数据(如果有)的实际 [Beers] 数组。

下面是您现在调用函数的方式:

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] 参数的闭包,并且只在数据被正确检索和转换时才调用闭包。