Since null is a perfectly valid JSON value, we should be able to do this:
let params:[String:AnyObject?] = ["a": nil, "b": 1]
Alamofire.request(.POST, "http://example.com", parameters:params)
However, since params is defined as [String:AnyObject]
, this yields the following compiler error:
'AnyObject?' is not identical to 'AnyObject'
No, I think you should use NSNull
instead of nil
, like
let params:[String:AnyObject?] = ["a": NSNull(), "b": 1]
Alamofire.request(.POST, "http://example.com", parameters:params)
Underlaying NSDictionary
not support nil
as the value of a entry.
Why do you prefer that? That seems objective-c-ish, not swiftish.
On Friday, September 12, 2014, YANKE Guo [email protected] wrote:
No, I think you should use NSNull instead of nil, like
let params:[String:AnyObject?] = ["a": NSNull(), "b": 1]
Alamofire.request(.POST, "http://example.com", parameters:params)Underlaying NSDictionary not support nil as the value of a entry.
—
Reply to this email directly or view it on GitHub
https://github.com/Alamofire/Alamofire/issues/92#issuecomment-55435041.
I think the reason is line 86 of Alamofire.swift
OC version of NSJSONSerialization
has limits
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
Swift is based on Foundation.framework, things won't change much.
Swift version of NSJSONSerialization
has the function signature
class func dataWithJSONObject(obj: AnyObject, options opt: NSJSONWritingOptions, error: NSErrorPointer) -> NSData?
, preventing things like Dictionary<String,AnyObject?>
passing in.
Set nil value of a Dictionary for a key will remove that key.
So, one solution I came about is replacing occurrence of nil
by NSNull
before passing it into NSJSONSerialization
. But iterating into a object is too expensive.
So, why not NSNull
?
That's my consideration, maybe totally wrong.
Expecting a better solution.
It doesn't really make sense for a dictionary to have an optional value type, since setting a nil
for a key removes that key from a dictionary:
var parameters: [String: AnyObject?] = [:]
parameters["foo"] = nil
parameters.keys.array.count // 0
If this weren't the case, we'd expect a non-empty set of keys.
As @yanke-guo points out, NSNull
is the appropriate value to pass here.
Why is the parameters dictionary using AnyObject vs Any? i.e. no support for nesting Swift Dictionaries/Arrays, etc...
Everything needs to bridge back to Objective-C to use NSJSONSerialization
@rlaferla. Hopefully Swift 3 will give us some better options.
:+1: to NSNull()
i got crash can any one help me how to set dictionary value in parameter i am new to swift ...
let parameters : [String: AnyObject] =
[
"user_name": [FBUserDataDict.value(forKey: "name")] as AnyObject,
"user_email":["email" : FBUserDataDict] as AnyObject,
"user_password":"" as AnyObject,
"user_phone_number":"" as AnyObject,
"user_registration_type":"facebook" as AnyObject,
"user_onesignalid":"1" as AnyObject,
"user_profile_pic_url":"" as AnyObject
]
@PurviJani Posting on old, unrelated issues is not a good way to get support. These types of questions are best suited for Stack Overflow and tag Alamofire
. We use our GitHub project for bug reports and feature requests.
Best of luck! 🍻
From our Contribution Guidelines
We don't use GitHub as a support forum. For any usage questions that are not specific to the project itself, please ask on Stack Overflow instead. By doing so, you'll be more likely to quickly solve your problem, and you'll allow anyone else with the same question to find the answer. This also allows maintainers to focus on improving the project for others.
I think [String: Any] is dangerous for JSON parameter specification unless Alamofire does some post-manipulation of the parameters dictionary before sending it to the json serializer. It's all too easy to accidentally include a String? in the parameters and cause a crash at runtime.
What I'd like to see is a transformation of all nil optionals to NSNull() automatically during serialization. @jshier what do you think about that?
@esetnik That issue should be fixed by SE-140 as part of Swift 3.0.1 which I believe will be part of Xcode 8.1, which will be released alongside iOS 10.1. When that will be I'm not sure.
In the mean time, doing what you suggest would be very complicated, requiring us to build our own non-generic optional protocol (for casting), along with functionality to extract values or NSNull
and dynamically rebuild parameters
values, which are arbitrarily sized. So it would be terrible for performance, requiring O(n)
time and O(n)
memory (as we'd have to copy the value passed in). It would also be slowed down by the fact that we'd have to do is
checks on every value in parameters, both to check for optionality as well as to see if it's a collection type. So, given that it will be fixed rather soon, I'd say we won't be adding that functionality.
@cnoon, anything to add?
Oh thanks for the link. That looks like the best solution until serialization is out of Obj-c world. The only downside I can see is the necessity to explicitly mark all optional parameter values as Any
I'm not sure where you get that necessity? The proposal bridges values (of any type) as their value or NSNull
. The Any
cast is done when passed as a [String : Any]
value to the Alamofire APIs.
Yes I was referring to the ensuing awkwardness of creating the [String: Any] parameters with optional values as per SE-140. This isn't really an Alamofire problem anymore, rather awkward bridging between languages. I can't think of a better option so I don't have anything further to contribute. Thanks for the feedback.
FWIW this is the awkwardness I'm referring to:
When we put an Optional into an Any, we should warn on the implicit conversion:
let x: Int? = 3
let y: Any = x // warning: Optional was put in an Any without being unwrapped
// `print` takes parameters of type Any
print(x) // warning: Optional was passed as an argument of type Any without
// being unwrapped
// `NSMutableArray` has elements of type `id _Nonnull` in ObjC,
// imported as `Any` in Swift
let a = NSMutableArray()
a.add(x) // warning: Optional was passed as an argument of type Any without
// being unwrapped
If passing the Optional is intentional, the warning can be suppressed by making the conversion explicit with as Any:
let y: Any = x as Any
print(x as Any)
a.add(x as Any)
Problem Solved ! Here is the solution...
let parameters : [String: AnyObject] =
[
"user_name": FBJSONdict["name"] as! String as AnyObject,
"user_email":FBJSONdict["email"] as! String as AnyObject,
"user_password":"" as AnyObject,
"user_phone_number":"" as AnyObject,
"user_registration_type":"facebook" as AnyObject,
"user_onesignalid":"1" as AnyObject,
"user_profile_pic_url":FBJSONdict["picture"] as! String as AnyObject
]
print("\(parameters)")
@jshier Sorry! i thought this form is the best platform for solving & discussing on issues and find the solution.
Trying to follow all of the discussion, but I'm still not fully understanding.
First, @mattt, you can definitely put nil in a dictionary as long as you don't use subscripting like in your example.
Xcode 8.3.3
Swift 3.1
Alamofire 4.5.0
When I use nil in a [String: Any?] dictionary, I get a warning:
"Expression implicitly coerced from 'Any?' to Any"
But the JSON output correctly has null.
If this is going to happen automatically, I don't see why parameters shouldn't be [String: Any?]. It's not much safer to have it as [String: Any], since that only protects top level values. You can easily depend on the automatic behavior without getting a compiler warning as shown in the following example:
let dictionary: [String: Any?] = [
"key": nil
]
let parameters: [String: Any] = [
"dictionary": dictionary
]
request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
My other question is what's happening with responses. I'm currently handling responses like this:
request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
.responseJSON { response in
let responseDictionary = response.value as? [String: Any?]
}
Does that depend on the same automatic casting of NSNull() to nil? Is this safe?
Now that Swift's underlying behavior has changed, it may be time for this to change. However, it really depends on how other parameter encodings handle it.
As for the response question, casting to [String : Any?]
seems safe, but has the added overhead of having to double unwrap the dictionary values, which can be a pain. If you cast to [String : Any]
, then it appears Swift does automatically convert nil
values to NSNull
(at least in my brief testing on Swift 4). However, we always recommend that any significant amount of response parsing should be done generically through a response serializer so you don't have to mess with nasty casting like this.
Got it, thanks. I'll keep an eye out for the update
@mattt pointed out that
var parameters: [String: AnyObject?] = [:]
parameters["foo"] = nil
parameters.keys.array.count // 0
would remove the key, but you should be able to do parameters["foo"] = nil as AnyObject?
.
Most helpful comment
It doesn't really make sense for a dictionary to have an optional value type, since setting a
nil
for a key removes that key from a dictionary:If this weren't the case, we'd expect a non-empty set of keys.
As @yanke-guo points out,
NSNull
is the appropriate value to pass here.