By Apple's definition of dictionary values, since a key may or may not exist any value returned from a dictionary will be optional
You can also use subscript syntax to retrieve a value from the
dictionary for a particular key. Because it is possible to request a
key for which no value exists, a dictionary's subscript returns an
optional value of the dictionary's value type. If the dictionary
contains a value for the requested key, the subscript returns an
optional value containing the existing value for that key.
I am trying to make a networking call with an auth_token in my parameters dictionary.
let params = ["auth_token" : authToken()]
print(params["auth_token"]) // shows Optional(dwadhuiwahd)
The value in the dictionary is not an optional. I check this with conditional binding in the authToken() function and by printing it out. However when printing out the dictionary key that holds the auth_token it shows up as:
Optional(dawudhawhd)
And this is causing my networking request to fail.
Alamofire.request(.GET, urlString, parameters: params, encoding: .JSON).responseJSON {
(response) in
//this fails
}
Unless I hardcode the url.
let urlString = "https://staging.travelwithterra.com/api/v1/users/" + agentID + "/agent_info?auth_token=" + authToken()
Alamofire.request(.GET, urlString, encoding: .JSON).responseJSON {
(response) in //this doesnt fail }
My question is, how come Alamofire doesnt take this into account? OR what am I doing wrong, its killing me!
********solution**********
DONT USE .JSON ENCODING! .URL ENCODING!!!
You just need to add an exclamation mark. As seen below.
print(params["auth_token"]!) // doesn't show Optional(dwadhuiwahd) :P
Related
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...
I need to do this in my project:
I can do this easily if I manually append strings to my URL in Alamofire, but I don't want that. I want the parameters as Parameter object.
multiple values in one common key for parameter
What I've been doing:
public func findCreate(tags: [String], withBlock completion: #escaping FindCreateServiceCallBack) {
/* http://baseurlsample.com/v1/categories/create_multiple?category_name[]=fff&category_name[]=sss */
let findCreateEndpoint = CoreService.Endpoint.FindMultipleCategories
let parameters: Parameters = ["category_name[]" : tags]
Alamofire.request(
findCreateEndpoint,
method: .post,
parameters: parameters,
encoding: URLEncoding(destination: .queryString),
headers: nil
).responseJSON { (response) in
print(response)
}
//....
}
The current result if I run this is okay but the values sent to the server has [" "]. For example:
["chocolate"]
Again, the questions are, in which part of my whole code I'm wrong? How can I send parameters like the above that have one common key and multiple values?
I also have tried adding encoding option to the Alamofire.request() If I add encoding: JSONEncoding.prettyPrinted or encoding: JSONEncoding.default I get status code 500.
Some links that have the same question but no exact answers, I always see posts that have answers like using a custom encoding and that's it.
https://github.com/Alamofire/Alamofire/issues/570
Moya/Alamofire - URL encoded params with same keys
Additional info:
This works, but I need to send multiple String:
let parameters: [String : Any] = ["category_name[]" : tags.first!]
And this works as well:
Alamofire.request("http://baseurlsample.com/v1/categories/create_multiple?category_name[]=fff&category_name[]=sss", method: .post).responseJSON { (data) in
print(data)
}
You don't need a custom encoding for this format.
You can send parameters encoded like this:
category_name[]=rock&category_name[]=paper
By using URLEncoding (which you're already doing) and including the multiple values that should have the same key in an array:
let parameters: Parameters = ["category_name": ["rock", "paper"]]
It'll add the [] after category_name for you, so don't include it when you declare the parameters.
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.
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.
I am having a devil of a time trying to work with Dictionaries in Swift. I have created the following Dictionary of Dictionaries but I am unable to unpack it.
var holeDictionary = Dictionary<String,Dictionary<String,Dictionary<String,Int>>>()
I can get the first Dictionary out with:
var aDictionary = holeDictionary["1"]
But trying to access the next Dictionary within it gives me an error as follows:
var bDictionary = aDictionary["key"] // [String : Dictionary<String, Int>]?' does not have a member named 'subscript'
I know what the contents of the Dictionaries are and can verify them with a println(aDictionary). So how can I get to the Dictionaries buried deeper down?
The key subscript on Dictionary returns an optional, because the key-value pair may or may not exist in the dictionary.
You need to use an if-let binding or force unwrap the optional before you can access it to subscript it further:
if let aDictionary = holeDictionary["1"] {
let bDictionary = aDictionary["key"]
}
Edit, to add forced unwrap example:
If you're sure that the key "1" exists, and you're okay with assert()ing at runtime if the key doesn't exist, you can force-unwrap the optional like this:
let bDictionary = holeDictionary["1"]!["key"]
And if you're sure that the key "key" will exist, you'd do this instead:
let bDictionary = holeDictionary["1"]!["key"]!
Accordingly to the swift documentation:
Because it is possible to request a key for which no value exists,
a dictionary’s subscript returns an optional value of the dictionary’s
value type
When you retrieve an item from a dictionary you have an optional value returned. The correct way to handle your case is:
var holeDictionary = Dictionary<String,Dictionary<String,Dictionary<String,Int>>>()
if let aDictionary = holeDictionary["1"] {
var bDictionary = aDictionary["key"]
}