简体   繁体   English

如何使用 Alamofire 异步等待空响应

[英]How to async await empty response using Alamofire

I have an API where I PUT stuff to.我有一个 API,我可以在其中PUT东西。 I need to make sure to wait until I get an http 200 response from the server, but I don't know how to await that using Alamofire because my response itself if empty.我需要确保等到我从服务器收到 http 200 响应,但我不知道如何使用 Alamofire 等待响应,因为我的响应本身是空的。 So it's just an http 200 with no content.所以它只是一个没有内容的http 200。

I only can find async functions that eg serialize a String or Data or a Decodable , but they don't work if my response is empty.我只能找到例如序列化StringDataDecodable的异步函数,但如果我的响应为空,它们将不起作用。

Is there a way to await something like that in Alamofire?有没有办法在 Alamofire 中等待类似的东西?

Alamofire already supports this, you just need to choose a form. Alamofire 已经支持这个,你只需要选择一个表单。 Your biggest issue will be accepting a 200 with no data, as that's technically invalid since only 204 or 205 are supposed to be empty.您最大的问题是接受没有数据的 200,因为这在技术上是无效的,因为只有 204 或 205 应该是空的。

All Alamofire responses require some sort of payload type, but Alamofire provides an Empty type to fill this role for Decodable .所有 Alamofire 响应都需要某种有效载荷类型,但 Alamofire 提供了一个Empty类型来填补Decodable的这个角色。 So the simplest way is to use the所以最简单的方法是使用

await AF.request(...)
        .serializingDecodable(Empty.self, emptyResponseCodes: [200])
        .response

Note, if you already have an Empty type or are importing Combine in the same file as this code, you may need to disambiguate by using Alamofire.Empty .请注意,如果您已经有一个Empty类型或正在与此代码在同一文件中导入 Combine,您可能需要使用Alamofire.Empty来消除歧义。

I know that your question is about async/await from Alamofire, but is good to know that the http status codes 204 and 205 are exactly for this.我知道您的问题是关于 Alamofire 的 async/await 的,但很高兴知道 http 状态代码 204 和 205 正是为此。 Which means that if you have access to the server code you could send the empty responses with the http status code 204 and 205 instead of 200 and then Alamofire would not generate any errors.这意味着如果您可以访问服务器代码,您可以发送带有 http 状态代码 204 和 205 而不是 200 的空响应,然后 Alamofire 不会产生任何错误。 But assuming you don't have access to the server code and you need to parse an empty response as correct then you could use the following code:但是假设您无权访问服务器代码并且您需要正确解析空响应,那么您可以使用以下代码:

func testRequestWithAlamofire() {
        let dataResponseSerializer = DataResponseSerializer(emptyResponseCodes: [200, 204, 205]) // Default is [204, 205] so add 200 too :P
        
        AF.request("http://www.mocky.io/v2/5aa696133100001335e716e0", method: .put).response(responseSerializer: dataResponseSerializer) { response in
            switch response.result {
                case .failure(let error):
                    print(error)
                case .success(let value):
                    print(value)
            }
        }
    }

And for a real and complete example of how async/await from Alamofire or any other async context look this code:对于 Alamofire 或任何其他异步上下文中的 async/await 如何查看此代码的真实且完整的示例:

// This function get report from API and save to a local JSON to be readed by the app
    func updateReport() {
        Task {
            
            guard let session = self.sessionRepository.getSession(WithUser: Defaults.lastLoggedUsername!) else { return }
            guard let company = session.profile?.companies.first else { return }
            self.apiManager.configure(WithToken: session.accessToken)
            
            do {
                let dateA = Date().dateAtStartOf(.year)
                //let dateB = Date().dateAtEndOf(.month)
                let dateB = Date() // Just now
                
                let report = try await self.apiManager.report(CompanyId: company._id, DateA: dateA, DateB: dateB, ChartPeriodicity: .month)
                
                self.currentReport = report
                
                // Save data to disk to be read later
                self.reportManager.saveReportToDisk(report: report!, withProfileId: session.profile!._id)
            } catch {
                print("Error getting report: \(error)")
            }
        }
    }

// Get personal report from a given date range
    func report(CompanyId companyId: String, DateA dateA: Date, DateB dateB: Date, ChartPeriodicity chartPeriodicity: ChartPeriodicity) async throws -> CDReport? {
        try await withCheckedThrowingContinuation { continuation in
            self.contappApi.request(.report(companyId: companyId, dateA: dateA, dateB: dateB, chartPeriodicity: chartPeriodicity)) { result in
                switch result {
                case let .success(response):
                    // Check status code
                    guard response.statusCode == 200 else {
                        continuation.resume(throwing: ContappNetworkError.unexpected(code: response.statusCode))
                        return
                    }
                    
                    // Decode data
                    do {
                        //let report = try JSONDecoder().decode(CDReport.self, from: response.data)
                        let report = try CDReport(data: response.data)
                        continuation.resume(returning: report)
                    } catch {
                        continuation.resume(throwing: ContappNetworkError.cantDecodeDataFromNetwork)
                    }
                    
                case .failure(_):
                    continuation.resume(throwing: ContappNetworkError.networkError)
                }
            }
        }
    }

If Alamofire does not provide a method for your purpose, then you will have wrap the old Alamofire methods that uses closures as below:如果 Alamofire 没有为您的目的提供方法,那么您将包装使用闭包的旧 Alamofire 方法,如下所示:

    func myRequest() async throws {
        try await withUnsafeThrowingContinuation { continuation in
            myAlamofireRequest {
                continuation.resume()
            }
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM