When to use exceptions and optionals in Swift error handling? - swift

I'm trying to understand Swift error handling better, but I can't find a specific answer to this question online. I have an application that uses optionals a lot to do error handling. For example, here's a snippet of code from my app:
if let winnerName = gameApiResponse.winner {
guard let gameFromApi = GameFromApi(id: gameApiResponse.id, contestantsNames: gameApiResponse.contestants, winnerName: winnerName, confidence: 50, conferencesNames: conferenceNamesInGame, week: gameApiResponse.week) else {
os_log("Could not unwrap new game object in loadGames(gameApiResponses:) in DataModelManager", type: .debug)
return GameFromApi()
}
return gameFromApi
} else {
guard let gameFromApi = GameFromApi(id: gameApiResponse.id, contestantsNames: gameApiResponse.contestants, winnerName: nil, confidence: 50, conferencesNames: conferenceNamesInGame, week: gameApiResponse.week) else {
os_log("Could not unwrap new game object in loadGames(gameApiResponses:) in DataModelManager", type: .debug)
return GameFromApi()
}
return gameFromApi
}
GameFromApi is a class that has a failable initializer and a default, non-failable initializer (with no parameters). If the regular initializer fails, I call the default initializer and just give this function back a "blank" GameFromApi object that has default values. But I'm mostly using this default initializer to indicate that something went wrong, which feels bad. Plus, I feel like I'm overusing optionals to do error handling in cases when the reason for the failure could be many different things.
What's the litmus test for when to use optionals to indicate an error, and when to use exceptions?

You should throw errors whenever details of the error are available, and they could be used in some way to recover the error.
For example, opening a file on the filesystem might fail because of an incorrect path, a permission error, or the file being the wrong type (e.g. a folder). A thrown error might include this information, but it won't really help, because there wouldn't be much your application could do with that information to recover from the error.
In your case, I think optionals are a good choice. However, you shouldn't repeat yourself:
guard let gameFromApi = GameFromApi(
id: gameApiResponse.id,
contestantsNames: gameApiResponse.contestants,
winnerName: gameApiResponse.winner,
confidence: 50,
conferencesNames: conferenceNamesInGame,
week: gameApiResponse.week
) else {
os_log("Could not unwrap new game object in loadGames(gameApiResponses:) in DataModelManager", type: .debug)
return GameFromApi()
}
return gameFromApi

Related

Can I make self.entity a non optional for certian functions in a GKComponent using swift?

I'm using the GameplayKit entity component system to develop a game.
I have a number of GKComponent. Most of the instance functions require that self.entity is not nil, but it is an optional, as a component may be initialised before being attached to an entity.
If I "do things properly", I understand I should use guard (or if) to check that self.entity is not nil, for each function where I require it. This adds a chunk of extra code for each function, and in addition a function may require the entity has specific components to function.
Problem statement:
For some functions in a GKComponent, I want to specify that the function requires self.entity is not nil, and has some specific components, without needing a number of guard or if statements which bulks up my code.
Things I've tried:
I tried creating an extension to GKComponent.
The extension contains a function which throws an error if the entity is nil, returns true if it is not nil.
import Foundation
import GameplayKit
extension GKComponent {
enum ComponentExtensionError: Error {
case hasNoEntity
}
func requiresEntity() throws -> Bool {
if (self.entity != nil) {
return true
} else {
throw ComponentExtensionError.hasNoEntity
}
}
}
GKComponent function:
func setTileWalls () {
try? self.requiresEntity()
# Some stuff
}
This compiles, but doesn't solve my problem, as I still have to access the entity as self.entity?.somefunc().
I figured now I know that self.entity is not nil, I could proceed to set it as unwrapped...
func setTileWalls () {
try? self.requiresEntity()
self.entity = self.entity!
# Some stuff
}
But alas, you cannot SET self.entity.
I considered I could in stead modify the extension to return the entity, and then do something like...
func stTileWalls () {
guard let entity = self.requiresEntity() else { throw }
}
but then I'd need to modify each functions code to use entity rather than self.entity. I don't feel like this is optimal solution, but maybe an acceptable one.
My ideal solution would be to be able to call a function which checks that self has an entity, and some components, making them non optional for the scope, and throwing an error if any are nil.
Why? I'm frustrated by entity and any component always being optional (when I know it shouldn't be). If I understand correctly, "doing things properly" requires that I guard and throw (or return maybe?). Syntactically I feel it looks ugly and I feel like I'm missing something in terms of fixing this in an elegent way with Swift.
There is no easy solution to solve this issue if you want a safe solution that requires less boilerplate code than unwrapping an optional.
Throwing an error instead of returning nil is definitely a bad idea, since handling an error requires even more boilerplate code than safely unwrapping an optional.
You cannot override existing variables types, so you cannot make GKComponent.entity non-optional either.
Use guard statements for optional binding, that way you can minimise the boilerplate code for unwrapping the value.

