Enumeration behaviour understanding (Intro to App Development with Swift - Lesson 19) - swift

I'm new to coding and am about to finish the "Intro to App Development with Swift" iBook. I am currently in lesson 19, Enumerations and Switch and, at page 8 of the associated playground it shows the following code:
enum LunchChoice {
case pasta, burger, soup
}
func cookLunch(_ choice: LunchChoice) -> String {
if choice == .pasta {
return "🍝"
} else if choice == .burger {
return "πŸ”"
} else if choice == .soup {
return "🍲"
}
return "Erm... how did we get here?"
}
cookLunch(.soup)
Per se, this is not a problem for me to understand but, once I call cookLunch(.soup), the last return statement doesn't show up.
The exercise just below ask me:
try to change the value passed in to cookLunch so that the final else statement is called
And that is where I got stuck because it seems impossible to pass something different to the cookLunch function other than the choices present in the enumeration.
Could you help me understand the sense behind all this and maybe provide me a solution?

You have two options:
Comment out the third comparison
// } else if choice == .soup {
// return "🍲"
Add a fourth case which is not covered by the comparisons
enum LunchChoice {
case pasta, burger, soup, steak
}
and pass it:
cookLunch(.steak)
However nobody would seriously write such an if - else chain, in Swift a switch expression is appropriate
func cookLunch(_ choice: LunchChoice) -> String {
switch choice {
case .pasta: return "🍝"
case .burger: return "πŸ”"
case .soup: return "🍲"
default: return "Erm... how did we get here?"
}
}

Your instructions say "so that the final else statement is called". That would be the soup return, not the "how did we get here" return. As you say, with 3 lunch choices and 3 if/else statements, one of them will always be invoked. You have to add a 4th lunch choice that doesn't have a corresponding if or else if in order for the "how did we get here" code to execute.

Related

Why use throw in this scenario or others? | Swift

Essentially while this is certainly a basic question with numerous reasons, I cannot seem to understand why it would be good idea to use throw when writing functions that validate.
For example if you take this simple username and password validation scenario, why would anyone want to use throw to help with the validation?
Does it just come down to coding style preference? Or is there a good reason one would opt for using throw for the return of this function?
enum ReturnMessages: String {
case passed, failed, tooshort, nonumbers
}
func validateCredentials(username: String, password: String) -> ReturnMessages {
guard (username.count >= 4 && password.count >= 4) else {return ReturnMessages.tooshort}
guard (password.rangeOfCharacter(from: .decimalDigits) != nil) else {return ReturnMessages.nonumbers}
return ReturnMessages.passed
}
validateCredentials(username: "jeff", password: "password")
Returning an enum value in this case is fine because the possibility to fail is quite high.
But consider that you have to use always a switch to distinguish the good from the bad path.
On the other hand in many cases the expected result is passed or some return value with a very low rate of errors. So isn’t it a better idea to return the good path directly and throw the bad paths.
The benefit is that you can continue your workflow without a runtime check good/bad.

Confused about Optional vs Default value -1

We are working in a Swift project. A function was there like,
fun getSleepAmmount() -> Int {
// calculate sleep time
// return value when valid
// else
return -1
}
My team member prefers the above function where caller needs to check with -1 which is not I am comfortable with. My suggestion is to redesign with nil return (although callers still need to check nullability) like,
fun getSleepAmmount() -> Int? {
// calculate sleep time
// return value when valid
// else
return nil
}
But my colleagues do not want to redesign. Which version of the functions is cleaner and why?
Obviously, nil is much cleaner. Because of -1 means nothing. This is just a magic word. It is difficult to support, refactor and handle this case.
Returning nil is a better solution instead of using any garbage or default value.
Returning any default value may in future clash with the actual result.
Also, there might be other developers dealing with the same code. So, using nil will have a better explanation than using -1.
Second is the better as youll do
if let v = getSleepAmmount() {}
But with First
let v = getSleepAmmount()
if v > 0 {}
Returning nil means this isn't a valid return while -1 may mean another thing that will be miss-understood by a new developer that checks the code
If there is code in the caller that should only run if there is a valid sleep amount, then optionals is the better and clearer way to go. This is exactly what guard let and if let are designed for:
guard let sleepAmount = getSleepAmount() { else return }
// do something with sleepAmount
An even better way would be to throw an error inside the function:
func getSleepAmmount() throws -> Int {
// calculate sleep time
// return when valid
// else
throw InvalidSleepAmount
}
Then
do {
let sleepAmount = try getSleepAmount()
// do something with it
} catch InvalidSleepAmount {
// error processing
}
(If you want, your function could throw different errors so the caller gets to know why the sleep amount is invalid, SleepTooShort, SleepTooLong, NoSleep etc)

Is my code complex enough for switch statement usage in Swift

