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
Swift 3: Transfer Utility Enumeratetoassignblocks Method Signature
How to Display Collectionviewcell in Center in Swift 4
How to Use Pod in Notification Service Extension
How to Make Inputaccessoryview Appear Above Uitabbarviewcontroller's Tabs
How to Add a Lock Screen Widget (Requiring iOS 16) and Still Support iOS 15
Swiftui Xcode 12.3 Can't Change Button Size in Toolbar
Data Repeat in UItableview When Scrolling
Center Swiftui View in Top-Level View
How to Draw a Uilabel with a Different Blend Mode in Draw(_ Rect: Cgrect) in Swift
How to Create a Scnnode from a .Usdz
How to Launch IPA File in iOS 10 Real Device Using Appium 1.6.0
How to Check a App Was Installed or Updated
Nstextattachment Image Not Shown in Nstextview (But in Uitextview)
How to Call Objective-C Instancetype Method in Swift
Nsmanagedobject Setter Giving Me [ Myobject Setname:]: Unrecognized Selector Sent to Instance
Does "Let _ = ..." (Let Underscore Equal) Have Any Use in Swift