Swift switch statement on a tuple of optional booleans

The accepted answer is out of date. So I rewrote it in Swift 5.

    var dict = Dictionary<String,Bool>()
    dict["a"] = true
    dict["c"] = false

    func matchOneOrTheOtherWithOptionals(_ a: Bool?, _ b: Bool?) -> String {
        switch (a, b) {
        case (.some(true), .none), (.some(true), .some(false)):
            return "a was true, but b was None or false"
        case (.none, .some(true)), (.some(false), .some(true)):
            return "a was None or false and b was true"
        default:
            return "They both had a value, or they were both missing a value"
        }
    }

    let xs: [Bool?] = [true, false, .none]
    for a in xs {
        for b in xs {
            print("a = \(String(describing: a)), b=\(String(describing: b))")
            print(matchOneOrTheOtherWithOptionals(a,b))
        }
    }

There are a bunch of ways to do this, but to fix the syntax of what you are trying to do literally, you need to explicitly unbox the Optional, either by matching against .Some(false), or unwrapping it with ! (which I think is kind of weird, as you'll see)

var dict = Dictionary<String,Bool>()
dict["a"] = true
dict["c"] = false

func matchOneOrTheOtherWithOptionals(a: Bool?, b: Bool?) -> String {
    switch (a, b) {
    case (.Some(true), let b) where b == .None || !b!: // gross
        return "a was true, but b was None or false"
    case (let a, .Some(true)) where a == .None || a == .Some(false):
        return "a was None or false and b was true"
    default:
        return "They both had a value, or they were both missing a value"
    }
}

matchOneOrTheOtherWithOptionals(true, .None) // "a was true, but b was None or false"
matchOneOrTheOtherWithOptionals(true, false) // "a was true, but b was None or false"
matchOneOrTheOtherWithOptionals(.None, true) // "a was None or false and b was true"
matchOneOrTheOtherWithOptionals(false, true) // "a was None or false and b was true"

matchOneOrTheOtherWithOptionals(false, false) // "They both had a value, or they were both missing a value"
matchOneOrTheOtherWithOptionals(true, true) // "They both had a value, or they were both missing a value"
matchOneOrTheOtherWithOptionals(.None, .None) // "They both had a value, or they were both missing a value"

You could also try the following:

func noneToFalse(bool: Bool?) -> Bool {
    if let b = bool {
        return b
    } else {
        return false
    }
}

func matchOneOrTheOther(a: Bool, b: Bool) -> String {
    switch (a, b) {
    case (true, false):
        return "a is true, b was false or None"
    case (false, true):
        return "a was false/None, b was true"
    default:
        return "both were true, or both were false/None"
    }
}

matchOneOrTheOther(noneToFalse(dict["a"]), noneToFalse(dict["b"]))

Here's a gist of the Playground I used while writing this answer: https://gist.github.com/bgrace/b8928792760159ca58a1


Simplify!

var dict = Dictionary<String,String>()

dict["a"] = "the letter a"

switch (dict["a"],dict["b"]) {
  case (.None, let b):
    println("false/nil, true \(b)")
  case (let a, .None):
    println("true, false/nil \(a)")
  default:
    println("don't know") 
}