Best way to pull non-optional values from optional containers - swift

This is a best practices question, and I have a feeling I'm missing an obvious solution...
I was stumped why this bit of code made it past the if when wantsGroundAlways was false and I1 was undefined:
let i = c.data["I1"]?.integerValue
if !wantsGroundAlways && i == 0 { return }
The problem was that data["I1"] is, by definition, optional, so Swift inferred i as int?, and nil != 0. Subtle, but obvious in retrospect.
But what is the best way to deal with this common case? One solution is:
let i = (c.data["I1"] ?? 0).integerValue
But personally I think that looks terrible and hides the intent. Something along the lines of:
guard let i = c.data["I1"]?.integerValue else { i = 0 }
would make it obvious what you're trying to do, but it doesn't work because the i cannot be accessed in the else clause and { let i = 0 } is not the same i (try it, you'll see what I mean).
Now there is this:
guard let arcradius = c.data["F1"]?.doubleValue else { return }
which seems really close to what I want to do, but I'm not sure this is really what I think it means - will the return really fire if F1 is not in the dict? And what is the difference between that version and this:
guard case let arcradius = c.data["F1"]?.doubleValue else { return }
Which tells me it's always true?
I think I am missing something key here... so what do you all do in these situations?

I think this is the most straightforward and expressive way of solving your first question:
let i = c.data["I1"]?.integerValue ?? 0
It doesn't require brackets, and shows intent.
?? is the nil coalescence operator. It's a binary operator. If the left operand is not nil, it's returned, otherwise the right operand is returned.
c.data["I1"] can be nil (because there might be no value for the "I1" key). In such a case, c.data["I1"]?.integerValue will be nil. The ?? will then return the 0. If all goes well, and the left side isn't nil, it'll be returned, ignoring the 0.

If you want only to check if a key exist or not then a "type cast" to Int or Double is irrelevant.
Why not simply
guard c.data["I1"] == nil && !wantsGroundAlways else { return }
It passes the test if I1 is not in the dictionary and wantsGroundAlways is false.
if let or guard let is not needed either because according the condition the value for key is never used.

Related

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)

Using guard keyword

I have encountered numerous situations where a coder have used the guard keyword. And then later, in a seemingly almost identical situation the same coder in the same code does not use the guard keyword. I am aware that this may be a stupid question, so please don't bash it. When should I use the guard keyword and where shouldn't I?
Here is an example (there are many more). This is part of a script that is requesting data form an API.
//Here I am using guard
guard let json = json else {
//Now I am not using guard
if let error = error {
completion(.Failure(error))
} else {
//Error handling
}
return
}
Why not use the:
if let var1 = var1 {
//Keep on going
} else {
//Don't crash
}
syntax all the time instead of the guard syntax? At first glance it even seems to have more functionality, but I am certain that does not have to be the case.
One great benefit of the guard statement is that you know that if the condition is not satisfied then the execution flow gets stopped.
This is important for several reasons
Unwrapping
You can define unwrapped values which don't need a new scope { ... } to be available
func next(num:Int?) -> Int? {
guard let num = num else { return nil }
return num + 1
}
Readability
When you read the code you know that if the guard condition is not satisfied then the following lines won't be executed.
Semantics
You know a guard statement is there to check conditions required for the following block of code.
But I can replace every guard with an if
Sure. We could also replace every while and for with a goto in some languages. And we could always replace recursion with iteration (and viceversa).
But this doesn't necessarily means it is always a good idea.
Despite we can implement some behaviours with more then one programming "tool", we should still use the one that better fits that specific scenario.

How to test if Any is actually an Optional? (Swift)

