Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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...

like image 994
CommaToast Avatar asked Dec 07 '17 07:12

CommaToast


People also ask

How do you indicate that this variable is optional Swift?

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!")

How the optional type is denoted in Swift?

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.

What do optional types in Swift handle?

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.

Can any be optional Swift?

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.


3 Answers

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.

like image 88
Hamish Avatar answered Oct 12 '22 14:10

Hamish


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

like image 3
RLoniello Avatar answered Oct 12 '22 12:10

RLoniello


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
like image 2
kelin Avatar answered Oct 12 '22 14:10

kelin