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
How to Obtain the Selected Text from the Frontmost Application in MACos
Can't Programmatically Change Color Set in Storyboard as Color from Xcassets Catalog
How to Pass a Class Type as a Function Parameter
How to Print Out the Method Name and Line Number in Swift
Checking for Nil Value in Swift Dictionary Extension
Closure Use of Non-Escaping Parameter May Allow It to Escape
Swift Make Method Parameter Mutable
Arkit - How to Export Obj from Iphone/iPad with Lidar
Xcode 9 and Xcode 10 Giving Different Results, Even with Same Swift Version
Swift Setter Causing Exc_Bad_Access
Swiftui iOS 14 Widget Countdown
Simple Swift Fibonacci Program Crashing (Project Euler 2)
Alamofire Asynchronous Completionhandler For Json Request
Check If Key Exists in Dictionary of Type [Type:Type]
Replacing Calayer and Cabasicanimation with Skscene and Skactions
Swift 2: Expression Pattern of Type 'Bool' Cannot Match Values of Type 'Int'