Reputation: 31283
I have a Swift struct like this.
struct Usage {
var totalData: Double
var remainingTotalData: Double
init(jsonData: NSData) {
var jsonDict = [String: AnyObject]()
do {
jsonDict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as! [String: AnyObject]
} catch {
print("Error occurred parsing data: \(error)")
}
totalData = jsonDict["totalfup"] as! Double
remainingTotalData = jsonDict["totalrem"] as! Double
}
}
From an API, I get the following JSON response. This is the println of the jsonDict
variable.
[
"totalfup": 96.340899,
"totalrem": 3548710948
]
When I try to assign the value of the totalfup
to the property totalData
, I get this error.
Could not cast value of type 'NSTaggedPointerString' to 'NSNumber'
Anyone knows why? I tried changing the property type to float
and then the whole struct to class but still the issue occurs.
Upvotes: 78
Views: 63576
Reputation: 2841
I posted my answer in a similar thread.
if let jsonDict_any = jsonDict["totalfup"],
let jsonDict_double = Double("\(jsonDict_any)") {
//..
}
Essentially, this avoids having to try multiple typecastings. By checking first for its existence, we can can then use string interpolation, then try casting it to a Double.
As @Alexander pointed out, this solution allows any Type
to become a String
and be checked for a Double
value. Only use this if you're not concerned about the value's Type
prior to interpolation.
Upvotes: 0
Reputation: 41
Tested and Working for Swift 5.0.
I had the same problem.
This worked for me.
// check dict["dummy"] is a String first
if let receivedData = (dict["dummy"]).doubleValue {
// add more code in case the condition is true
}
else {
// add more code in case the condition is false
}
It is really helpful when you want to compare the received data or just check the value like the following example.
let receivedData = (results["data"]!).doubleValue
if (receivedData == 0){
self.label.text = "Nothing seem to be added yet!"
}
Upvotes: 0
Reputation: 10317
The reason of the error is jsonDict["totalfup"]
is a String (NSTaggedPointerString
is a subclass of NSString
) , so you should convert String to Double.
Please make sure, catch exception and check type before force-unwrap !
totalData = (jsonDict["totalfup"] as! NSString).doubleValue
For safety, using if let
:
// check dict["totalfup"] is a String?
if let totalfup = (dict["totalfup"] as? NSString)?.doubleValue {
// totalfup is a Double here
}
else {
// dict["totalfup"] isn't a String
// you can try to 'as? Double' here
}
Upvotes: 117
Reputation: 5213
let strStatus:String = dictProperty.value(forKey: "StatusType") as! String
let myStatus = Double.init(strStatus)
extension String {
func toDouble() -> Double? {
let numberFormatter = NumberFormatter()
numberFormatter.locale = Locale(identifier: "en_US_POSIX")
return numberFormatter.number(from: self)?.doubleValue
}
func toInt() -> Int? {
let numberFormatter = NumberFormatter()
numberFormatter.locale = Locale(identifier: "en_US_POSIX")
return numberFormatter.number(from: self)?.intValue
}
func toFloat() -> Float? {
let numberFormatter = NumberFormatter()
numberFormatter.locale = Locale(identifier: "en_US_POSIX")
return numberFormatter.number(from: self)?.floatValue
}
func toBool() -> Bool? {
let numberFormatter = NumberFormatter()
numberFormatter.locale = Locale(identifier: "en_US_POSIX")
return numberFormatter.number(from: self)?.boolValue
}
}
Upvotes: -2
Reputation: 285039
The failure reason is that the JSON returns String
values, not numbers.
If the returned JSON data contains only these two key value pairs declare the type as [String:String]
that avoids the type casting.
In any case you have to put the code to update the variables into the "good" branch of the do - catch
expression.
struct Usage {
var totalData = 0.0
var remainingTotalData = 0.0
init(jsonData: NSData) { // Swift 3: Data
do {
let jsonDict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as! [String: String]
// Swift 3: let jsonDict = try NSJSONSerialization.jsonObject(with: jsonData) as! [String: String]
totalData = Double(jsonDict["totalfup"]!)
remainingTotalData = Double(jsonDict["totalrem"]!)
} catch {
print("Error occurred parsing data: \(error)")
}
}
}
Upvotes: 5
Reputation: 17534
why not use Swift's native types directly?
import Foundation
struct Usage {
var totalData: Double = 0
var remainingTotalData: Double = 0
init(jsonData: NSData) {
do {
if let jsonDict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as? [String:Double] {
totalData = jsonDict["totalfup"] ?? 0
remainingTotalData = jsonDict["totalrem"] ?? 0
}
} catch {
print("Error occurred parsing data: \(error)")
}
}
}
if let data = "{\"totalfup\":96.340899,\"totalrem\":3548710948}".dataUsingEncoding(NSUTF8StringEncoding) {
let usage = Usage(jsonData: data)
dump(usage)
/*
▿ Usage
- totalData: 96.340899
- remainingTotalData: 3548710948.0
*/
}
Upvotes: -1
Reputation: 237
I think this could help you
totalData = Double(jsonDict["totalfup"] as! String)!
Upvotes: 21