I'm writing some debug code to which I need to pass a parameter of type Any. For printing purposes I'd like to unwrap the parameter value iff it's an optional, but I can't figure out how to test that - every syntactic form I can think of is rejected by the compiler. E.g.,
switch val {
case as Optional<Any>:
.
.
and a variety of let forms (including trying .dynamicType) aren't legitimate. Does anyone know how to actually do this? Overall, what I'm trying to accomplish is such that whether or not the value is an optional, I get the actual value into a string and not Optional.
Martin is absolutely correct. From the linked post, modified slightly because I wanted a different return for nil:
func unwrap(any:Any, ifNil: Any = "nil") -> Any {
let mi = Mirror(reflecting: any)
if mi.displayStyle != .Optional {
return any
}
if mi.children.count == 0 { return ifNil }
let (_, some) = mi.children.first!
return some
}

Creating a calculator app

So I'm very new to Swift and have been following this tutorial to make this app https://www.youtube.com/watch?v=NJHsdjH2HdY
This was the first problem: currentNumber = currentNumber * 10 + Float(sender.titleLabel!.text!.toInt()!)
In the comments section the guy said to change that line to:
currentNumber = currentNumber * 10 + Float(Int(sender.titleLabel!.text!)!)
I did this and I get the error: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
As said in the comments, you should always avoid using the 'crash operator' (!) – and learn to safely deal with optional values instead.
Your program is crashing because either the titleLabel, text or Int(...) are nil – and you are then trying to force unwrap them. This could mean that your button doesn't have a titleLabel or that titleLabel's text isn't convertible to an Int.
The solution is to safely deal with the optionals as you encounter them. There are many ways of doing this, but I usually like to use one or multiple guard statements. This allows you to safely unwrap an optional if it has a value, otherwise it will execute the code within the brackets. This is useful for when future code depends on the optional not being nil. For example:
guard let buttonText = sender.titleLabel?.text else {
print("Sender didn't have either a titleLabel or text!")
return
}
guard let textAsInt = Int(buttonText) else {
print("Text wasn't convertible to an Int!")
return
}
currentNumber = currentNumber*10 + Float(textAsInt)
Now you get helpful print messages instead of crashes! Therefore you know exactly what went wrong, and what you can do to fix it (if it needs fixing).
You could also consolidate both of these checks into a single guard if you want more concise code, but less precise errors:
guard let buttonText = sender.titleLabel?.text, textAsInt = Int(buttonText) else {
print("Something went wrong when converting the button title to an Int!")
return
}
currentNumber = currentNumber*10 + Float(textAsInt)
Or you can use flatMap if you like closures:
guard let i = sender.titleLabel?.text.flatMap({Int($0)}) else {
print("Something went wrong when converting the button title to an Int!")
return
}
currentNumber = currentNumber*10 + Float(i)
The flatMap option can look a bit weird at first, but all it's doing is attempting to convert the button's titleLabel's text to an Int. If it fails it will return nil (which the guard will pick up), else it will return the numerical value of the text.
As #vacawama said in the comments, you could also use the nil coalescing operator in order to use 0 in the event that the titleLabel, text or Int(...) are nil:
currentNumber = currentNumber * 10 + Float(Int(sender.titleLabel?.text ?? "") ?? 0)
However bear in mind that this could lead to unexpected behaviour. I suspect that your program is crashing because your logic is getting run for non-numerical buttons, for example the "+" button. If this is the case, you'll be multiplying your number by 10 every time you press a non-numerical button. You'd have to first ensure that your logic only gets called on numerical buttons.
Although without seeing your full code, it's hard to say for sure.
For more info about how to safely deal with optionals, see this extensive Q&A on the subject.

Why does an optional property have to return something?

So i've got this read only property:
private var allProducts: [String]? {
if let selectedProductSeries = selectedProductSeries {
return ["someProduct"]
} else {
return nil
}
}
Great, that does what I want, but my question is why do I have to be exhaustive in my if/else. I'm marking it as optional and thus what I'd expect to happen is that if I don't return anything its just nil. But of course if I get rid of the else it complains saying: Missing return in function expected to return: [String]? Which is funny because the message says its expecting me to return something even though its optional.
Why is this?
I would assume that errors might occur more often, if the return nil was implicitly added. Inside of a function/method which contains many if- or switch-statements, you could easily overlook, that you might fall through each and every statement. An explicit return nil forces you to deal with that possibility.