Errors in Swift initialisers - swift

What is the best way to handle an init that might fail in Swift? For example, you create an instance of class that depends on a certain resource that might not be available.
Apparently we have 2 options:
A bailable init that returns nil (the Cocoa way)
An init that throws an error
See below
enum ThingError: ErrorType{
case crap
}
class Thing {
init(c: Int) throws{
if c < 0 {
throw ThingError.crap
}
}
}
var c = try Thing(c: 3)
do{
var d = try Thing(c: -4)
}catch{
print("oh vey!")
}
Is there a recommended way of doing this? The second option seems more "Swifty"...

Neither is inherently better or Swiftier.
Personally I find throws initializers a huge pain. I'd much rather have a failed initializer return nil, because then I can do my initialization with guard let instead of having to wrap things in do/catch and deal with the resulting scoping issues. Your code illustrates the problem; your var d is "stuck" inside a do scope. I'd rather say this:
guard let d = Thing(c:-4) else {return}
// now d is unwrapped and in scope!
...than this (what you have to say):
do {
var d = try Thing(c: -4)
} catch {
print("oh vey!")
}
// and here there is no `d`, so _now_ what?
On the other hand, throwing an error offers an opportunity to send a message, i.e. to be communicative about exactly what went wrong. You can't do that with a mere init? initializer; it works or it fails, and that's all the caller knows.

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)

if let a = b, a.something() else