From Swift documentation:
Typically, you use the if statement to evaluate simple conditions with only a few possible outcomes. The switch statement is better suited to more complex conditions with multiple possible permutations and is useful in situations where pattern matching can help select an appropriate code branch to execute.
I'm trying to decide on if I should use the switch or if/else statements based on if I have a complex condition. So my question is, is my condition complex or not.
Here is an example of what I have:
var sampleVar = Measurement(value: amount, unit: UnitLength.megameters)
if(type == "inches"){
//do something
}
else if...
I have between 5 to 15 possible conditions I'm checking for so would that make it complex enough to justify using a switch statement? Or is complexity based on the condition and not how many conditions there are?
The ultimate test is to just write out both and compare.
When switch is better
When you're dealing with a situation that favours a switch over an if/else ladder, you code will look like:
if something == 1 {
foo()
} else if something == 2 {
bar()
} else if something == 3 {
baz()
} else {
quux()
}
As you can see, all the bracketing, repeated keywords (else, if) repeated operators == and repeated instance of the same identifier (something) add a bunch of noise with really little value. Compare to the switch:
switch something {
case 1: foo()
case 2: bar()
case 3: baz()
default: quux()
}
When if/else if/else is better
You'll find yourself writing a switch where the switched variable isn't really being matched much, but instead you have a bunch of where clauses that check a lot of unrelated conditions. Compare:
switch something {
case 1: foo()
case _ where case2_1() || case2_2(): bar()
case _ where case3(): baz()
case _ where case4(): quux()
}
vs.
if something == 1 || case1() { foo() }
else if case2_1() || case2_2() { bar() }
else if case3() { baz() }
else if case4() { quux() }
Don't forget about polymorphism!
Whenever possible, try to break up complex switching logic into dynamic calls to methods on an object. This allows you to separate the logic of each case into a separate class, where all related logic can be grouped.

Pass Keyword in Swift

I know that the keyword "pass" in Python will allow one to leave a line of code empty where it should have contained an executable statement. Is there a similar keyword in Swift?
I am using a switch statement and Swift requires there to be a default case. The code should reach the default statement most of the time and I want nothing to be done in this case.
You can break out of the default case. Swift just wants you to be explicit about that to avoid bugs.
Here's a simple example:
enum Food {
case Banana
case Apple
case ChocolateBar
}
func warnIfUnhealthy(food : Food) {
switch food {
case .ChocolateBar:
println("Don't eat it!")
default:
break
}
}
let candy = Food.ChocolateBar
warnIfUnhealthy(candy)
The proper way to add a catch-all without an action to a switch statement is to add
default: break
at the end.

Noop for Swift's Exhaustive Switch Statements

Swift requires exhaustive switch statements, and that each case have executable code.
'case' label in a 'switch' should have at least one executable statement
Has anybody settled on a good way to handle the cases where you don't want to actually do anything? I can put a println() in there, but that feels dirty.
According to the book, you need to use break there:
The scope of each case can’t be empty. As a result, you must include at least one statement following the colon (:) of each case label. Use a single break statement if you don’t intend to execute any code in the body of a matched case.
You can use a break statement:
let vegetable = "red pepper"
var vegetableComment: String = "Nothing"
switch vegetable {
case "cucumber", "watercress":
break // does nothing
case let x where x.hasSuffix("pepper"):
vegetableComment = "Is it a spicy \(x)?"
default:
vegetableComment = "Everything tastes good in soup."
}
Example modified from the docs
Below is one option for null statement, but maybe not a good solution. I cannot find a statement like python pass
{}()
for switch case, break is better choice.
break
Do nothing in exhaustive switch case statements:
Swift:
switch yourVariable {
case .someCase:
break
}
SwiftUI:
switch yourVariable {
case .someCase:
EmptyView() // break does not work with ViewBuilder
}
Using EmptyView() instead of break in SwiftUI views prevents the error:
Closure containing control flow statement cannot be used with function
builder ViewBuilder.
EmptyView() is a SwiftUI standard view (tested with Xcode 12, iOS 14) and does not need to be defined yourself.
In addition to break mentioned in other answers, I have also seen () used as a no-op statement:
switch 0 == 1 {
case true:
break
case false:
()
}
Use () if you find break confusing or want to save 3 characters.
The cleanest solution I've found is to simply include your last statement in the switch case as your default. This avoids the need to add break or other unnecessary statements while still covering all possible cases.
For example:
switch myVar {
case 0:
myOtherVar = "Red"
case 1:
myOtherVar = "Blue"
default:
myOtherVar = "Green"
}
You can check specific case, no need to be exhustive with switch cases
Say you have a enum like this,
enum Selection {
case one
case two
case three
}
var myCase = Selection.one
you can check like this,
if case .one = myCase {
print("one")
}
A clean solution I use for my default case is:
default: ()