6

I'm trying to get an unwrapped type from an optional type in runtime. The following code would print the type of a as Optional<String>.

class MySubClass: MyClass {

    var a: String? = nil
}

var a = MySubClass()

let mirror = Mirror(reflecting: a)
for child in mirror.children {
    print(child.value.dynamicType)
}

Now I want to unwrap the type and get String, what should I do to make this happen in runtime?

Zhu Shengqi
  • 3,632
  • 3
  • 24
  • 29
  • To what end? Is it just the log you don't like or you have a real problem? – Wain Dec 06 '15 at 10:08
  • In my real case it's like this: var a: Model? = nil, and I still want the unwrapped type – Zhu Shengqi Dec 06 '15 at 10:12
  • 2
    Why do you want the unwrapped type would you like to use that? or would you like to write that to the console or somewhere else? – Gyuri T Dec 06 '15 at 10:17
  • @GyorgyTrum I want to transfer json into model objects, so I need to know the unwrapped type of every property in an model object. – Zhu Shengqi Dec 06 '15 at 10:43
  • @ZhuShengqi: I still don't understand the purpose. You have a JSON but at compile time you don't know which model class should contain that data? So you are calling a web service but you don't know what data it will return? – Luca Angeletti Dec 06 '15 at 11:24
  • @appzYourLife I'm developing a framework, not an app. I can't know the exact json model. – Zhu Shengqi Dec 06 '15 at 11:28
  • Possible duplicate of [Check if variable is an Optional, and what type it wraps](https://stackoverflow.com/questions/32645612/check-if-variable-is-an-optional-and-what-type-it-wraps) – Sam Hanley Aug 14 '18 at 20:27

4 Answers4

9

Assuming you have an optional

let someVar: String?

then print(type(of: someVar)) will print

Optional<String>

but if you add the following extension to Optional

protocol OptionalProtocol {
 func wrappedType() -> Any.Type
}

extension Optional: OptionalProtocol {
 func wrappedType() -> Any.Type {
   return Wrapped.self
 }
}

then print(someVar.wrappedType()) will print

String

No reflection whatsoever


Summary

As long as the optional is not referenced by Any or AnyObject the code will work fine.

For Any you will have to cast it to OptionalProtocol first. Running

let someVar: String?
let anyVar = someVar as Any

if let op = anyVar as? OptionalProtocol {
    print(op.wrappedType())
}

will print

String


As for AnyObject, strangely enough (at least for me), it doesn't cast to OptionalProtocol.


The original StackOverflow answer can be found here

donjuedo
  • 2,475
  • 18
  • 28
tonymontana
  • 5,728
  • 4
  • 34
  • 53
4

I played with your idea a little bit, but I think there isn't a real way to do that, since you can't get the type of the associated value of an enumeration, yet. Hence Optionals are basically Enumerations, we have a problem here.

My idea would be to test for all possible value types in your model objects could be or hold. Like:

let myModelObject:Any? = someWayToGetTheData()
if let aString = myModelObject as? String {
    // do everything you need to store a string
} 
else if let anInteger = myModelObject as? Int {
    // do everything you need to store an integer
} 

// and so on ...

Since your json and your model must have a predefined number of supported conversions that is a possible way, and as far as I understand your original problem, it's basically as useful as testing for the dynamic associated value type of an Optional Enumeration, which will lead into a chain of if-else statements as well.

kiecodes
  • 1,642
  • 14
  • 28
  • 1
    Yep, ur right. I just find that the reflection API of Swift 2.1 is totally a shit. I have to make every property of model object non-optional, which I think is really an ugly design (optional is a good tool to check if there is a value). Hope Swift 3.0 change this shit. – Zhu Shengqi Dec 06 '15 at 11:25
  • @ZhuShengqi: If my answer helps you, it would be great if you could mark it as the the right answer. Thank you. – kiecodes Dec 07 '15 at 10:18
0

You can either unwrap the optional explicitly with a bang (!) or with an if let.

For example:

var foo: String? = nil
if foo == nil {
    print("foo is nil")
    foo = "bar"
}

let fooBang = foo!
print("fooBang: \(fooBang)")

if let ifLetFoo = foo {
    print("ifLetFoo: \(ifLetFoo)")
}

This will print:

foo is nil
fooBang: bar
ifLetFoo: bar

In your context, I think print(child.value.dynamicType!) might be what you're looking for.

Lander
  • 3,369
  • 2
  • 37
  • 53
0

If you cast the value to the non-optional String, it will print you the unwrapped type.

let mirror = Mirror(reflecting: a)
for child in mirror.children {
    print(String(child.value).dynamicType) //String
}

Or you can play with the String and get the type from the Optional type.

class MySubClass: MyClass {

var a: Int? = nil
}

var a = MySubClass()

let mirror = Mirror(reflecting: a)
for child in mirror.children {
     let typeArr = String(child.value.dynamicType).characters.split{$0 == "<"}.map(String.init)
     let typeArr2 = typeArr[1].characters.split{$0 == ">"}.map(String.init)

      print(typeArr2[0]) // print: Int
 }

Sorry this is lame but you can do something like this.

Gyuri T
  • 290
  • 3
  • 13