Trying out the request on Postman, the "data" of the response is an empty dictionary.
However, when I try that in swift using Alamofire, "data" gets misinterpreted as an empty array. What could I be doing wrong?
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")
}
}
}
}