簡體   English   中英

將 Alamofire 完成處理程序轉換為異步/等待 | Swift 5.5, *

[英]Convert Alamofire Completion handler to Async/Await | Swift 5.5, *

我有當前可用的 function。 我將它與完成處理程序一起使用:

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 的新異步/等待功能?

這是我試過的:

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
}

首先,你的結構是錯誤的。 不要從您的原始代碼開始並將其全部包裝在延續塊中。 只需制作一個包含在延續塊中的AF.request版本。 例如,JSON 解碼不應該是被包裝內容的一部分; 它是 .networking 的結果返回給你之后發生的事情——這就是你想要將AF.request轉換為async function 的原因。

其次,正如錯誤消息告訴您的那樣,通過返回顯式返回類型或通過將類型聲明為延續聲明的一部分來解析泛型。

因此,例如,我要做的只是將AF.request最小化包裝在async throws function 中,如果我們獲得數據,我們將返回它,如果我們收到錯誤,我們將其拋出:

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類型,因為我已經聲明了函數的返回類型。 (這就是為什么我在我關於這個主題的在線教程中向你指出了我的解釋和示例;你讀了嗎?)

好的,重點是,現在在異步/等待世界中調用 function 是微不足道的。 一個可能的基本結構是:

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版本。

我個人認為在 a.network 調用中吞下錯誤是個壞主意,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)
                }
            }
        })
    }
}

這是我的答案和馬特提供的答案的混合體。 一旦 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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM