How do I convert NSDictionary to Dictionary? - swift

I have already updated to XCode 8 and now I need to convert my code from Swift 2 to Swift 3.
Before, when I want to convert NSDictionary to Dictionary, I just wrote the following:
let post_paramsValue = post_params as? Dictionary<String,AnyObject?>
where post_params is NSDictionary.
But now with Swift 3, I am receiving this error:
NSDictionary is not convertible to Dictionary
Why? What's changed?
Edit 1
I've also tried the following:
let post_paramsValue = post_params as Dictionary<String,Any>
But that gives this error:
Edit 2
I've also tried the following:
let post_paramsValue = post_params as Dictionary<String,Any>
Where I declare NSDictionary instead of NSDictionary!, but it doesn't work; I got this error:
Edit 3
I've also tried the following:
let post_paramsValue = post_params as Dictionary<String,Any>!
But I received this error:

NSDictionary in Objective-C has always non-optional values.
AnyObject has become Any in Swift 3.
Considering the first two "rules" NSDictionary can be bridge cast to Dictionary
let post_paramsValue = post_params as Dictionary<String,Any>
If the source NSDictionary is an optional you might use as Dictionary<String,Any>? or as? Dictionary<String,Any> or as! Dictionary<String,Any> or as Dictionary<String,Any>! depending on the actual type of the NSDictionary

For those who have a problem with NSDictionary, simply use this extension:
Swift 3.0
extension NSDictionary {
var swiftDictionary: Dictionary<String, Any> {
var swiftDictionary = Dictionary<String, Any>()
for key : Any in self.allKeys {
let stringKey = key as! String
if let keyValue = self.value(forKey: stringKey){
swiftDictionary[stringKey] = keyValue
}
}
return swiftDictionary
}
}

You just need to declare the NSDictionary properly in objc
For example: NSDictionary<NSString *, NSString*> gets translated automatically to [String: String] in swift interfaces

Related

Swift JSON to Dictionary<String: Any>. Then cast "Any" as NSMutableArray

I am trying to read from a JSON String and draw a graph with its data:
{"y-axis-data":{"min":0.0,"max":1000,"Step":100.0},"x-labels":[1994,2000,2005],"y-values":[20,305,143]}
I wrote a function to create a dictionary from the string:
func jsonToDictionary(jsonString: String) -> [String: Any]? {
if let jsonData: Data = jsonString.data(using: String.Encoding.utf8) {
do {
return try (JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any])!
} catch {
some bla bla
}
}
return nil
}
The return dictionary should count 3 elements inside when I pass my JSON string, and it does.
I can then change of some variables (Double) which are 0 until now and give them the values of min max and Step from the "y-axis-data" key of my dictionary, using {"min":0.0,"max":1000,"Step":100.0} as a dictionary it self. Works fine.
My trouble comes when trying to initialize other atributes:
self.my_view!.x-labels = (jsonToDictionary!["x-labels"]) as? NSMutableArray
my_view has already been initialized as UIViewCustomClass(frame: someFrame)
myview.x-labels is an NSMutableArray and it is initialized as nil. After executing that line of code it is still nill, of course myview.x-labels.count is nil. if I do it this way:
self.my_view!.x-labels = (jsonToDictionary!["x-labels"]) as! NSMutableArray
I get a warning :
Treating a forced downcast to NSMutableArray as optional will never produce nil.
It then crashes on runtime with this error:
Could not cast value of type '__NSArrayI' (0x110ed5448) to 'NSMutableArray' (0x110ed4598).
of course the exact same thing happens with "y-values"
What is the right way to do this?
It was because your json!["x-labels"] is implicitly treated as NSArray, so you somehow had to do a "double-force-cast"
// get the converted json
let j = jsonToDictionary(jsonString: dict)
// double cast
let m = (j!["x-labels"] as! NSArray).mutableCopy() as! NSMutableArray
Result:
I think, JSONSerialization class converts into Array, and then Swift cannot cast Array to NSMutableArray. You can do this (Swift4):
let array = (jsonToDictionary!["x-labels"]) as? [Int]
if array != nil {
self.my_view!.x-labels = NSMutableArray(array: array!)
}