ReactiveKit Bond KVO observe UserDefaults

I was previously using RxSwift and I decided I did not want to use it anymore and was able to convert everything over to Bond which I am much more familiar with. Since the new changes though to Bond v5, I cannot seem to figure out how to observe values in UserDefaults. The following code ends up giving me a fatal error.
userDefaults.reactive
.keyPath(LocationManager.HomeLocationKey, ofType: String.self, context: .immediateOnMain)
.map(self.initLocation(from:))
.bind(to: self.homeLocation)
userDefaults is a reference to UserDefaults.standard and LocationManager.HomeLocationKey is a string. I am providing the initLocation function below as I know it will be asked for. Below that function I will post the error that I am receiving after the app starts up.
func initLocation(from string: String?) -> Location?
{
guard let dataString = string
else { log.warning("Location data did not exist, returning nil"); return nil }
let json = JSON.parse(dataString)
return Location(from: json)
}
Error:
fatal error: Could not convert nil to String. Maybe `dynamic(keyPath:ofExpectedType:)` method might be of help?): file /Users/sam/Documents/iOS Apps/Drizzle/Pods/Bond/Sources/Shared/NSObject+KVO.swift, line 58
It might not be obvious, but if the observed value can be nil, the ofType argument must be an Optional type. In your case, that would be:
userDefaults.reactive
.keyPath(LocationManager.HomeLocationKey, ofType: Optional<String>.self, context: .immediateOnMain)
...

if statements and optionals in Swift

Is there a difference between something like
if let error = error {
print(error.localizedDescription)
}
and just checking if it is = nil
if error != nil {
print(error.localizedDescription)
}
if I want to check if error has a value? Think of firebase's creating user function.
Yes.
The if let statement allows you to bind the value of error to a variable if it is non-nil and use it in the block. If maybeError is of type Error?, when you do:
if let error = maybeError {
/* block contents */
}
the type of error will be Error within the block - ie: It won't be an optional anymore. If you just do a nil-check using if, error will still be of type Error? within the block. So the code that would actually be equivalent to your first snippet would be:
if error != nil {
print(error!.localizedDescription)
}
(Your second snippet, as it is, won't compile, as you're trying to get the localizedDescription variable of an Error? object, which has no such property)
By the way, in case you haven't seen it before, the !. thing is the unwrap operator. It runs the method on the object if the object is non-nil, but it crashes in case the object is nil. In this case, you generally know it won't crash. (But this might not actually be safe depending on where and how you use it - check #rmaddy's comment)
In the first, error is now a non-optional type, so you can use .. This is also idiomatic -- it more clearly shows what you are trying to do.
In the second, you would need to use ?. to look at error's properties (your code won't compile because you haven't done that).

How to avoid returning an optional after throwing an exception?

I'm writing a utility function which takes a parameter and always returns a valid non-nil result (notice the returned value is not optional because the possible parameters are all actually hardcoded and valid, so I know the function cannot fail):
func myFunction(param: String) -> NonTrivialObject {...}
Now, during development I want to experiment with possible parameters and, should I make a mistake, I want the function to throw an exception and just crash. I don't want or need to throw Swift errors or catch them, I want to hard-crash and fix the parameter immediately. In Objective C I would just use NSParameterAssert() or do something along these lines:
guard let validatedParam = param where param != nil else {
NSException(...).raise()
return nil
}
// do the actual work and return a non-optional result
However, I cannot return nil because the result is not an optional. Is there a way to somehow tell the compiler that it doesn't need to bother returning anything from the function after an exception is thrown? Or am I doomed to litter my code with unwrapping optionals or try! statements or to return a dummy object just to make the compiler pleased?
You can use Swift assert(_:_file:line:) function as follows
assert(some condition, "Message to display if condition is false (optional)" )
If the condition is verified the app will continue running, otherwise it will terminate
An optional may contain nil, but Swift syntax forces you to safely deal with it using the ? syntax to indicate to the compiler you understand the behavior and will handle it safely.
You can define the function to be a throwing function:
func foo(param: String) throws -> NonTrivialObject {
guard param != nil else {
throw SomeErrorEnum.NilFound
}
doStuff(...)
}
The error enum needs to conform to the ErrorType protocol. The function call is now able to catch errors like this:
do {
try foo(param)
} catch SomeErrorEnum.NilFound {
print("Found nil")
}
In this way the function returns a non-optional but can also throw errors. For more information see: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html

Errors in Swift initialisers

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.