如何等到来自网络调用的数据到来,然后才 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
,第二个 await
s 是第一个:
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]
参数的闭包,并且只在数据被正确检索和转换时才调用闭包。
我有一项服务 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
,第二个 await
s 是第一个:
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]
参数的闭包,并且只在数据被正确检索和转换时才调用闭包。