Init has been renamed to init(describing) error in Swift 3

This code works fine in Swift 2:
guard let userData = responseData["UserProfile"] as? [String : AnyObject] else { return }
var userProfileFieldsDict = [String: String]()
if let profileUsername = userData["Username"] as? NSString {
userProfileFieldsDict["username"] = String(profileUsername)
}
if let profileReputationpoints = userData["ReputationPoints"] as? NSNumber {
userProfileFieldsDict["reputation"] = String(profileReputationpoints)
}
But, in Swift 3 it throws an error on userProfileFieldsDict["reputation"] saying
init has been renamed to init(describing:)
My question is why does it trigger on that line and not on the userProfileFieldsDict["username"] assignment line, and how to go about fixing it? I'm assuming it's because I'm casting a NSNumber to a String, but I can't really understand why that matters.
NSNumber is a very generic class. It can be anything from a bool to a long to even a char. So the compiler is really not sure of the exact data type hence it's not able to call the right String constructor.
Instead use the String(describing: ) constructor as shown below
userProfileFieldsDict["reputation"] = String(describing: profileReputationpoints)
Here's more info about it.
You need to drop your use of Objective-C types. This was always a bad habit, and now the chickens have come home to roost. Don't cast to NSString and NSNumber. Cast to String and to the actual numeric type. Example:
if let profileUsername = userData["Username"] as? String {
userProfileFieldsDict["username"] = profileUsername
}
if let profileReputationpoints = userData["ReputationPoints"] as? Int { // or whatever
userProfileFieldsDict["reputation"] = String(profileReputationpoints)
}

Convert from NSDictionary to [String:Any?]

I am using xmartlabs/Eureka to build an app with a dynamic form.
In order to fill the form I have to use setValues(values: [String: Any?]).
But I have the form values in an NSDictionary variable and I cannot cast it to [String:Any?].
Is there a way to convert an NSDictionary to [String:Any?] ?
Just an example:
if let content = data["data"] as? [String:AnyObject] {
print(content)
}
The data is a JSON object here. Use it accordingly.
Hope this helps:
let dict = NSDictionary()
var anyDict = [String: Any?]()
for (value, key) in dict {
anyDict[key as! String] = value
}

Why is Swift Dictionary not bridgeable?

Dictionary is a bridged type, why is it that I can switch from Swift dictionary to NSDictionary but not the other way around? (Compile Error: NSDictionary is not convertible to 'Dictionary')
According to Apple's doc:
All NSDictionary objects can be bridged to Swift dictionaries, so the Swift compiler replaces the NSDictionary class with [NSObject: AnyObject] when it imports Objective-C APIs.
import Foundation
var swiftDict = ["0": "zero"]
let nsDict:NSDictionary = swiftDict
let backToSwiftDict:Dictionary = nsDict
This is correct but you have to perform a type safe cast from NSDictionary to a Dictionary
var swiftDict = ["0": "zero"]
let nsDict: NSDictionary = swiftDict
let backToSwiftDict: Dictionary = nsDict as Dictionary
... or you can cast it back into a dictionary with type-safe fields ...
var swiftDict = ["0": "zero"]
let nsDict:NSDictionary = swiftDict
let backToSwiftDict = nsDict as! [String:String]

How to unwrap optional NSDictionary

I cannot unwrap an optional NSDictionary. I am using NSDictionary because that is what I am returned when loading a plist from a file path.
var dict: NSDictionary? = NSDictionary(contentsOfFile: getFontPath())
assert(dict != nil, "Top level dictionary from plist can't be nil")
var fontArray = dict!objectForKey("fonts") as [NSDictionary]
The compiler is not recognizing dict! as an unwrap - tells me I should separate sequences by a ;
What is the problem and solution?
You're missing the attribute accessor (ie, the dot) after the exclamation point:
var fontArray = dict!.objectForKey("fonts") as [NSDictionary]
^
Depending on what you're doing, it might make more sense to use if let, eg:
if let unwrappedDict = dict as? NSDictionary {
}