Dynamic casting Any to a runtime optional type

Hi guys in Swift community, this is my first topic in this community, I want to discuss the way to unwrap an Any object to a runtime optional type.

class Test {
    var biz: Int64? = 0xFF0000
}

let a: Int8? = nil
let b: Test? = Test()

let aa = a as Any
let bb = b as Any

// questions start from here, now we have 2 `Any` objs, lets figure out whether it is nil.

for p in [aa, bb] {
    print(p == nil ? "NIL" : "Non Nil")
}

I see the compiler warns me that this comparison always fails. And after some investigation, found that Any is a wrapper, so the wrapper is not nil of course.

Code I want to achieve:

class Test {
    var biz: Int64? = 0xFF0000
}

let a: Int8? = nil
let b: Test? = Test()

let aa = a as Any
let bb = b as Any

let values = [aa, bb]
let types = [Int8.self, Test.self]
for i in 0...2 {
    print((values[i] as? types[i]) == nil ? "NIL" : "Non Nil") // as? requires a compile time type, not runtime.
}

The reason I want to distinguish Any from nil withou compile time type is that I want to use this API.

AnyReturn swift_reflectionMirror_subscript(OpaqueValue *value, const Metadata *type,
                                           intptr_t index,
                                           const char **outName,
                                           void (**outFreeFunc)(const char *),
                                           const Metadata *T)

Value was wrapped in Any and I'm trying to reverse the way used by this function to wrap value to get the concrete value is nil or not. To be more specific, in EnumImpl's AnyReturn subscript(intptr_t i, const char **outName, void (**outFreeFunc)(const char *)) function, raw value was wrapped to Any, I want to unwrap Any to raw value conversely.

The question is, is there any better way to unwrap an Any to an Optional in runtime without compile-time type?

Example application scene:

struct Body {
    let weight: Int?
    let height: Int?
}

class Person {
    let name: String?
    let body: Body? = Body()
}

@_silgen_name("swift_reflectionMirror_subscript")
internal func _getChild<T>(
  of: T,
  type: Any.Type,
  index: Int,
  outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
  outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
) -> Any


func getSubStructure(obj: Any) -> [Any] {
    var res = [Any]()
    for i in 0...1  {
        let child = _getChild(of: obj,
                    type: type(of: obj),
                    index: i,
                    outName: name,//ignore decl
                    outFreeFunc: func//ignore decl
                    )
        res.append(child)
    }
    return res
}


let subStructure = getSubStructure(Person())

now, how to determine subStructure[0] is nil, and subStructure[1] isn't nil with a generic function? I have to use this function to handle all types that may appear in my app, I can't just write subStructure[0] as? XX, I want a runtime type coercion here.

Thanks, best wishes,
Yifan

You can cast to Any? when declaring aa and bb:

class Test {
  var biz: Int64? = 0xFF0000
}

let a: Int8? = nil
let b: Test? = Test()

let aa = a as Any?
let bb = b as Any?

for p in [aa, bb] {
  print(p == nil ? "NIL" : "Non Nil")
}

Sorry, the question begins at an Any object, not an Any?,

let aaa = aa as Any?

just wrapping it again.

Update code with an annotation. Thanks for your watching!

Could you provide an actual example in Swift of what you're trying to achieve? From what you wrote above, it seems you have a RawRepresentable enum, an instance of it casted to Any and you want to be able to dynamically get the RawValue associated type of the underlying type? Maybe your use of "raw value" means something else?

Thanks for your replying, I'm implementing a tool which queries object's substructure, as variable declared with optional type is an enum, _getChild API give me an Any which wraps the concrete value like struct, class object, enum or tuple, if it is an optional, I can't figure out how to unwrap it as I can't have a compile time type. It means, I have an Any object, but I don't know it is aa or bb, as they are not the same type, I can't write let concreteObj = obj as? XX, so I can't unwrap this Any to an Optional object.

Update an example scene in the description, you can check, thanks very much

Is wickwirew/Runtime sufficient for what you're trying to achieve?

Have seen this, this one helps to analyze Type, but it can't help to unwrap Any to optional. I think the real problem is that as? operator doesn't support runtime types. It only accepts compile-time types no matter a concrete one or a generic one.

We can use this answer from the thread Challenge: Flattening nested optionals:

protocol Flattenable {
  func flattened() -> Any?
}

extension Optional: Flattenable {
  func flattened() -> Any? {
    switch self {
    case .some(let x as Flattenable): return x.flattened()
    case .some(let x): return x
    case .none: return nil
    }
  }
}

To write a function like this:

func flatten(_ a: Any) -> Any? {
  return (a as? Flattenable)?.flattened()
}

And then you can use:

for p in [aa, bb] {
    print(flatten(p) == nil ? "NIL" : "Non Nil")
}

• • •

If you just want the boolean result, you could do:

func isActuallyNil(_ a: Any) -> Bool {
  return (a as? Flattenable)?.flattened() == nil
}

Or if you want to extract the innermost wrapped type into an Any.Type, that can be done using the solutions from Challenge: Finding base type of nested optionals.

1 Like

Wow thank you, Nevin! This solution looks so great!!!!

You’re welcome.

Note however, that the above solution fully flattens the input, to test whether it holds a value at the most deeply-nested level of optionality.

If instead you only care about the first level of optionality, and don’t want to recursively unwrap, then the solution is even easier:

protocol OptionalProtocol {
  var isNil: Bool { get }
}

extension Optional: OptionalProtocol {
  var isNil: Bool { self == nil }
}

func isNil(_ a: Any) -> Bool {
  return (a as? OptionalProtocol)?.isNil ?? false
}

Got it, case .some(let x as Flattenable): return x.flattened() is designed for nested optional value. I think I need the nested one. So this solution uses a protocol to overcome the problem that as? operator requires a compile-time type, it's really genius, thanks for your sharing, I really learn a lot from this solution, have a nice day :P

To be clear, the difference is in how you want values like this to behave:

let x = Int??.some(Int?.none)
1 Like