Home > Mobile >  Alamofire parse response incorrectly
Alamofire parse response incorrectly

Time:02-01

Trying out the request on Postman, the "data" of the response is an empty dictionary.

postman response

However, when I try that in swift using Alamofire, "data" gets misinterpreted as an empty array. What could I be doing wrong? alamofire response

Raw response using debugPrint(response) prints the following:

[Response]:
    [Status Code]: 200
    [Headers]:
        Access-Control-Allow-Origin: *
        Alt-Svc: h3=":443"; ma=2592000, h3-29=":443"; ma=2592000, h3-Q050=":443"; ma=2592000, h3-Q046=":443"; ma=2592000, h3-Q043=":443"; ma=2592000, quic=":443"; ma=2592000; v="43,46"
        Cache-Control: no-cache, private
        Content-Encoding: br
        Content-Length: 71
        Content-Type: application/json
        Date: Tue, 31 Jan 2023 16:32:18 GMT
        Vary: Accept-Encoding
        x-powered-by: PHP/8.0.24
        x-ratelimit-limit: 60
        x-ratelimit-remaining: 59
    [Body]:
        {"status":false,"message":"Kullan\u0131c\u0131 bilgileri hatal\u0131.","data":[]}

Tried changing the encoding and headers of the request, none was helpful.

CodePudding user response:

This isn't an Alamofire issue, it's an error from JSONDecoder. You can check your actual response by printing the raw response in Alamofire (which you don't post any code for).

.response<...> { response in 
  debugPrint(response)
}

That will print a String version of the response body (unless it's very large) which you can use to see what you're actually receiving.

You can also check your actual response using a proxy like Proxyman or a traffic inspector like Wireshark and see what you're actually getting. It seems likely you're actually receiving an array.

CodePudding user response:

I've come across this odd way of representing null from a server (openweathermap), where instead of null, it uses {}. To work with this, I used a custom decoder, something like:

// for the case where we have:  "data": { } instead of, "data": null

public init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    if let theData = try? values.decode([String].self, forKey: .data) {
        self.data = theData
    } else {
        self.data = nil
    }
    // ....other keys
}

with declaration, let data: [String]? or whatever the array holds.

Note this is a JSONDecoder behaviour, not Alamofire, I was using URLSession.shared... with this. When the JSONDecoder tries to decode this {}, it crashes, because it is not an a null and it not an array either. Postman is probably more forgiving than Swift JSONDecoder. Alamofire tries to decode data as an array, because you probably declared it as an array in your struct/class to decode.

EDIT-1:

here is the SwiftUI code I used to test my answer (same approach for UIKit and Alamofire):

struct RequestResponse: Decodable {
    var status: Bool
    var message: String?
    var data: [String]?
    
    enum CodingKeys: String, CodingKey {
        case status, message, data
    }
    
    // for the case where we have: "data": { } instead of, "data": null
    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.status = try values.decode(Bool.self, forKey: .status)
        self.message = try values.decode(String?.self, forKey: .message)
        if let theData = try? values.decode([String].self, forKey: .data) {
            self.data = theData
        } else {
            self.data = nil
        }
    }
}

struct ContentView: View {
    @State var response: RequestResponse?
    
    var body: some View {
        VStack {
            if let results = response {
                Text(results.status ? "true" : "false")
                Text(results.message ?? "no message")
                ForEach(results.data ?? [], id: \.self) { item in
                    Text("\(item)")
                }
            }
        }
        .onAppear {
            let json = """
{
   "status": false,
   "message": "some message here",
   "data": {}
}
"""
            // simulated API data from the server
            let data = json.data(using: .utf8)!
            do {
                let decoded = try JSONDecoder().decode(RequestResponse.self, from: data)
                print("\n---> decoded: \(decoded) \n")
                response = decoded
            } catch {
                print("\n---> error: \n \(error)\n")
            }
        }
    }
}
  • Related