Matching multiple enums with a single guard case? - swift

I want to match against multiple enums and have something like this:
guard case .north = $0, case .south = $0 else { return }
Is there a way to condense this to a single statement like this?
guard case (. north, . south) = $0 else { return }
The above does not compile, but was hoping I can do something like this. Is there an alternative?

You can put the desired cases into a literal array and use contains to test for a match:
guard [.north, .south].contains($0) else { return }

Related

finding a number in a range swift 3

I have a number that I get from JSON, this number represents an age. Users give me a range of two ages and may code is supposed to check if this number I'm getting from JSON is in the range.
here is my code and it gives me error
Type of Expression is ambiguous without more context?
let age = "40"
if Int(AgeFrom) ... Int(AgeTO) ~= Int(age) {
print("yes")
}
Update
if let value: AnyObject = response.result.value as AnyObject? {
var ages = String
let json = JSON(value)
for (key, subJson) in json {
ages.append(subJson["age"].string!)
}
guard let min = Int(self.DropDownFrom.selectedItem!) else { return }
guard let max = Int(self.DropDownTo.selectedItem!) else { return }
for fitage in ages {
switch ages
{
case (min...max):
print ("Age is in range")
default:
print ("Nope, not this time")
}
}
Still gives me an error.
You need to unwrap the optionals because the Int(:String) method might not have a valid answer.
Best way to do this is kind of thing is with guard
guard let min = Int(AgeFrom) else { return }
guard let max = Int(AgeTo) else { return }
And from there you can go with the simple if statement:
if (min <= age && age <= max)
{
print ("Age is in range")
}
or get really fancy and use the switch statement pattern matching syntax (which I much prefer)
switch age
{
case (min...max):
print ("Age is in range")
default:
print ("Nope, not this time")
}
if - simplicity and readability
It is a basic thing in programming, checking if an optional is between two other optional values with an if:
if Int(AgeFrom)! <= Int(age)! && Int(AgeTO)! >= Int(age)! {
print("It is in the range!")
}
switch - multiple cases handling
However, I recommend using a switch for case handling:
switch(Int(AgeFrom)! <= Int(age)!, Int(AgeTO)! >= Int(age)!){
case (true,true): print("Yes, it fits the range")
case (false,true): print("Too young!")
case (true,false): print("Too old!")
}
The second solution is far better for multiple cases of the age value, especially when it's outside the range.
Hope it helps!
You can also use optional binding:
if let ageFrom = Int(ageFrom),
let ageTo = Int(ageTo),
ageFrom...ageTo ~= age
{
print("yes")
} else {
print("no")
}
You have to unwrap the optionals:
if Int(AgeFrom)!...Int(AgeTO)! ~= Int(age)! {
print("yes")
}
of course that is the unsafe way of unwrapping, since it will crash if the conversion of AgeFrom, AgeTO or age fail.

Can I use "guard let" if you want to pass over an optional string to "rawValue:" in enum in Swift?

I want to initialize a enum from a variable of type String?, like:
guard let rawId = request.queryParameters["id"] else {
return
}
guard let id = MyIdentifier(rawValue: rawId) else {
return
}
In this case, request.queryParameters["id"] returns String?. Then after I ensure that it is String in rawId, I convert it into an enum instance id.
However, the code is dirty and I want to write it in one-line if at all possible.
However, I don't like to make it unwrapped via forced optional unwrapping, because if it can not be transformed to String, the app would end up with an error, since rawValue: only takes String. I meant something like the following, which I don't like:
guard let id = MyIdentifier(rawValue: request.queryParameters["id"]!) else {
return
}
So is it still possible to define the guard let in one-line, maybe using where and/or case in guard?
You have two conditions there, trying to combine them into one condition is not always possible.
In your exact case I believe an empty id will behave the same as a nil id, therefore nil coalescing can be used:
guard let id = MyIdentifier(rawValue: request.queryParameters["id"] ?? "") else {
return
}
However, there is nothing dirty about splitting two checks into two statements. Code is not written to be short, it's written to be clear:
guard let rawId = request.queryParameters["id"],
let id = MyIdentifier(rawValue: rawId) else
return
}
Also, there is nothing wrong with creating a custom initializer for your enum:
init?(id: String?) {
guard let id = id else {
return nil
}
self.init(rawValue: id)
}
and then
guard let id = MyIdentifier(id: request.queryParameters["id"]) else {
return
}
Try this:
You can simply combine both the statements into a single statement ,i.e,
guard let id = request.queryParameters["id"], let id2 = MyIdentifier(rawValue: id) else {
return
}

Pattern match if not nil

# Let's say I've got the following typealias
public typealias DateRange = (from: NSDate?, to: NSDate?)
# And use it here
var selectedDateRange: DateRange = (nil, nil)
Can I then use pattern matching to both get the values and unwrap it using pattern matching?
# I can check vs. `nil` like so:
if case (nil, nil) = segmentControl.selectedCustomDateRange {
print("Damn, nil!")
}
# And I want something shorter than
if let from = segmentControl.selectedDateRange.from,
let to = segmentControl.selectedDateRange.to
{
model.setPeriod(from, to: to)
}
Any suggestions?
You can use if with pattern matching and the optional pattern
x?:
if case let (from?, to?) = selectedDateRange {
model.setPeriod(from, to: to)
}
Similar to the above, but different...
guard case let (from?, to?) = selectedDateRange else {
print("Damn, at least one nil!")
return
}
model.setPeriod(from, to: to)}

Pattern matching negation

How can I negate pattern matching in Swift?
For example I want to do something like:
guard case .wait != currentAction.type else {
return
}
But apparently, I can't. I can do this:
if case .wait = currentAction.type {
return
}
but it's less Swifty. Is there a better way?
Apparently, there is no way to do that right now as of Swift 3.
Things can change in future releases.
You can do this in Swift 3.0.2:
guard currentAction.type != .wait else {
return
}

How to use optional unwrapping and a boolean condition in a single if condition

I want to check whether an object is type of NSNumber and a boolean variable is true or not in a single statement.
For this I wrote as below:
let someBool = ...
if value.isKindOfClass(NSDictionary) {
// do something with dict
}
else if (let number = value as? NSNumber) && someBool{
//Do something with number
}
else {
// do something here
}
But, its throwing error like 'pattern variable binding cannot appear in an expression'.
How to use optional unwrapping and a boolean condition in a single if condition?
You could use a where clause:
if let number = value as? NSNumber where someBool {
// ...
}
ABakerSmith's answer is perfect.
I just want to share how to achieve the same result using the powerful Swift switch statement.
switch value {
case let dictionary as NSDictionary:
println("do something with dictionary")
case let number as NSNumber where someBool:
println("do something with number")
default: println("do something here")
}