skip to Main Content

I have an API where I PUT stuff to. 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. So it’s just an http 200 with no content.

I only can find async functions that e.g. serialize a String or Data or a Decodable, but they don’t work if my response is empty.

Is there a way to await something like that in Alamofire?

3

Answers


  1. If Alamofire does not provide a method for your purpose, then you will have wrap the old Alamofire methods that uses closures as below:

        func myRequest() async throws {
            try await withUnsafeThrowingContinuation { continuation in
                myAlamofireRequest {
                    continuation.resume()
                }
            }
        }
    
    Login or Signup to reply.
  2. 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. 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. 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:

    // 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)
                    }
                }
            }
        }
    
    Login or Signup to reply.
  3. Alamofire already supports this, you just need to choose a form. 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.

    All Alamofire responses require some sort of payload type, but Alamofire provides an Empty type to fill this role for 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search