Update Responsejson to Responsedecodable in Swift

Update responseJSON to responseDecodable in Swift

Alamofire recommends to use responseDecodable() because people were often using responseJSON(), then get the response.data, and call a JSONDecoder() on it. So this was making inner call of JSONSerialization for "nothing". Also, since Codable is "new", and there were still old questions available, people could be missing the Codable feature. See this topic on Alamofire Repo.

So if you use Codable, I encourage it when possible, use responseDecodable() instead.

But, you still can do it manually, by retrieving Data with no conversion:

For that, use:

@discardableResult func responseData(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDataResponse<Data>) -> Void) -> Self

In use:

request.responseData { response in
switch response.result {
case .success(let data):
do {
let asJSON = try JSONSerialization.jsonObject(with: data)
// Handle as previously success
} catch {
// Here, I like to keep a track of error if it occurs, and also print the response data if possible into String with UTF8 encoding
// I can't imagine the number of questions on SO where the error is because the API response simply not being a JSON and we end up asking for that "print", so be sure of it
print("Error while decoding response: "\(error)" from: \(String(data: data, encoding: .utf8))")
}
case .failure(let error):
// Handle as previously error
}
}

How to use responseDecodable instead of responseJSON using Alamofire 6

Unlike .responseJSON which returns a dictionary or array .responseDecodable deserializes the JSON into structs or classes.

You have to create an appropriate model which conforms to Decodable, in your code it's represented by ResponseType(.self).

The associated value of the success case is the root struct of the model.

But deprecated (in a yellow warning) means the API is still operational.

Side note: A JSON dictionary is never [String: Any?] the value is always non-optional.

responseJSON deprecated and will be removed in Alamofire 6. Use responseDecodable instead

Try this. I am using Alamofire 5.5.0

final class EdamamSession: Session {
func request<T: Decodable>(url: URL, callback: @escaping (DataResponse<T, AFError>) -> Void) {
AF.request(url).responseDecodable { (response: DataResponse<T, AFError>) in
callback(response)
}
}
}

Parameters input from responseJSON to responseDecodable using Alamofire

I think you are mixing things up here. You are implementing Decodable and talking/naming it that way, but what you are doing is Encoding.

It seems the Api expects a simple String as paramter, constructing it with a [String: String] seems ok. But what you are trying to send is JSON. I think the request is failing while trying to encode: ["keys": "latlng,time,altitude"] to your custom struct. Hence the error:

urlRequestValidationFailed....bodyDataInGETRequest

As has allready been pointed out in the comments you would need to use your old constructor for sending the request.

AF.request(url_activity, method: .get, parameters: params, headers: head)

then use the method:

.responseDecodable(of: "what you expect in the response".self) {

Here you need to provide a custom struct that conforms to Decodable and represents the >"returning json"<


For the request you linked it would be:

struct ResponseElement: Codable {
let type: String
let data: [Double]
let seriesType: String
let originalSize: Int
let resolution: String

enum CodingKeys: String, CodingKey {
case type, data
case seriesType = "series_type"
case originalSize = "original_size"
case resolution
}
}

and decoding with:

.responseDecodable(of: [ResponseElement].self) {

How to use responseDecodable method in Alamofire?

As you can read in the documentation, using responseDecodable is straightforward:

AF.request(...).responseDecodable(of: ResponseType.self) { response in 
...
}

Decoding an Error Response using Alamofire 5 and the responseDecodable function

Similiar login function from the article you mentioned, updated for current beta of Alamofire 5 and dealing with 404, 401 etc. (via the guard statement)

  static func login(email: String, password: String, completion:@escaping (Result<UserCredentials>)->Void) {
performRequest(router: Router.login(email: email, password: password), completion: completion)
AF.request(Router.login(email: email, password: password))
.validate(statusCode: 200..<300)
.responseDecodable { (response: DataResponse<UserCredentials>) in
guard response.result.isSuccess else {
print(" Error on login: \(String(describing: response.error))")
return
}
completion(response.result)
}
}

AF no member 'responseDecodable' with router

The error is saying that you want to use responseDecodable(of:) on a AFRouter instance.
But, in fact, you want to use it on a DataRequest instance.

But it "should work", so are you calling it on a mistaken instance? if we observe, there is a missing ):

AF.request(AFRouter.test(["Testing": testInfo]).responseDecodable(of:...

=>

AF.request(AFRouter.test(["Testing": testInfo])).responseDecodable(of:...

Swift/AlamoFire: Unable to decode JSON response

Thanks to everyone who commented! I was able to sort this out by adding a missing coding key. I created Regions.swift, which looks like this:

import Foundation

struct Regions: Decodable {

let all: [Region]

enum CodingKeys: String, CodingKey {
case all = "data"
}
}

The API response is encapsulated in data. Once I added this coding key, I was able to get the decoded response I was looking for:

   AF.request("https://api.linode.com/v4/regions")
.validate()
.responseDecodable(of: Regions.self) {(response) in
guard let regions = response.value else {return}
print(regions.all[0].self)
}

Response:

Region(capabilities: ["Linodes", "NodeBalancers", "Block Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans"], country: "in", id: "ap-west", status: "ok", resolvers: QuickCloud.Resolvers(ipv4: "172.105.34.5,172.105.35.5,172.105.36.5,172.105.37.5,172.105.38.5,172.105.39.5,172.105.40.5,172.105.41.5,172.105.42.5,172.105.43.5", ipv6: "2400:8904::f03c:91ff:fea5:659,2400:8904::f03c:91ff:fea5:9282,2400:8904::f03c:91ff:fea5:b9b3,2400:8904::f03c:91ff:fea5:925a,2400:8904::f03c:91ff:fea5:22cb,2400:8904::f03c:91ff:fea5:227a,2400:8904::f03c:91ff:fea5:924c,2400:8904::f03c:91ff:fea5:f7e2,2400:8904::f03c:91ff:fea5:2205,2400:8904::f03c:91ff:fea5:9207"))

Swift5 : How to handle this JSON response into Swift Data Model

Trying to address both the question and the comments above, the first thing would be to get some valid JSON. It could be that your API is providing non-JSON data, in which case neither this answer or using AlamoFire to decode it will work. However, formatting the above as JSON, at my best guess of what it's meant to be, will give:

let json = """
{
"id": 1,
"count": "",
"data": [
{
"key1": "value1",
"key2": "value2"
}
],
"message": "SUCCESS",
"response_code": 1
}
"""

At this point it's more obvious that the content under the data key is not an array of Test as defined above, but is an array of dictionaries (with a single enry in the array in the example). So it will be necessary to respecify the data model. For something this simple, for the use case mentioned, there is no real reason to go for multiple types, so we're going to redefine Getdata (I'm sticking with your naming despite not liking it):

struct Getdata: Decodable {
let all : [[String:String]]

enum CodingKeys: String, CodingKey {
case all = "data"
}
}

To test the decoding let's used the standard JSONDecoder (I don't have a Playground handy with AlamoFire as I'd never use it):


do {
let wrapper = try JSONDecoder().decode(Getdata.self, from: Data(json.utf8))
print(wrapper.all)
} catch {
print("Decoding error \(error.localizedDescription)")
}

This outputs

[["key1": "value1", "key2": "value2"]]

as would be expected.

Now there's a valid JSON decoding solution it should be easy to drop this into the AlamoFire APIs if you so wish. Although if the backend really is providing the misformed JSON as in question this won't work and you'll have to change the backend, decode it with your own decoder or mangle it into valid JSON.



Related Topics



Leave a reply



Submit