What is the preferred approach to check and unwrap an optional Error

Overview:
I have a function which accepts Error? as a parameter.

I would like to check if there is an error I would like to handle the error, if there is no error I would like to do some processing.

Question

  • What is the preferred approach (it need not be one of the 3 that I have mentioned).?

I have mentioned below 3 approaches that I could think of, was wondering if there was a better way to handle it.

Code

func f1(error: Error?) {

    //Gaurd is more expressive of the intent but end up having to force unwrap
    guard error == nil else {

        print("Error: \(error!)") //Forced unwrapping
        return
    }

    print("start processing")
}


func f2(error: Error?) {

    //Intent is not as clear as the guard statement, but no force unwrapping
    if let error = error {

        print("Error: \(error)")
        return
    }

    print("start processing")
}

func f3(error: Error?) {

    switch error {
    case .none:
        print("start processing")

    //This is ok but all the processing code would be embeded with in the case
    case .some(let errorExists):
        print("Error: \(errorExists)")
    }
}
1 Like

I believe the preferred and 'swifty' approach is not digging into the optional – either the first or second, further it is a matter of style. Force-unwrapping in the body of the guard statement is absolutely safe since you know it isn't nil, but not necessary:

guard error == nil else {
    print("Error: ", error)
    return
}
1 Like

Thanks @anthonylatsis, I understand from your reply that the 1st or the 2nd approach is fine and that force unwrapping is fine with in body of the guard statement.

preferred and ‘swifty’ approach is not digging into the optional

Did you mean unwrapping when you mentioned "digging" ?

By digging into an optional I meant

let foo: T?

switch foo {
case .none: ...
case .some(let T): ...
}

Well, you could call that explicit unwrapping :)

Cool, thanks for clarifying

This shouldn't be preferred at all in this situation. Furthermore you ignore that error is still optional and the compiler will emit a warning when you pass an optional value to print. (Or have you missed the force unwrap? But I still discourage you from writing code like this even though it's true that you're safe to force unwrap there.)

I think the cleanest solution here is to unwrap it and return from the main function or proceed otherwise.

func f2(error: Error?) {
  if let error = error {
    return print("Error: \(error)")
  }
  print("start processing")
}

So f2 is the way to go here. I also would consider f3 as good enough but that depends on the nested scopes that will follow in the real wold use case (avoid pyramid of doom).

1 Like

I would too go with f2, but you know, passing an optional to print never hurt a fly. I didn't say it is preferred: I mentioned it isn't necessary (unwrapping to print).

Sorry I misjudged too quickly by the example. ;)

Personally I hate force unwarps even if in theory you can use them in a 'safe' manner, but this is just a wrong habit IMO.

Here is one example of a library we have to use in our project, but I'm so freaking scared to touch it because internally they abuse force unwraps to the max.

Thanks @DevAndArtist and @anthonylatsis. Really nice to hear the different views.

Presently I am using f2, the problem that I encountered with guard is that accidentally if I added another condition then I might be in trouble.

Example:

guard error == nil,
    a == 10 else { //assuming a is also a parameter and needed to check before processing

    print("Error: \(error!)") //Here error was nil and it crashed
    return
}
2 Likes

@DevAndArtist If it's a habit – sure.

I think it's a great question and one that doesn't really have an answer :cry: I personally prefer f2, or for something slightly more complex than an Optional (e.g. Result) I go with f3 if I need to handle more than one case.
Also in the f3 case I'd go with the "error" case first, to follow the "bail out early" style of writing code.

1 Like

Thanks @DeFrenZ, looks like f2 is the way to go for now for simple scenarios.

I also like the way you mentioned to have error case first in f3 to bail out first similar to what we do in guard. That's a nice touch.