Swift Optional Patterns

How does the '?' unwrap an optional in a case let declaration?

This is syntactic sugar for option patterns. The docs on option pattern says:

An optional pattern matches values wrapped in a some(Wrapped) case of an Optional<Wrapped> enumeration. Optional patterns consist of an identifier pattern followed immediately by a question mark and appear in the same places as enumeration case patterns.

Thus, your code is the same as:

var x: Int? = 42

if case .some(let a) = x {
print(a)
}

It's not typical for simple if statements as you can just do this instead:

if let a = x {
print(a)
}

But consider an enum wrapped in an optional:

enum Foo {
case bar
case baz
}

let y: Foo? = .bar

switch y {
case .none: break
case .some(.bar): break
case .some(.baz): break
}

This switch can be written in a more succinct way using some sugar:

switch y {
case nil: break
case .bar?: break
case .baz?: break
}

How do I unwrap an Optional when pattern matching tuples in Swift?

You can use the x? pattern:

case (1990...2015, let unwrappedUrl?):
print("Current year is \(year), go to: \(unwrappedUrl)")

x? is just a shortcut for .some(x), so this is equivalent to

case (1990...2015, let .some(unwrappedUrl)):
print("Current year is \(year), go to: \(unwrappedUrl)")

What are the advantages/use cases of optional patterns introduced in swift 2?

I believe you're conflating two different concepts. Admittedly, the syntax isn't immediately intuitive, but I hope their uses are clarified below.
(I recommend reading the page about Patterns in The Swift Programming Language.)

case conditions

The "case condition" refers to the ability to write:

  • if case «pattern» = «expr» { ... }
  • while case «pattern» = «expr» { ... }
  • for case «pattern» in «expr» { ... }

These are particularly useful because they let you extract enum values without using switch.

Your example, if case let x? = someOptional ..., is a valid example of this, however I believe it's most useful for enums besides Optional.

enum MyEnum {
case A
case B(Int)
case C(String)
}

func extractStringsFrom(values: [MyEnum]) -> String {
var result = ""

// Without case conditions, we have to use a switch
for value in values {
switch value {
case let .C(str):
result += str
default:
break
}
}

// With a case condition, it's much simpler:
for case let .C(str) in values {
result += str
}

return result
}

You can actually use case conditions with pretty much any pattern that you might normally use in a switch. It can be weird sometimes:

  • if case let str as String = value { ... } (equivalent to if let str = value as? String)
  • if case is String = value { ... } (equivalent to if value is String)
  • if case 1...3 = value { ... } (equivalent to if (1...3).contains(value) or if 1...3 ~= value)

The optional pattern, a.k.a. let x?

The optional pattern, on the other hand, is a pattern that lets you unwrap optionals in contexts besides a simple if let. It's particularly useful when used in a switch (similar to your username/password example):

func doSomething(value: Int?) {
switch value {
//case 2: // Not allowed
case 2?:
print("found two")

case nil:
print("found nil")

case let x:
print("found a different number: \(x)")
}
}

How to unwrapping an optional while pattern matching in Swift?

You could use a where clause to check if alertView is equal to b.

case (_, 1) where alertView == b:

To ensure alertView unwrapped in not nil, just replace the underscore with .some.

case (.Some, 1) where alertView == b:

Or similarly you could let the unwrapped alertView, but this is essentially the same as the above.

case (let .Some(_alertView), 1) where _alertView == b:

swift if case and optional binding

The first three code snippets you showed are the same. Out of the three, the first (optional binding) is definitely the recommended way to bind an optional in an if statement, as recommended by the Swift Guide:

You use optional binding to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable.

The other two ways, I feel, are a bit more roundabout. Why would you write x + 2 - 1 when you can just write x + 1?

The enum case pattern works because Optional being implemented as an enum.

According to the Swift Reference, the optional pattern is just a syntactic sugar for the enum case pattern for the special case of Optional:

An optional pattern matches values wrapped in a some(Wrapped) case of an Optional<Wrapped> enumeration.

OTOH, there are times when you can't use optional binding, such as in a for loop or a switch statement. These are the times when optional patterns come in handy. An example from the Swift Reference:

let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
// Match only non-nil values.
for case let number? in arrayOfOptionalInts {
print("Found a \(number)")
}

If you have a look at the syntax of the control flow statements and loops, you'll notice that patterns can be used in a lot more places than optional binding. Enum case patterns and optional patterns are patterns, but optional binding isn't a pattern.

The fourth code snippet prints nothing, because Optional.some(42) does not match the pattern x?. Recall what an optional pattern matches, it matches .some(x). x is nil (you haven't initialised it to anything), so you are matching .some(42) to .some(.none). Clearly they don't match, so the if statement does not run. Even if it did run, printing x would give you nil, as this is pattern matching, not variable assignment. If you want to do something similar to assigning variables...

You'd need to use a value binding pattern like let x? in cases like this. The optional pattern expands to let .some(x), and,

Identifiers patterns within a value-binding pattern bind new named variables or constants to their matching values.

What does ?? mean on a variable declaration in Swift case let pattern matching?

In the context of pattern matching, x? is the “optional pattern” and equivalent to .some(x). Consequently, case x?? is a “double optional pattern” and equivalent to .some(.some(x)).

It is used here because UIApplication.shared.delegate?.window evaluates to a “double optional” UIWindow??, compare Why is main window of type double optional?.

Therefore

if case let presentationAnchor?? = UIApplication.shared.delegate?.window

matches the case that UIApplication.shared.delegate is not nil and the delegate implements the (optional) window property. In that case presentationAnchor is bound to the “doubly unwrapped” UIWindow.

See also Optional Pattern in the Swift reference:

An optional pattern matches values wrapped in a some(Wrapped) case of an Optional<Wrapped> enumeration. Optional patterns consist of an identifier pattern followed immediately by a question mark and appear in the same places as enumeration case patterns.

Swift Pattern Matching - Switch, downcasting, and optional binding in a single statement

I'm not sure you can eliminate the duplication with a switch and optionally bind email, but you can with an if:

if case .success(let value as [String : Any]) = result, let email = value["email"] as? String {
print("email: \(email)")
} else {
self.present(error: Error(.facebookOther))
}

The best you can do with a switch is to avoid the duplication by adding a where clause, but you miss out on the optional binding of email:

switch result {
case .success (let value as [ String : Any ]) where value["email"] is String:
let email = value["email"] as! String
print("email: \(email)")
default:
self.present(error: Error(.facebookOther))
}

Is it possible to pattern match with optionals in Swift?

A little bit out of context, but: Playground doesn't print the println() statements in the right column.
You can write again the variable that you want to read:

...
case .Some(let numeral):
println("Caught a \(numeral)")
numeral
...

In this case you'll see {Some 2}.

Or you can open the Assistant Editor (View -> Assistant Editor -> Show Assistant Editor) and read the Console output to read the println() evaluated.

EDIT after Xcode 6 beta-5

With Xcode 6 beta-5, you can finally println(), you'll see the text in the right column.



Related Topics



Leave a reply



Submit