Send an array as a parameter in a Alamofire POST request - ios

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.

Related

Alamofire 5.5 responseDecodable on JSON array

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?

When i use UserDefaults.standard.object in Alamofire it shows "Extra argument in call" error

When I Use the Alamofire Function With Some CoreData Like UserDefaulfts.standard.object then the xcode will Throws an Error Extra Argument in call i tried approx all the Ans from Stackoverflow Like HTTPMethod and .JSONEncoding but not Happening
func Sync(){
let url = "http://abcd.com/reportlist"
let parameters = ["sort_by":"month_year",
"start":"1",
"end":"10"]
let key2 = UserDefaults.standard.object(forKey: "accessToken")
let headers = [
"Authorization": key2,
"Content-Type": "application/x-www-form-urlencoded"]
Alamofire.request(url, method: .post, parameters: parameters, headers: headers).responseJSON { response in
if response.result.isSuccess
{
var dictVal = response.result.value
let data1 = response.data
.................
Hold option key and press object(forKey function to read documentation hint. You will see that return type of that function is optional since nothing can be stored under the key you pass, right? So your key2 constant also is of Optional type.
Then when you use it in dictionary, headers itself becomes of type "String: Optional(String)" as one of values is Optional (key2).
Alamofire expects [String: String] dictionary type in headers parameter and since the type of your headers dict does not match it generates this error - extra argument. Like I don't know why you pass it to me I do not expect anything of type like that
That is it
So you either use string(forKey method which will return empty string in case nothing is stored under the key passed, or provide default value like that
let key2 = (UserDefaults.standard.object(forKey: "accessToken") as? String) ?? "defaultAccessTokenOrNothing" which has the same result as first suggestion
the Key used here is a String Type and in Header passing, u did not Mention the type of variable in a header
before I used this
let key2:String = UserDefaults.standard.object(forKey: "accessToken") as! String
xcode is again Happy to Code...

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"
}

JSON POST Request with Alamofire and EVReflection

I"m trying to do a simple POST request in swift. I'd like to post an array of objects (alarms) and I"m constantly running into problems. Here is what I have so far:
func submitAlarms(alarms: [Alarm],onCompletion: #escaping ([Alarm]) -> Void){
let route = baseURL
let token = SessionManager().storedSession!.token.idToken
let headers = [
"Authorization": "Bearer \(token)"
]
let parameters = [
"alarms": alarms.toJsonString()]
print("Parameters ", parameters)
Alamofire.request(route, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers)
.validate()
.responseJSON{ response in
switch response.result{
case .failure(let error):
print("POST Alarm JSON Error: \(error)")
case .success(let value):
let json = JSON(value)
let alarms_json = json["alarms"]
print(alarms_json)
let alarms_string = alarms_json.rawString()
let alarms: [Alarm] = [Alarm](json: alarms_string)
//let alarms = [Alarm](json: res_string)
onCompletion(alarms)
}
}
}
I"m mainly having issues with the parameters part. For whatever reason, Alamofire can't seem to take an array of objects, so I created a dictionary with a top level alarm key that holds the array of alarms. I then use EVReflection to convert my alarm array to a json string. But that conversion gives me this:
Parameters ["alarms": "[\n{\n \"isActive\" : true,\n \"volume\" : 10,\n \"minute\" : 15,\n \"days\" : [\n 0,\n 1,\n 2,\n 3,\n 4,\n 5,\n 6\n ],\n \"brightness\" : 10,\n \"hour\" : 6,\n \"audio\" : 4,\n \"duration\" : 1,\n \"label\" : \"Alarm\",\n \"allowSnooze\" : false,\n \"isSynchronized\" : false\n}\n]"]
Where are all these extra \ and \n coming from? Using Alamofire, the server responds with a 400 because these extra pieces make it invalid JSON. What am I missing here? I am using Xcode 8/Swift 3, with the latest Alamofire and EVReflection. I"ve looked at other examples, but they are mostly out of date. A lot of them also use extensions and custom encoding, which seems ridiculous for such a simple request. Appreciate any help and bonus points if you can clean up the response as well.
For creating the parameters you are doing a:
let parameters = ["alarms": alarms.toJsonString()]
Which means that you will get 1 paramater that will contain the json as a string.
EVReflection will first convert the alarms object to a dictionary and then convert it to json using the Apple standard function:
JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
Since it's using the .prettyPrinted option it will be formatted in a nice readable format. This means that it will add /n for where there should be enters plus spaces for making a nice layout.
In your case you have to look at the api that you are calling to see what you should change in your code. Currently you are doing a http get. Then the parameters should be simple key - value pairs. You could use the EVReflection toDictionary function for that if you want to post all your object properties. But in your case I think you need to do a post (see Alamofire documentation) and then you can put the json in the request body.
P.S. I just found this issue by accident. You are welcome to also create issues at https://github.com/evermeer/EVReflection or ask questions at https://gitter.im/evermeer/EVReflection Then i will receive a notification for it.

Alamofire 4 Request is returning NSArray, cannot figure out how to parse with SwiftyJSON in Swift 3

The previous version of this Alamofire .POST request and SwiftyJSON parsing was working fine with Swift 2.2 and Xcode 7. Updated to Swift 3.0, which required install of updated Alamofire (4.0.0) and updated SwiftyJSON. After some syntax adjustments, everything now compiles.
The problem is that my web app now appears to return an NSArray, whereas before, when the code worked, a nearly identical Request got an NSData return that SwiftyJSON would parse. The following code shows the Request:
Alamofire.request("https://www.mywebappurl", method: .post, parameters: parameters)
.responseJSON { (response:DataResponse<Any>) in
if let data = response.result.value as? Data {
The data variable is never assigned because the response type is not NSData. Tried to cast to that type by changing the last line to this:
let data = response.result.value as! Data
That version compiles fine, but as soon as you trigger the Request you get an error: Could not cast value of type '__NSArrayI' (0x105a37c08) to 'NSData'
Note that the request is returning data as expected. And in the previous Alamofire this data was NSData without any action being taken to convert it. Since it appears from the aforementioned error that the returned data is an array already, has it already been parsed by Alamofire? Or is there something that can be done to make SwiftyJSON parse it like it was parsed before?
EDIT
Since the current type being returned is an NSArray, and the web app is sending an array, it seems possible that the SwiftyJSON parsing is no longer necessary? Tried the following code:
Alamofire.request("https://www.mywebappurl", method: .post, parameters: parameters)
.responseJSON { (response:DataResponse<Any>) in
let testdata = response.result.value as! NSArray
print(testdata[0])
Which yielded this output in the Xcode "All Output" screen:
{
1 = "08/01/16";
2 = 285;
3 = 160;
}
It's not clear to me if that means testdata is an array of arrays, an array of dictionaries, or an array of unparsed strings. So as an alternative to answering the question of "How can one obtain an NSData response from this Alamofire request", a response to the question of "What one line of code, if any, can be used to obtain the integer value 285 from the NSArray testdata shown above?" would also resolve the problem.
If you change the data initialization line from this:
if let data = response.result.value as? Data {
to this:
let data = response.data
Then the request results in an NSData return that can be parsed by SwiftyJSON as it was before. The working request and parsing code looks like this:
Alamofire.request("https://www.mywebappurl", method: .post, parameters: parameters)
.responseJSON { (response:DataResponse<Any>) in
let data = response.data
let jsonvalues = JSON(data: data!)
Some error handling might be appropriate to add, but that's not related to the question at hand.

Resources