Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift convert object that is NSNumber to Double

Tags:

ios

swift

I have this code in Swift and it works, but I would think there is a better way to get my object from NSNumber and convert it to a Double:

var rating: NSNumber var ratingDouble: Double  rating = self.prodResult?.prodsInfo.prodList[indexPath.row].avgRating as NSNumber!!  ratingDouble = Double(rating.doubleValue) 
like image 688
Bryan Cimo Avatar asked Dec 03 '14 18:12

Bryan Cimo


1 Answers

Update

Swift's behavior here has changed quite a bit since 1.0. Not that it was that easy before, but Swift has made it harder to convert between number types because it wants you to be explicit about what to do with precision loss. Your new choices now look like this:

var rating: NSNumber var ratingDouble: Double  ratingDouble = rating as! Double // 1 ratingDouble = Double(exactly: rating)! // 2  ratingDouble = Double(truncating: rating) // 3 ratingDouble = rating.doubleValue // 4  if let x = rating as? Double { // 5     ratingDouble = x }  if let x = Double(exactly: rating) { // 6     ratingDouble = x } 
  1. This calls Double._forceBridgeFromObjectiveC which calls Double(exactly:) with Double, Int64, or UInt64 based on the stored type in rating. It will fail and crash the app if the number isn't exactly representable as a Double. E.g. UInt64.max has more digits than Double can store, so it'll crash.

  2. This is exactly the same as 1 except that it may also crash on NaN since that check isn't included.

  3. This function always returns a Double but will lose precision in cases where 1 and 2 would crash. This literally just calls doubleValue when passing in an NSNumber.

  4. Same as 3.

  5. This is like 1 except that instead of crashing the app, it'll return nil and the inside of the statement won't be evaluated.

  6. Same as 5, but like 2 will return nil if the value is NaN.

If you know your data source is dealing in doubles, 1-4 will probably all serve you about the same. 3 and 4 would be my first choices though.


Old Answer for Swift 1 and 2

There are several things you can do:

var rating: NSNumber var ratingDouble: Double  ratingDouble = rating as Double   // 1 ratingDouble = Double(rating)     // 2 ratingDouble = rating.doubleValue // 3 
  1. The first item takes advantage of Objective-Cbridging which allows AnyObject and NSNumber to be cast as Double|Float|Int|UInt|Bool.
  2. The second item presumably goes through a constructor with the signature init(_ number: NSNumber). I couldn't find it in the module or docs but passing AnyObject in generated an error that it cannot be implicitly downcast to NSNumber so it must be there and not just bridging.
  3. The third item doesn't employ language features in the same way. It just takes advantage of the fact that doubleValue returns a Double.

One benefit of 1 is that it also works for AnyObject so your code could be:

let ratingDouble = self.prodResult!.prodsInfo.prodList[indexPath.row].avgRating! as Double 

Note that I removed the ? from your function and moved the ! in. Whenever you use ! you are eschewing the safety of ? so there's no reason to do both together.

like image 65
Brian Nickel Avatar answered Sep 29 '22 12:09

Brian Nickel