How can you check if a type is Optional in Swift?
Say I have a variable of type PartialKeyPath where:
struct Foo {
let bar: String
let baz: String?
}
typealias Property<Root> = (key: PartialKeyPath<Root>, value: Any?)
typealias Properties<Root> = [Property<Root>]
Now say I iterate thru an instance of Properties:
properties.forEach { prop in
let valueType1 = type(of: prop.key).valueType
let valueType2 = type(of: value)
...
How can I check here whether valueType1 is Optional<valueType2>
, or whether it is Optional of any other flavor for that matter?
So far the only way I’ve found is really ugly...
If you defined a variable as optional, then to get the value from this variable, you will have to unwrap it. This just means putting an exclamation mark at the end of the variable. Optional("Hello, Swift 4!")
An optional in Swift is basically a constant or variable that can hold a value OR no value. The value can or cannot be nil. It is denoted by appending a “?” after the type declaration.
Optionals are in the core of Swift and exist since the first version of Swift. An optional value allows us to write clean code with at the same time taking care of possible nil values. If you're new to Swift you might need to get used to the syntax of adding a question mark to properties.
Any data type can be optional in Swift: An integer might be 0, -1, 500, or any other range of numbers. An optional integer might be all the regular integer values, but also might be nil – it might not exist.
Using a similar approach to Optional field type doesn't conform protocol in Swift 3, you could define a 'dummy protocol' for Optional
and use this to get the wrapped metatype:
protocol OptionalProtocol {
// the metatype value for the wrapped type.
static var wrappedType: Any.Type { get }
}
extension Optional : OptionalProtocol {
static var wrappedType: Any.Type { return Wrapped.self }
}
If you just want to know a type is an optional:
func isOptionalType(_ type: Any.Type) -> Bool {
return type is OptionalProtocol.Type
}
print(isOptionalType(String.self)) // false
print(isOptionalType(String?.self)) // true
If you want to check if one metatype is the 'optional version' of another metatype:
struct Foo {
let bar: String
let baz: String?
}
struct Property<Root> {
var key: PartialKeyPath<Root>
var value: Any
}
let properties = [Property(key: \Foo.baz, value: "hello")]
/// Attempt to get the `Wrapped` metatype from a metatype of an
/// `Optional<Wrapped>`. If not an `Optional`, will return `nil`.
func wrappedTypeFromOptionalType(_ type: Any.Type) -> Any.Type? {
return (type as? OptionalProtocol.Type)?.wrappedType
}
for property in properties {
let valueType1 = type(of: property.key).valueType
let valueType2 = type(of: property.value)
if wrappedTypeFromOptionalType(valueType1) == valueType2 {
print("\(valueType1) == Optional<\(valueType2)>")
}
}
// Optional<String> == Optional<String>
However there's almost certainly a better way to do whatever you're trying to do here with the key paths.
could you use a mirror reflecting Any and check displayStyle is optional?.
func isOptional(any:Any) -> Bool {
let mirror = Mirror(reflecting: any)
if mirror.displayStyle == .Optional {
return true
} else {
return false
}
}
More on mirror display style: https://developer.apple.com/documentation/swift/mirror.displaystyle
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With