aryaxt aryaxt - 1 month ago 10
Swift Question

Given a Swift `Any` type can I determine if it's an `Optional`?

Given a value of type

Any
is it possible to check and see if it's an Optional or not?
This code doesn't work because instead of checking to see if it's optional or not it's trying to cast it, and it passes

let a: Any = "5"

switch a {
case let optional as Optional<Any>:
if case .some(let value) = optional {
print("wrapped value of `\(a)` is `\(value)`")
}

default:
print("\(a) is not an optional")
}





Base on @dfri's solution

func isOptional<T>(_ input: T) -> Bool {
let mirror = Mirror(reflecting: input)
let style = mirror.displayStyle

switch style {
case .some(.optional):
return true
default:
return false
}
}

Answer

You can use runtime introspection using Mirror:

let foo: String? = "foo"
let bar: String = "bar"
var a: Any = foo

// if wrapping an optional, the reflection of the value has
// a displaystyle "optional"
if let displayStyle = Mirror.init(reflecting: a).displayStyle {
    print(displayStyle) // optional
}

// for a non-optional fundamental native type: no displaystyle
a = bar
if let displayStyle = Mirror.init(reflecting: a).displayStyle {
    print(displayStyle)
} // prints nothing

Optional/non-optional example where the underlying type is user-defined (non native):

struct Foo {}
let foo: Foo? = Foo()
let bar: Foo = Foo()
var a: Any = foo

// if wrapping an optional, the reflection of the value has
// a displaystyle "optional"
if let displayStyle = Mirror(reflecting: a).displayStyle {
    print(displayStyle) // optional
}

// for a non-optional non-fundamental type:
a = bar
if let displayStyle = Mirror(reflecting: a).displayStyle {
    print(displayStyle) // struct
}

If you don't want need to use the binded displayStyle variable (e.g. for printing) but simply want check whether the wrapped value is any kind of optional, you can add a boolean clause to the if statement that holds the optional binding of the displayStyle case,

if let displayStyle = Mirror(reflecting: a).displayStyle,
    displayStyle == .optional {
    // is an optional ...
}

... or remove the binding entirely in favour of a single conditional expression using the nil coalescing operator (??)

if Mirror(reflecting: a).displayStyle ?? .class == .optional {
    // is an optional
}

Note however that for all the methods above, this simply tells you as dev whether the type wrapped by the Any instance is optional or not: Swifts typing system still knows nothing of the sort.