将 Alamofire 完成处理程序转换为 Async/Await | Swift 5.5, *
Convert Alamofire Completion handler to Async/Await | Swift 5.5, *
我有当前可用的功能。我将它与完成处理程序一起使用:
func getTokenBalances(completion: @escaping (Bool) -> Void) {
guard let url = URL(string: "someApiUrlFromLostandFound") else {
print("Invalid URL")
completion(false)
return
}
AF.request(url, method: .get).validate().responseData(completionHandler: { data in
do {
guard let data = data.data else {
print("Response Error:", data.error as Any)
completion(false)
return
}
let apiJsonData = try JSONDecoder().decode(TokenBalanceClassAModel.self, from: data)
DispatchQueue.main.async {
self.getTokenBalancesModel = apiJsonData.data.items
completion(true)
}
} catch {
print("ERROR:", error)
completion(false)
}
})
}
如何将其转换为 swift 5.5 的新 async/await 功能?
这是我试过的:
func getTokenBalances3() async {
let url = URL(string: "someApiUrlFromLostandFound")
let apiRequest = await withCheckedContinuation { continuation in
AF.request(url!, method: .get).validate().responseData { apiRequest in
continuation.resume(returning: apiRequest)
}
}
let task1 = Task {
do {
// Decoder is not asynchronous
let apiJsonData = try JSONDecoder().decode(SupportedChainsClassAModel.self, from: apiRequest.data!)
// Working data -> print(String(apiJsonData.data.items[0].chain_id!))
} catch {
print("ERROR:", error)
}
}
let result1 = await task1.value
print(result1) // values are not printed
}
但是我没有得到打印语句末尾的值。
我有点迷失在这个过程中,我想转换我的旧功能,这个例子会有很大帮助。
编辑:
下面的答案有效,但我在 Alamofire 团队实现异步时找到了自己的解决方案:
func getSupportedChains() async throws -> [AllChainsItemsClassAModel] {
var allChains: [AllChainsItemsClassAModel] = [AllChainsItemsClassAModel]()
let url = URL(string: covalentHqUrlConnectionsClassA.getCovalenHqAllChainsUrl())
let apiRequest = await withCheckedContinuation { continuation in
AF.request(url!, method: .get).validate().responseData { apiRequest in
continuation.resume(returning: apiRequest)
}
}
do {
let data = try JSONDecoder().decode(AllChainsClassAModel.self, from: apiRequest.data!)
allChains = data.data.items
} catch {
print("error")
}
return allChains
}
首先,你的结构是错误的。不要从您的原始代码开始并将其 all 包装在继续块中。只需制作一个包含在延续块中的 AF.request
本身的版本。例如,JSON 解码不应该是被包装内容的一部分;它是在网络 returns 给你的结果之后出现的——这就是你想要将 AF.request
变成一个 async
函数的原因。
其次,正如错误消息告诉您的那样,解析 泛型,方法是将 return 转换为显式 return 类型,或者声明作为延续声明一部分的类型。
因此,例如,我要做的只是将 AF.request
最小化包装在一个 async throws
函数中,如果我们得到数据,我们 return 它,如果我们得到一个我们抛出错误:
func afRequest(url:URL) async throws -> Data {
try await withUnsafeThrowingContinuation { continuation in
AF.request(url, method: .get).validate().responseData { response in
if let data = response.data {
continuation.resume(returning: data)
return
}
if let err = response.error {
continuation.resume(throwing: err)
return
}
fatalError("should not get here")
}
}
}
您会注意到我不需要解析泛型 continuation
类型,因为我已经声明了函数的 return 类型。 (这就是为什么我向您指出我在 online tutorial 中关于这个主题的解释和示例;您读过了吗?)
好的,重点是,现在在async/await世界中调用该函数是微不足道的。一个可能的基本结构是:
func getTokenBalances3() async {
let url = // ...
do {
let data = try await self.afRequest(url:url)
print(data)
// we've got data! okay, so
// do something with the data, like decode it
// if you declare this method as returning the decoded value,
// you could return it
} catch {
print(error)
// we've got an error! okay, so
// do something with the error, like print it
// if you declare this method as throwing,
// you could rethrow it
}
}
最后我要补充一点,所有这些努力可能都白费了,因为我希望 Alamofire 的人现在随时都能使用他们自己的 async
版本的所有异步方法。
这是我的答案和马特提供的答案的混合体。一旦 Alamofire 团队实现了异步,可能会有一个更简单、更清晰的实现,但至少现在我已经摆脱了回调地狱...
func afRequest(url: URL) async throws -> Data {
try await withUnsafeThrowingContinuation { continuation in
AF.request(url, method: .get).validate().responseData { response in
if let data = response.data {
continuation.resume(returning: data)
return
}
if let err = response.error {
continuation.resume(throwing: err)
return
}
fatalError("Error while doing Alamofire url request")
}
}
}
func getSupportedChains() async -> [AllChainsItemsClassAModel] {
var allChains: [AllChainsItemsClassAModel] = [AllChainsItemsClassAModel]()
let url = URL(string: covalentHqUrlConnectionsClassA.getCovalenHqAllChainsUrl())
do {
let undecodedData = try await self.afRequest(url: url!)
let decodedData = try JSONDecoder().decode(AllChainsClassAModel.self, from: undecodedData)
allChains = decodedData.data.items
} catch {
print(error)
}
return allChains
}
我个人认为在网络调用中吞下错误是个坏主意,UI 应该接收所有错误并做出相应的选择。
这里是 responseDecodable 的短包装示例,它产生异步响应。
public extension DataRequest {
@discardableResult
func asyncDecodable<T: Decodable>(of type: T.Type = T.self,
queue: DispatchQueue = .main,
dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
decoder: DataDecoder = JSONDecoder(),
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) async throws -> T {
return try await withCheckedThrowingContinuation({ continuation in
self.responseDecodable(of: type, queue: queue, dataPreprocessor: dataPreprocessor, decoder: decoder, emptyResponseCodes: emptyResponseCodes, emptyRequestMethods: emptyRequestMethods) { response in
switch response.result {
case .success(let decodedResponse):
continuation.resume(returning: decodedResponse)
case .failure(let error):
continuation.resume(throwing: error)
}
}
})
}
}
我有当前可用的功能。我将它与完成处理程序一起使用:
func getTokenBalances(completion: @escaping (Bool) -> Void) {
guard let url = URL(string: "someApiUrlFromLostandFound") else {
print("Invalid URL")
completion(false)
return
}
AF.request(url, method: .get).validate().responseData(completionHandler: { data in
do {
guard let data = data.data else {
print("Response Error:", data.error as Any)
completion(false)
return
}
let apiJsonData = try JSONDecoder().decode(TokenBalanceClassAModel.self, from: data)
DispatchQueue.main.async {
self.getTokenBalancesModel = apiJsonData.data.items
completion(true)
}
} catch {
print("ERROR:", error)
completion(false)
}
})
}
如何将其转换为 swift 5.5 的新 async/await 功能?
这是我试过的:
func getTokenBalances3() async {
let url = URL(string: "someApiUrlFromLostandFound")
let apiRequest = await withCheckedContinuation { continuation in
AF.request(url!, method: .get).validate().responseData { apiRequest in
continuation.resume(returning: apiRequest)
}
}
let task1 = Task {
do {
// Decoder is not asynchronous
let apiJsonData = try JSONDecoder().decode(SupportedChainsClassAModel.self, from: apiRequest.data!)
// Working data -> print(String(apiJsonData.data.items[0].chain_id!))
} catch {
print("ERROR:", error)
}
}
let result1 = await task1.value
print(result1) // values are not printed
}
但是我没有得到打印语句末尾的值。
我有点迷失在这个过程中,我想转换我的旧功能,这个例子会有很大帮助。
编辑:
下面的答案有效,但我在 Alamofire 团队实现异步时找到了自己的解决方案:
func getSupportedChains() async throws -> [AllChainsItemsClassAModel] {
var allChains: [AllChainsItemsClassAModel] = [AllChainsItemsClassAModel]()
let url = URL(string: covalentHqUrlConnectionsClassA.getCovalenHqAllChainsUrl())
let apiRequest = await withCheckedContinuation { continuation in
AF.request(url!, method: .get).validate().responseData { apiRequest in
continuation.resume(returning: apiRequest)
}
}
do {
let data = try JSONDecoder().decode(AllChainsClassAModel.self, from: apiRequest.data!)
allChains = data.data.items
} catch {
print("error")
}
return allChains
}
首先,你的结构是错误的。不要从您的原始代码开始并将其 all 包装在继续块中。只需制作一个包含在延续块中的 AF.request
本身的版本。例如,JSON 解码不应该是被包装内容的一部分;它是在网络 returns 给你的结果之后出现的——这就是你想要将 AF.request
变成一个 async
函数的原因。
其次,正如错误消息告诉您的那样,解析 泛型,方法是将 return 转换为显式 return 类型,或者声明作为延续声明一部分的类型。
因此,例如,我要做的只是将 AF.request
最小化包装在一个 async throws
函数中,如果我们得到数据,我们 return 它,如果我们得到一个我们抛出错误:
func afRequest(url:URL) async throws -> Data {
try await withUnsafeThrowingContinuation { continuation in
AF.request(url, method: .get).validate().responseData { response in
if let data = response.data {
continuation.resume(returning: data)
return
}
if let err = response.error {
continuation.resume(throwing: err)
return
}
fatalError("should not get here")
}
}
}
您会注意到我不需要解析泛型 continuation
类型,因为我已经声明了函数的 return 类型。 (这就是为什么我向您指出我在 online tutorial 中关于这个主题的解释和示例;您读过了吗?)
好的,重点是,现在在async/await世界中调用该函数是微不足道的。一个可能的基本结构是:
func getTokenBalances3() async {
let url = // ...
do {
let data = try await self.afRequest(url:url)
print(data)
// we've got data! okay, so
// do something with the data, like decode it
// if you declare this method as returning the decoded value,
// you could return it
} catch {
print(error)
// we've got an error! okay, so
// do something with the error, like print it
// if you declare this method as throwing,
// you could rethrow it
}
}
最后我要补充一点,所有这些努力可能都白费了,因为我希望 Alamofire 的人现在随时都能使用他们自己的 async
版本的所有异步方法。
这是我的答案和马特提供的答案的混合体。一旦 Alamofire 团队实现了异步,可能会有一个更简单、更清晰的实现,但至少现在我已经摆脱了回调地狱...
func afRequest(url: URL) async throws -> Data {
try await withUnsafeThrowingContinuation { continuation in
AF.request(url, method: .get).validate().responseData { response in
if let data = response.data {
continuation.resume(returning: data)
return
}
if let err = response.error {
continuation.resume(throwing: err)
return
}
fatalError("Error while doing Alamofire url request")
}
}
}
func getSupportedChains() async -> [AllChainsItemsClassAModel] {
var allChains: [AllChainsItemsClassAModel] = [AllChainsItemsClassAModel]()
let url = URL(string: covalentHqUrlConnectionsClassA.getCovalenHqAllChainsUrl())
do {
let undecodedData = try await self.afRequest(url: url!)
let decodedData = try JSONDecoder().decode(AllChainsClassAModel.self, from: undecodedData)
allChains = decodedData.data.items
} catch {
print(error)
}
return allChains
}
我个人认为在网络调用中吞下错误是个坏主意,UI 应该接收所有错误并做出相应的选择。
这里是 responseDecodable 的短包装示例,它产生异步响应。
public extension DataRequest {
@discardableResult
func asyncDecodable<T: Decodable>(of type: T.Type = T.self,
queue: DispatchQueue = .main,
dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
decoder: DataDecoder = JSONDecoder(),
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) async throws -> T {
return try await withCheckedThrowingContinuation({ continuation in
self.responseDecodable(of: type, queue: queue, dataPreprocessor: dataPreprocessor, decoder: decoder, emptyResponseCodes: emptyResponseCodes, emptyRequestMethods: emptyRequestMethods) { response in
switch response.result {
case .success(let decodedResponse):
continuation.resume(returning: decodedResponse)
case .failure(let error):
continuation.resume(throwing: error)
}
}
})
}
}