Swift: Testing Against Optional Value in Switch Case

Swift: testing against optional value in switch case

Optional is just a enum like this:

enum Optional<T> : Reflectable, NilLiteralConvertible {
case none
case some(T)

// ...
}

So you can match them as usual "Associated Values" matching patterns:

let someValue = 5
let someOptional: Int? = nil

switch someOptional {
case .some(someValue):
println("the value is \(someValue)")
case .some(let val):
println("the value is \(val)")
default:
println("nil")
}

If you want match from someValue, using guard expression:

switch someValue {
case let val where val == someOptional:
println(someValue)
default:
break
}

And for Swift > 2.0

switch someValue {
case let val where val == someOptional:
print("matched")
default:
print("didn't match; default")
}

How to type match and unwrap optional values in a switch statement (simultaneously)?

I don't think you will be able to cast and unwrap in a case, but with this extension at least your Loop A would be less verbose:

extension MyObjects {
subscript<T: MyObjects, V>(topicOrQuestion keypath: (T) -> V) -> V? {
get {
guard let root = self as? T else { return nil }
return keypath(root)
}
}
}

you can also write this loop:

for topicOrQuestion in topicOrQuestions {

if let topicName = topicOrQuestion[topicOrQuestion: \MyTopic.name] as? String {
print(topicName)
}
if let questionText = topicOrQuestion[topicOrQuestion: \MyQuestion.text] as? String ,
let questionTopic = topicOrQuestion[topicOrQuestion: \MyQuestion.topic] as? String {
print(questionText, questionTopic)
}
}
// questionA topicA
// topicA
// topicB

Switch testing a non optional against optional case values

Currently pattern matching optional values isn't supported in the Swift standard library. However, you could easily execute your logic using if/else if statements:

if nonOptionalView === optionalView1 {
// Do something
} else if nonOptionalView === optionalView2 {
// Do something else
}

If you'd really like to be able to pattern match optional values, you can use operator overloading. To overload the pattern matching operator ~=, place the following code in your project:

public func ~=<T : Equatable>(a: T?, b: T?) -> Bool {
return a == b
}

Now you'll be able to pattern match optional values just fine:

switch nonOptionalView {
case optionalView1:
// Do something
case optionalView2:
// Do something else
default:
// Handle default case
}

Match optional in switch statement

Swift 4.0

You can check with optional case as like below.
Hope this will help you.

let test = "some string"

let x: String? = nil
let y: String? = "some string"
let z: String? = "another string"

switch test {
case let val where val == x:
print(x)
case let val where val == y:
print(y)
case let val where val == z:
print(z)
default:
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)")

Why switch does not accept case nil in optional?

I think this is just an incorrect diagnostic. You can add a default case, and this code will work, but I'm actually surprised that works.

You see, the value you're switching on is a Bool?, but 2 of the values you're comparing it against are true and false. The typical approach for this is to use pattern matching:

func test(value: Bool?) {
switch value {
case .some(true): print("true")
case .some(false): print("false")
case .none: print("nil")
}
}

This works correctly, but it's a bit wordy. Luckily, Swift has a syntactic sugar for pattern matching against optionals, ? and nil:

func test(value: Bool?) {
switch value {
case true?: print("true")
case false?: print("false")
case nil: print("nil")
}
}

Both of these snippets compile and work fine. It does open a new question as to why your original cases worked, if only you added the default.

I suggest you post about this on Swift forums, to get more input from the compiler nerds :)

Swift Switch statement fails to match strings: What could cause this?

I suspect the problem is actually in your getWatchModel method, specifically in the line:

var machine = CChar()

This allocates a single byte. You need to allocate size bytes.
Here's one way of doing it:

    func getWatchModel() -> String? {
var size: size_t = 0
sysctlbyname("hw.machine", nil, &size, nil, 0)
var machine = [CChar](repeating: 0, count: size)
sysctlbyname("hw.machine", &machine, &size, nil, 0)
return String(cString: machine, encoding: .utf8)
}

I suspect this lead to the String containing garbage. I'm actually surprised you did not see any crashes.

I have to unwrap optional twice in switch statement inside didSet

It is inconsistent, but the reason is a bit complex.

In order for a type to be used in a switch statement, it needs to conform to Equatable.

Look at this example using a new struct type:

struct MyType: Equatable {
let x: Int

static func ==(lhs: MyType, rhs: MyType) -> Bool {
return lhs.x == rhs.x
}
}

var mytypeTest: MyType! {
didSet {
switch mytypeTest {
case MyType(x: 1): print("one")
case MyType(x: 2): print("two")
default: print("something else")
}
}
}

mytypeTest = MyType(x: 1)

This works, but if you remove : Equatable from MyType you will get the error Operator function '~=` requires that 'MyType' conform to 'Equatable'.

So there's the first hint. switch uses the ~= operator for comparisons and the type must be Equatable.

So what happens if we try to compare two tuples using '~=':

if (1, 3) ~= (1, 3) {
print("same")
}

This gives the error: Type '(Int, Int)' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols.

So, this would imply that tuples can't be used in a switch and we know that isn't true.

Well, tuples have a special place in a switch, and they're used in pattern matching for deconstructing the tuple. For example:

let a = (1, 2)

switch a {
case let (x, y):
print("the tuple values are \(x) and \(y)")
}

This prints the tuple values are 1 and 2.

So, tuples are used in switch for matching and deconstructing using pattern matching. So you can use a tuple in a switch even though it doesn't conform to Equatable because it has this special use.

The problem with your example is that the pattern doesn't match the type you are switching on. The type of your value is (Int, Int)? and the pattern is (Int, Int).

So, how can you fix this without force unwrapping the tuple value? Change your pattern to match by adding ? to the pattern:

var toupleTest: (one: Int, two: Int)! {
didSet {
switch toupleTest {
case (1, 1)?: print("one, one")
case (2, 2)?: print("two, two")
default: print("something else")
}
}
}

Note: Adding ? works for your Int example as well:

var test: Int! {
didSet {
switch test {
case 1?: print("one")
case 2?: print("two")
default: print("something else")
}
}
}

but it isn't necessary because Int is Equatable and Swift knows how to compare an Int? to an Int for equality.



Related Topics



Leave a reply



Submit