Given a Swift 'Any' Type How to Determine If It's an 'Optional'

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

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.

Determine if Any.Type is Optional

Assuming that what you are trying to do is something like this:

let anyType: Any.Type = Optional<String>.self
anyType is Optional<Any>.Type // false

Sadly swift currently (as of Swift 2) does not support covariance nor contravariance and type checks directly against Optional.Type cannot be done:

// Argument for generic parameter 'Wrapped' could not be inferred
anyType is Optional.Type // Causes error

An alternative is to make Optional extend an specific protocol, and check for that type:

protocol OptionalProtocol {}

extension Optional : OptionalProtocol {}

let anyType: Any.Type = Optional<String>.self
anyType is OptionalProtocol.Type // true

How can you check if a type is Optional in Swift?

This is a hacky but working solution:

func isOptional(_ type: Any.Type) -> Bool {
let typeName = String(describing: type)
return typeName.hasPrefix("Optional<")
}

Test:

let t1 = Int?.self
let t2 = Bool.self

print(isOptional(t1))
// true

print(isOptional(t2))
// false

Swift: Testing optionals for nil

In Xcode Beta 5, they no longer let you do:

var xyz : NSString?

if xyz {
// Do something using `xyz`.
}

This produces an error:

does not conform to protocol 'BooleanType.Protocol'

You have to use one of these forms:

if xyz != nil {
// Do something using `xyz`.
}

if let xy = xyz {
// Do something using `xy`.
}

Checking optional isEqualToString

Optional has an == operator defined for it in the Swift standard library:

func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool

That means if the optional contains a value that’s equatable, you can compare two optionals. If the two optionals are both nil they’re equal, and if the two optionals wrap values that are equal, then they’re equal.

So if you don’t care about whether person.name is nil or not, just whether it contains a value of "John", you can just write if person.name == "John".

How does that work, when "John" is a String not a String??* Because Swift will implicitly convert any type to an optional wrapping that type if that’s what an argument requires. So because the == function for comparing optionals requires a String? argument, “John” will be implicitly converted to {Some "John"}, allowing the == operator between two optionals to be used.

* well actually "John” isn't a String either, it’s a string literal that is getting converted into a String



Related Topics



Leave a reply



Submit