I have a code snippet here that works but am curious if there is a cleaner way to accomplish the same thing. I haven't seen anything exactly like this so far.
Logic I want to Achieve
The error is nil or is not a SpecialError
The error is non-nil BUT .foo() returns false
Code
enum SpecialError: Error {
func foo() -> Bool
}
let error: Error? // Some optional Error is passed in
if let myError = error as? SpecialError, myError.foo() {
// Don't care about this case
} else {
// This is the case I'm interested in
bar()
}
I'm curious if there is a better way to accomplish this if let else logic.
I may be misunderstanding, but it seems like nothing happens in the first branch of the if statement and you want to cut it down to just the second part? In that case, this should work for you:
if !((error as? SpecialError)?.foo() ?? false) {
bar()
}
This will execute bar() if:
1. error is nil
2. error is not a SpecialError
3. foo() returns false
The condition you want is for when the expression (error as? SpecialError)?.foo() evaluates to either:
nil, in which case error is not a SpecialError, or is nil.
false, in which case error is a SpecialError, but foo() returned false.
In your case, one way to express this is by taking advantage of the fact that the equality operators are overloaded for optionals, and saying:
if (error as? SpecialError)?.foo() != true {
bar()
}
As we're using the overload of != that compares optionals, true will be promoted to a Bool?, so we're checking that (error as? SpecialError)?.foo() is not a .some(true), which in this case is equivalent to checking if it is .some(false) or .none.
If I understand your question correctly, you're probably looking for something like this:
if !((error as? SpecialError)?.foo() ?? false) {
How about translating it exactly as you explained it:
if error == nil || !(error! is SpecialError) || !error!.foo() {
bar()
}
The short-circuiting of or will prevent the force unwraps of error from being a problem.
I think your code is hard to read, and John Montgomery's code is even harder to read and I predict it will be hard to maintain: imagine another developer looking at this code a year from now and asking you what it is doing, or even worse, that developer cannot ask you as you are no longer available. Imagine yourself looking at this code even even a couple months from now.
if let myError = error as? SpecialError, myError.foo() is convoluted, perhaps being a bit too clever. It contains too much logic to be long term readable by a team of developers.
Your first block could just check if myError is nil
Your second block could just check if is myError of type SpecialError
//you could use a guard statement here
if myError == nil {
return;
}
if let myError = error as? SpecialError {
//ok myError is type SpecialError it has a foo method I can call
if(myError.foo()) {
//do something
} else {
//do something else
}
} else { //myError not type SpecialError and is not nil
// This is the case I'm interested in
bar()
}
if let theFoo = (error as? SpecialError)?.foo(){
if theFoo != true{
bar()
} //in case you want to do handling if theFoo is true
} //in case you want to do handling if error is not SpecialError

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.

try, try! & try? what’s the difference, and when to use each?

In Swift 2.0, Apple introduced a new way to handle errors (do-try-catch).
And few days ago in Beta 6 an even newer keyword was introduced (try?).
Also, knew that I can use try!.
What's the difference between the 3 keywords, and when to use each?
Updated for Swift 5.1
Assume the following throwing function:
enum ThrowableError: Error {
case badError(howBad: Int)
}
func doSomething(everythingIsFine: Bool = false) throws -> String {
if everythingIsFine {
return "Everything is ok"
} else {
throw ThrowableError.badError(howBad: 4)
}
}
try
You have 2 options when you try calling a function that may throw.
You can take responsibility of handling errors by surrounding your call within a do-catch block:
do {
let result = try doSomething()
}
catch ThrowableError.badError(let howBad) {
// Here you know about the error
// Feel free to handle or to re-throw
// 1. Handle
print("Bad Error (How Bad Level: \(howBad)")
// 2. Re-throw
throw ThrowableError.badError(howBad: howBad)
}
Or just try calling the function, and pass the error along to the next caller in the call chain:
func doSomeOtherThing() throws -> Void {
// Not within a do-catch block.
// Any errors will be re-thrown to callers.
let result = try doSomething()
}
try!
What happens when you try to access an implicitly unwrapped optional with a nil inside it? Yes, true, the app will CRASH!
Same goes with try! it basically ignores the error chain, and declares a “do or die” situation. If the called function didn’t throw any errors, everything goes fine. But if it failed and threw an error, your application will simply crash.
let result = try! doSomething() // if an error was thrown, CRASH!
try?
A new keyword that was introduced in Xcode 7 beta 6. It returns an optional that unwraps successful values, and catches error by returning nil.
if let result = try? doSomething() {
// doSomething succeeded, and result is unwrapped.
} else {
// Ouch, doSomething() threw an error.
}
Or we can use guard:
guard let result = try? doSomething() else {
// Ouch, doSomething() threw an error.
}
// doSomething succeeded, and result is unwrapped.
One final note here, by using try? note that you’re discarding the error that took place, as it’s translated to a nil.
Use try? when you’re focusing more on successes and failure, not on why things failed.
Using Coalescing Operator ??
You can use the coalescing operator ?? with try? to provide a default value incase of failure:
let result = (try? doSomething()) ?? "Default Value"
print(result) // Default Value

Proper way to handle a fail to init

I am looking for a proper way to handle a invalid argument during a initialization.
I am unsure how to do it using Swift as the init has't a return type. How can I tell whoever is trying to initialize this class that you are doing something wrong?
init (timeInterval: Int) {
if timeInterval > 0
self.timeInterval = timeInterval
else
//???? (return false?)
}
Thank you!
Use a failable initializer. Such an initializer looks very similar to a regular designated initializer, but has a '?' character right after init and is allowed to return nil. A failable initializer creates an optional value.
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
See Apple's documentation on failable initializers for more detail.
In swift, you can't really abort a task half way through execution. There are no exceptions in swift and in general the philosophy is that aborting a task is dangerous and leads to bugs, so it just should't be done.
So, you verify a value like this:
assert(timeInterval > 0)
Which will terminate the program if an invalid value is provided.
You should also change timeInterval to be a UInt so that there will be a compiler error if anybody tries to give a < 0 value or an integer value that could be < 0.
It's probably not the answer you're looking for. But the goal is to check for bad parameters as early as possible, and that means doing it before you create any objects with those parameters. Ideally the check should be done at compile time but that doesn't always work.
I think this is the best solution, took it from:How should I handle parameter validation Swift
class NumberLessThanTen {
var mySmallNumber: Int?
class func instanceOrNil(number: Int) -> NumberLessThanTen? {
if number < 10 {
return NumberLessThanTen(number: number)
} else {
return nil
}
}
#required init() {
}
init(number: Int) {
self.mySmallNumber = number
}
}
let iv = NumberLessThanTen.instanceOrNil(17) // nil
let v = NumberLessThanTen.instanceOrNil(5) // valid instance
let n = v!.mySmallNumber // Some 5
In the Swift book by Apple, at the very bottom of this section:https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399
They say:
When to Use Assertions
Use an assertion whenever a condition has the potential to be false,
but must definitely be true in order for your code to continue
execution. Suitable scenarios for an assertion check include:
An integer subscript index is passed to a custom subscript
implementation, but the subscript index value could be too low or too
high. A value is passed to a function, but an invalid value means that
the function cannot fulfill its task. An optional value is currently
nil, but a non-nil value is essential for subsequent code to execute
successfully.
This sounds exactly like your situation!
Thus your code should look like:
init (timeInterval: Int) {
assert (timeInterval > 0, "Time Interval Must be a positive integer")
// Continue your execution normally
}