Преобразовать обработчик завершения Alamofire в 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)
        }
    })
}

Как я могу преобразовать его в новую функциональность async / await в 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
}

Но я не получаю значение в конце оператора печати.

Я как бы потерялся в процессе, я хотел бы преобразовать свои старые функции, с этим примером это очень поможет.

3 ответа

Во-первых, ваша структура неверна. Не начинайте с исходного кода и не оборачивайте его целиком в блок продолжения. Просто создайте свою версию, заключенную в блок продолжения. Например, декодирование JSON не должно быть частью того, что упаковывается; это то, что приходит после того, как к вам возвращается результат работы в сети - это причина, по которой вы хотите AF.request в async функция для начала.

Во-вторых, как сообщает вам сообщение об ошибке, разрешите универсальный тип, либо путем возврата к явному возвращаемому типу, либо указав тип как часть объявления продолжения.

Лично я считаю, что проглатывание ошибок внутри сетевого вызова - плохая идея, пользовательский интерфейс должен получать все ошибки и делать выбор соответственно.

Вот пример короткой оболочки для 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
}
Другие вопросы по тегам