Cast value from Dictionary to String in Swift - 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

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

Cannot downcast object of type Any to Int when accessing from dictionary

I have a Gfycat struct that represents the data I want to store after making a network call to the Gfycat API.
typealias JSONDictionary = [String: Any]
struct Gfycat {
let id: String
let number: Int
}
In an extension to the Gfycat struct, I wrote a failable initializer that takes a dictionary of type [String: Any] as its argument. This dictionary is then used to assign values to the struct's properties. This is the original init method I wrote:
extension Gfycat {
init?(dictionary: JSONDictionary) {
guard let id = dictionary["gfyId"] as? String,
let number = dictionary["gfyNumber"] as? Int { return nil }
self.id = id
self.number = number
}
}
The problem is that when accessing a value from the dictionary, I cannot downcast the value from Any to Int. I must first downcast Any to String, then convert that string to Int. Is this a bug or rather a feature of Swift that I don't understand?
This was my solution:
extension Gfycat {
init?(dictionary: JSONDictionary) {
guard let id = dictionary["gfyId"] as? String,
let uncastedNumber = dictionary["gfyNumber"] as? String,
let number = Int(uncastedNumber) else { return nil }
self.id = id
self.number = number
}
}
I must first downcast Any to String, then convert that string to Int. Is this a bug or rather a feature of Swift that I don't understand?
It's neither a bug nor a feature of Swift. It's a fact about the dictionary you're working with. This thing is a String, not an Int. So you cannot cast it to an Int.

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]

Swift AnyObject is not convertible to String/Int

I want to parse a JSON to object, but I have no idea how to cast AnyObject to String or Int since I'm getting:
0x106bf1d07: leaq 0x33130(%rip), %rax ; "Swift dynamic cast failure"
When using for example:
self.id = reminderJSON["id"] as Int
I have ResponseParser class and inside of it (responseReminders is an Array of AnyObjects, from AFNetworking responseObject):
for reminder in responseReminders {
let newReminder = Reminder(reminderJSON: reminder)
...
}
Then in Reminder class I'm initialising it like this (reminder as AnyObject, but is Dictionary(String, AnyObject)):
var id: Int
var receiver: String
init(reminderJSON: AnyObject) {
self.id = reminderJSON["id"] as Int
self.receiver = reminderJSON["send_reminder_to"] as String
}
println(reminderJSON["id"]) result is: Optional(3065522)
How can I downcast AnyObject to String or Int in case like this?
//EDIT
After some tries I come with this solution:
if let id: AnyObject = reminderJSON["id"] {
self.id = Int(id as NSNumber)
}
for Int and
if let tempReceiver: AnyObject = reminderJSON["send_reminder_to"] {
self.id = "\(tempReceiver)"
}
for string
In Swift, String and Int are not objects. This is why you are getting the error message. You need to cast to NSString and NSNumber which are objects. Once you have these, they are assignable to variables of the type String and Int.
I recommend the following syntax:
if let id = reminderJSON["id"] as? NSNumber {
// If we get here, we know "id" exists in the dictionary, and we know that we
// got the type right.
self.id = id
}
if let receiver = reminderJSON["send_reminder_to"] as? NSString {
// If we get here, we know "send_reminder_to" exists in the dictionary, and we
// know we got the type right.
self.receiver = receiver
}
reminderJSON["id"] gives you an AnyObject?, so you cannot cast it to Int You have to unwrap it first.
Do
self.id = reminderJSON["id"]! as Int
if you're sure that id will be present in the JSON.
if id: AnyObject = reminderJSON["id"] {
self.id = id as Int
}
otherwise
Now you just need to import Foundation. Swift will convert value type(String,int) into object types(NSString,NSNumber).Since AnyObject works with all objects now compiler will not complaint.
This is actually pretty simple, the value can be extracted, casted, and unwrapped in one line: if let s = d["2"] as? String, as in:
var d:[String:AnyObject] = [String:AnyObject]()
d["s"] = NSString(string: "string")
if let s = d["s"] as? String {
println("Converted NSString to native Swift type")
}