Alamofire 5.5 responseDecodable on JSON array - ios

I am trying to decode an api response that just responds with an array. All the examples I see for using responseDecodable are a dictionary with a data key or something. But when I just pass [Movie].self into as the decoder I get:
failure(Alamofire.AFError.responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "name", intValue: nil), Swift.DecodingError.Context(codingPath: responseDecodable
I'm assuming I'm not doing this right, looking for some help
GET /request
Response:
[{"id": 1, "name": "test"}]
struct Movie: Codable, Hashable {
let id: Int
let name: String
}
AF.request("http://localhost/request", parameters: parameters)
.responseDecodable(of: [Movie].self) { response in
debugPrint(response)
}

You will need to first assign a variable to accept the JSON data. From there, you can display the result. To illustrate:
AF.request("http://localhost/request", parameters: parameters)
.responseDecodable(of: [Movie].self) { response in
let myresult = try? JSON(data: response.data!)
then print it:
debugPrint(myresult)
If your JSON data is sound, then you should have no problem receiving the data. If your JSON is nested, then, you can drill-down into it:
let myresult = try? JSON(data: response.data!)
let resultArray = myresult!["result"]
and so on.
As for the "KeyNotFound" error ... It's because of what you said ... it's looking for a dictionary key/value pair. That should be resolved once you try the above example. Also, it's good practice to place your "method" in your AF request. :)

According to the error, your JSON didn't return a name value, so check that's it's actually supposed to be that key, or mark the value as optional if it's not always returned. let name: String?

Related

iOS swift - convert JSON string to array and parse each item

So far I have this:
do{
let (responseString, _) = try await URLSession.shared.data(for: request)
if let decodedResponse = try? JSONDecoder().decode([Quote].self, from: responseString){
quotes = decodedResponse
}
}catch{
print("data not valid")
}
which instantly converts the json string to object array (quotes) but this is not what I need.
I need to parse each item and then add it to the object array like this:
quotes.append(Quote(quote_id: id, quote: qqq, author: aut, series: srs))
how to do that?
The only similar question I've found is
String convert to Json Array and then parse value in swift iOS?
but he doesn't have a json array but json object so the answers there aren't helping

Parse an object returned as JSON from API in swift

I have recieved a response object (res) in swift from REST API. It is of type. __NSArrayM. It contains a JSON format string which I want to parse.
{ JsonResult = "[ { \"IsAuth\":\"true\" } ]"; }
It is a long JSON String and I have shortened it for simplicity.
To parse a json, the object needs to be of type Dictionary but I can't cast the object of type __NSArrayM into it.
I searched a lot but can't figure out anyway to read this JSON string.
Additional: Whichever object I try to cast the response object. I get the error -
Could not cast value of type '__NSArrayM' (0x107e86c30) to 'NSData' (0x107e86168) or whichever data type I cast into.
Let's do this step by step.
You say you have an object named "res" which is of type __NSArrayM and which contains this thing:
{ JsonResult = "[ { \"IsAuth\":\"true\" } ]"; }
It means that you already have converted the JSON to an object, namely an NSArray.
In this array that we don't see, this thing you're showing us is a dictionary (that we will name "dict") with its value being a String which itself represents another JSON object.
Let's get the value using the key:
if let value = dict["JsonResult"] as? String {
print(value)
}
Now "value" is supposed to be "[ { \"IsAuth\":\"true\" } ]".
This is a String which represents JSON. To decode the JSON, we first have to make the string into data then we can decode:
if let data = value.data(using: .utf8) {
if let content = try? JSONSerialization.jsonObject(with: data, options: []),
let array = content as? [[String: Any]]
{
print(array)
}
}

Swift Dictionary adding square brackets after key for a value of type [String]

I have a serializer method that turns my swift objects into dictionaries so that I can send them in http requests.
this particular method is the one giving me the problem
class func toDictionary ( _ order: Order ) -> Dictionary<String, Any> {
return [
"products" : NSArray(array:order.getProducts()),
"owning_user" : NSString(string: order.getOwningUser()),
"restaurant" : NSString(string: order.getRestaurantId())
]
}
order.getProducts() returns an array of type [String]
When I send this in a http request it gets sent as
{"products[]":["...","..."],
"restaurant":"sdfsdf"
}
Obviously my server is expecting products as the key therefore its not getting the proper values.
Any idea why the square brackets are being added?
Note:
My http requests are done via alamofire
EDIT:
Turns out the problem was with alamofire
Please see below for solution
Turns out this is a problem to do with alamofire's encoding when passing a dictionary as a httpBody.
For anyone having the same problem:
solve this by adding the following property to your Alamofire post request
encoing: JSONEncoding.default
the final request looks as follows
Alamofire.request( requestURL, method: .post, parameters: orderJson, encoding: JSONEncoding.default)
and it yields the following JSON being posted
{ restaurant: '580e33ee65b84973ffbc7beb',
products: [ '580f5cdafaa1067e55be696d' ],
owning_user: '580e2d174e93b0734e9a04cb'
}
As I had originally wanted.
Ok this appears to work here, so I think I need more context as to what your doing different. If this solves your issue, please up vote! Thanks!
Possible issues; you may be have an array within another array? If order.getProducts() already returns an array, don't place it in another. Another option may be to .flatMap it "NSArray(array:order.getProducts()).flatMap {$0}" << will make a single array out of arrays of arrays.
//: Playground - noun: a place where people can play
import UIKit
func toDictionary () -> Dictionary<String, Any> {
return [
"products" : NSArray(array:["Paper","Pencil","Eraser"]),
"owning_user" : NSString(string: "user2976358"),
"restaurant" : NSString(string: "TacoBell")
]
}
let rValue = toDictionary()
let jsonData:Data!
do {
jsonData = try JSONSerialization.data(withJSONObject: rValue, options: .prettyPrinted)
let newString = String(data: jsonData, encoding: .utf8)
print(newString!)
} catch
{
print(error)
}
The results in the Debug area show this
{
"restaurant" : "TacoBell",
"products" : [
"Paper",
"Pencil",
"Eraser"
],
"owning_user" : "user2976358"
}

Send an array as a parameter in a Alamofire POST request

My app is currently using AWS API Gateway and Alamofire to access different lambda functions that act as my backend.
I have the need to send an array as one of the parameters to one of those API end points, for that I am using the following code:
var interests : [String]
interests = globalInterests.map({ (interest) -> String in
return interest.id!
})
// Parameters needed by the API
let parameters: [String: AnyObject] = [
"name" : name,
"lastName" : lastName,
"interests" : interests
]
// Sends POST request to the AWS API
Alamofire.request(.POST, url, parameters: parameters, encoding: .JSON).responseJSON { response in
// Process Response
switch response.result {
case .Success:
print("Sucess")
case .Failure(let error):
print(error)
}
}
But that is not working because of the array is not being recognized by the API, but if I create a "static" array
let interests = ["a", "b", "c"]
Everything works as it is supposed to.
How can I fix this situation given that the array of interests come from another part of the code, how should I declare it or construct it?
A friend managed to accomplish this in Android using an
ArrayList<String>
EDIT:
Printing the parameters array shows me this:
["name":test, "interests": <_TtCs21_SwiftDeferredNSArray 0x7b05ac00>( 103, 651, 42), "lastName": test]
By using NSJSONSerialization to encode JSON, you can build your own NSURLRequest for using it in Alamofire, here's a Swift 3 example:
//creates the request
var request = URLRequest(url: try! "https://api.website.com/request".asURL())
//some header examples
request.httpMethod = "POST"
request.setValue("Bearer ACCESS_TOKEN_HERE",
forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")
//parameter array
let values = ["value1", "value2", "value3"]
request.httpBody = try! JSONSerialization.data(withJSONObject: values)
//now just use the request with Alamofire
Alamofire.request(request).responseJSON { response in
switch (response.result) {
case .success:
//success code here
case .failure(let error):
//failure code here
}
}
}
AnyObject can only represent class type. In Swift, Array and Dictionary are struct, instead of the class type in many other languages. The struct cannot be described into AnyObject, and this is why Any comes in. Besides of class, Any can be utilized in all other types too, including struct and enum.
Therefore whenever we type cast array or dictionary to AnyObject _TtCs21_SwiftDeferredNSArray error occurs.So we have to Any instead of AnyObject.
let parameters: [String: Any] = [
"name" : name,
"lastName" : lastName,
"interests" : interests
]
The problem is that you have just declared the array and not initialized it. That makes the interest array as nil even if u try to insert the data. Try writing
var interests = [String]()
instead of
var interests : [String]
let ValueArray = ["userid": name,"password":password]
pass ValueArray [parameters: ValueArray] also verify the encoding accepted by the API.
It turned out to be a problem with duplicated ids in the array. The code behind the API threw an exception that was not being send back as an error.
All the other answers are correct, I tested them after finding the problem and everything worked so I am going to up-vote them.
Thank you very much.

Response JSONString order not correctly after reading in iOS

I have JSONString from api as bellow:
[JSONString from api]
But after I read in iOS from Alamofire the order of the JSONString is not correct as api:
[JSON After read in iOS]
How can I keep the JSON format the same order as api?
As explained by #Nimit, JSON format represented in your callback and the API response is of least concern. What you need to care about is that when you are accessing the values from the response, the KEY should be same as seen in the API. No mismatch, not even of the case-sensitive letter, or you will always get the NIL in the response.
To explain it better to you with the use of Alamofire, let's me show you one example:
let APIURL = "https://api.yoururl.com"
Alamofire.request(.GET, APIURL , headers: headers) .responseJSON { response in
let value = response.result.value!
let JSONRes = JSON(value)
let KLValue = JSONRes["Kuala Lumpur"].int!
print(KLValue) //Or call a function to handle the callback
}
Here I am using SwiftyJSON for JSON. In the end, all you want to do is get the data out of the associated keys in the JSON response, no need to worry about how they have been formatted, or what's the order of Keys in the response - most of the time you will get the same as in the API - but in case it changes, need not to worry.
On the another front, to be sure that nothing happens to your app when JSON fields are nil, always put an if-let like this:
if let valueFromJSON = JSONRes["Kuala Lumpur"].string {
someVariable = valueFromJSON
} else {
someVariable = "No Value"
}
Thanks!
You can't do it, unless you write your own JSON parser. Any self-respecting JSON library won't guarantee you the order, if it wants to conform to the JSON spec.
From the definition of the JSON object:
the NSDictionary class represents an unordered collection of objects;
however, they associate each value with a key, which acts like a label
for the value. This is useful for modeling relationships between pairs
of objects.
If you have a jsonObject, such as data, then you can convert to json string like this:
let jsonString = JSONSerialization.data(withJSONObject: data,
options: JSONSerialization.WritingOptions.sortedKeys)
when you use sortedKeys option, the json will be specifies that the output sorts keys in lexicographic order.

Resources