将 Alamofire 完成处理程序转换为异步/等待 | 斯威夫特 5.5,*

Art*_*uro 7 swift alamofire alamofire-request swiftui alamofire5

我有当前有效的功能。我将它与完成处理程序一起使用:

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)
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

如何将其转换为 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
}
Run Code Online (Sandbox Code Playgroud)

但我没有得到打印语句末尾的值。

我在这个过程中有点迷失,我想转换我的旧函数,这个例子会很有帮助。

编辑:

下面的答案有效,但我在 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
}
Run Code Online (Sandbox Code Playgroud)

mat*_*att 12

首先,你的结构是错误的。不要从原始代码开始并将其全部包装在延续块中。AF.request只需制作一个包含在延续块中的自身版本即可。例如,JSON 解码不应该是被包装内容的一部分;这是网络结果返回给你之后的结果 \xe2\x80\x94 这就是你想要变成AF.request一个async函数的原因。

\n

其次,正如错误消息告诉您的那样,通过返回显式返回类型或将类型声明为延续声明的一部分来解析泛型。

\n

因此,举例来说,我要做的只是最低限度地包装AF.request在一个async throws函数中,如果我们获得数据,我们将返回它,如果我们收到错误,我们将抛出它:

\n
func afRequest(url:URL) async throws -> Data {\n    try await withUnsafeThrowingContinuation { continuation in\n        AF.request(url, method: .get).validate().responseData { response in\n            if let data = response.data {\n                continuation.resume(returning: data)\n                return\n            }\n            if let err = response.error {\n                continuation.resume(throwing: err)\n                return\n            }\n            fatalError("should not get here")\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您会注意到我不需要解析泛型continuation类型,因为我已经声明了函数的返回类型。(这就是为什么我在有关该主题的在线教程中向您指出我的解释和示例;您阅读了吗?)

\n

好的,重点是,现在在 async/await 世​​界中调用该函数很简单。一个可能的基本结构是:

\n
func getTokenBalances3() async {\n    let url = // ...\n    do {\n        let data = try await self.afRequest(url:url)\n        print(data)\n        // we\'ve got data! okay, so\n        // do something with the data, like decode it\n        // if you declare this method as returning the decoded value,\n        // you could return it\n    } catch {\n        print(error)\n        // we\'ve got an error! okay, so\n        // do something with the error, like print it\n        // if you declare this method as throwing,\n        // you could rethrow it\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

async最后我应该补充一点,无论如何,所有这些努力可能都被浪费了,因为我希望 Alamofire 人员现在随时都能使用他们自己的所有异步方法的版本。

\n


小智 5

我个人认为在网络调用中吞没错误是一个坏主意,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)
                }
            }
        })
    }
}
Run Code Online (Sandbox Code Playgroud)