Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert RealmOptional to Int or Float in Swift?

Tags:

ios

swift

realm

I want to convert a type defined as RealmOptional to either Int or Float, depending on the type assigned to the generic. However, when I tried distributing them using switch statement, it turned out that not RealmOptional<Int>() but RealmOptional<Float>() were classified to Int cases. For example,

switch value { // value is of type AnyObject?
    case is String:
        cell.valueLabel.text = value as? String
    case is Int:
        // RealmOptional<Float> are executed here
        let v = value as! Int
        cell.valueLabel.text = String(v) // Float is now treated as Int
    case is Double:
        print("double") // not printed at all 
        cell.valueLabel.text = String(value!)
    default:
        break
}

Why does RealmOptional<Float>() behave as Int here? And how can I set the text to the value correctly?

like image 229
Blaszard Avatar asked Dec 22 '15 07:12

Blaszard


1 Answers

Lets assume your RealmOptional<Float>() variable is named myFloat. Then, use the getter for the RealmOptional:s (see this git entry for Realm) underlying value, .value, rather then checking the RealmOptional itself:

var value = myFloat.value // value variable now of type Float

Below follows and explanation of why the AnyObject? switches doesn't behave as you expect:

From Apples Language Guide - Type Casting:

Type Casting for Any and AnyObject

Swift provides two special type aliases for working with non-specific types:

  • AnyObject can represent an instance of any class type.
  • Any can represent an instance of any type at all, including function types.

Hence, the AnyObject type can can hold instances of any class type, but the fundamental numeric types (Int, Double etc) in Swift are not of class type, but of structure type.

In the switch in your example, the AnyObject instance is not inferred but cast to the first possible successful downcast, which will be whatever case you put top-most that is of a numeric type. Hence, if you change the ordering of your case:s in the switch, the casting will change.

let value: AnyObject? = 30.0
// try change the order
switch value {
case is String: print("string");
case is Float: print("float"); // hits `Float` downcast first -> prints float
case is Int: print("int");
default: print("other")
}

Now, you could, however, cast your AnyObject to an NSNumber (class instance), and continue from there. From Working with Cocoa Data Types documentation:

Instances of the Swift numeric structure types, such as Int, UInt, Float, Double, and Bool, cannot be represented by the AnyObject type, because AnyObject only represents instances of a class type. However, when bridging to Foundation is enabled, Swift numeric values can be assigned to constants and variables of AnyObject type as bridged instances of the NSNumber class.

Note however that NSNumber is fundamentally different from the Swift numeric types (Int, Double etc) in that the former hold any kind of number, and allows us to cast this number to different type of numeric types. However, we cannot easily infer which type a specific NSNumber instance should be cast to, but we could implement our own (not so pretty) specification as how to infer different NSNumber to different swift fundamental numeric types.

However, before proceeding with NSNumber hacking:

From above, the core problem is that your value property is of type AnyObject?. Could you please post the code that lead to value being of type AnyObject?. Possibly the casting to Int, Float and so on if not necessary if using the getter of the RealmOptional (hence no need to casting RealmOptional<T>() to AnyObject??).

like image 111
dfrib Avatar answered Sep 25 '22 02:09

dfrib