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

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!)
}

Related

Cast value from Dictionary to String in Swift

I got below error. I can print the value with print but can't declare it to label. I guess I have a problem on casting any to string.
"Could not cast value of type '__NSCFNumber' (0x1b6ffd7f0) to
'NSString' (0x1b7009398)."
let dataDict: [AnyHashable: Any] = LBValueConverter.manageValueSnore(dataValue)
// print("kUUIDSnoreSensor dict: \(dataDict)")
let allValues = Array(dataDict.values)
for value in allValues{
mTextLabel.text = value as! String
}
That's because you are trying to cast from NSNumber to String. If you have different values inside this dictionary use this:
for value in dataDict.values {
if let value = value as? String {
mTextLabel.text = value
}
else if let value = value as? NSNumber {
mTextLabel.text = value.stringValue
}
....
}
In case all values are NSNumber you can cast your dataDict to [String: NSNumber] to avoid the if-lets

How do I convert NSDictionary to Dictionary?

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

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

Assign Value of NSNumber to AnyObject

I have a segment of code that gets info from an API, and I need to add it to a Dictionary. The code is below:
typealias JSONdic = [String: AnyObject]
var weatherData: AnyObject = StorageManager.getValue(StorageManager.StorageKeys.WeatherData)!
let json: AnyObject = ["Any": "Object"]
if let json = json as? JSONdic, history = json["history"] as? JSONdic, tempi = history["tempi"] as? Int, hum = history["hum"] as? String, precip = history["precipi"] as? String{
println("Temperature:\(tempi) Humidity:\(hum) Precipitation:\(precip)")
weatherData = [NSDate: AnyObject]()
let temp = tempi as NSNumber
weatherData[(The Current Date)] = temp
}
I want to first add "temp" to the weatherData Dictionary, but even after casting it to NSNumber, I am told that an NSNumber value cannot be assigned to the AnyObject?! type. Can anyone help me fix this?
Your weatherData variable is of type AnyObject. Despite the fact that you later assign it a value of type [NSDate: AnyObject], the variable itself is still considered by the compiler to be AnyObject. You then hit problems because you try to subscript it, assigning an NSNumber, which is obviously not possible on AnyObject.
Your declaration of weatherData should ensure it is the type you intend. If you are sure that your StorageManager will return you the appropriate dictionary type for the weather data key, you can force downcast it to the correct type:
var weatherData = StorageManager.getValue(StorageManager.StorageKeys.WeatherData) as! [NSDate: NSObject]

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