Malik
Malik

Reputation: 3802

Unable to bridge NSNumber to Float Swift 3.3

After an Xcode update I encountered a weird problem. In my application, when I get a response from an API, I parse it manually and map it to my model. There are a lot of places in my code where I cast a JSON value to Float with null coalescing like below:

randomVariable = jsonDict["jsonKey"] as? Float ?? 0

It used to work fine but after the update, it would always default to 0. In order to determine the cause, I tried force casting it to Float like below

randomVariable = jsonDict["jsonKey"] as! Float

This resulted in the following error

Unable to bridge NSNumber to Float

Upon further investigation, I found out that Xcode is using Swift 3.3 (previously, it was using Swift 3.2). The solutions I found on StackOverflow revolve around casting it to NSNumber instead of Float. As I mentioned above, I have similar lines of code spread across my app. I was wondering if there is a way to fix this issue. Maybe using an extension of some sort?

Upvotes: 6

Views: 6218

Answers (3)

Krešimir Prcela
Krešimir Prcela

Reputation: 4281

The problem is in 32 bit architecture and updated NSNumber class in swift 4.1.

For example when I write number 1.1 into dictionary it will be stored as 64bit NSNumber with most accurate representation = 1.10000000000000008881784197001...

So, when you read this number as! Float it will fail because 32 bit precision is not good enough to get this most accurate representation.

The most accurate representation in 32 bit = 1.10000002384185791015625

64 bit presentation of float number

So, you need to get truncated result that is implemented inside NSNumber.floatValue() function.

randomVariable = (jsonDict["jsonKey"] as! NSNumber).floatValue

Upvotes: 5

OOPer
OOPer

Reputation: 47896

As you have found, you may need to cast it first to NSNumber.

Something like this:

randomVariable = (jsonDict["jsonKey"] as? NSNumber)?.floatValue ?? 0

Maybe regex replacement would help you update your code.

Pattern: jsonDict\[([^\]]*)\] as\? Float

Replace with: (jsonDict[$1] as? NSNumber)?.floatValue

Upvotes: 17

Glenn Posadas
Glenn Posadas

Reputation: 13290

Before I discovered SwiftyJSON, I used to do this, parsing the data manually. You may want to check their source code under the hood to see how it parses your data.

// Sample: rate is a Float

// Number
if let number = dictionary[SerializationKeys.rate] as? NSNumber {
    print("IS A NUMBER")
    rate = number.floatValue
}

// String

if let string = dictionary[SerializationKeys.rate] as? String {
    print("IS A STRING")
    let decimalNumber = NSDecimalNumber(string: string)
    rate = decimalNumber.floatValue
}

And since I've mentioned the SwiftyJSON, I'd like to recommend it to avoid such headache. Leave the parsing of json to that guy. https://github.com/SwiftyJSON/SwiftyJSON

I hope it helps.

Upvotes: 0

Related Questions