Swift: Try, Try?, Try! and ‘Double Optionals’..

Hadhi Abdul Kareem
Aug 31, 2018 · 4 min read

Swift introduced error handling with a better and simple approach than Objective C. You can mark a method with the swift keyword throws which means this method may throw errors and callers has to handle it as they like.

enum UserNameError : Error {
case userIdNotExist
}
func getNameOfUser(userId uid: Int) throws -> String { if uid == 0 {
return "Alex"
} else if uid == 1 {
return "Jack"
} else if uid == 2 {
return "Jil"
}
throw UserNameError.userIdNotExist
}

In this example, method getNameOfUser handles only 3 userIds. For other userIds it throws an error. Now lets see, how we can use Try, Try! and Try? for calling this method.

Try

You can use this in 2 ways. First one is, catch the error while calling using try/catch.

func callerMethod() {    do {        let name = try getNameOfUser(userId: 1)    } catch {
// Handle the error
}
}

.. or make sure that, the caller method is marked with throws which passes the error to the call chain.

func callerMethod() throws {    let name = try getNameOfUser(userId: 1)}

Try?

This is the latest addition. It returns an optional value and successfully unwraps if the call was successful. It returns nil in case of any error.

func callerMethod() {    let name = try? getNameOfUser(userId: 1)}

In the given example, variablename will get unwrapped String value for userIds 0, 1 and 2. But for other userIds, name will be nil. This same can be handled using the guard keyword

func callerMethod() {    guard let name = try? getNameOfUser(userId: 1) else {        //Handle error by throwing an error or do something        return
}
// name can be used now as it holds unwrapped value
}

Try!

This is same as Try? but it does a forced unwrap. So this may cause a crash in case of an error. Use this if you are damn sure that the callee method is going to return a value.

func callerMethod() {    let name = try! getNameOfUser(userId: 1)}

In this example, userIds 0, 1 and 2 will return a valid String but anything else will cause a crash.

Double Optionals

Hope all you got above points.. Now there is a special case where none of these going to work as expected. And that is Double Optionals. Lets have a special case for this. We are going to modify our simple getNameOfUser method for that.

func getNameOfUser(userId uid: Int) throws -> String? {    if uid == 0 {
return "Alex"
} else if uid == 1 {
return "Jack"
} else if uid == 2 {
return nil
}
throw UserNameError.userIdNotExist
}

In this modified example, getNameOfUser method now returns optional value. In this case, userId 2 returns nil but for values other than 0, 1 and 2, this method can throw error too.

When you use Try!, it still returns an optional value instead of an unwrapped value. If you use Try?, it gives an optional but you will be able to see 2 ?? in the debugger. Check below screenshots of the debugger window

The second screenshot shows, Double Optional. To solve this, either you have to unwrap it twice..

… or use case let keyword.

Hadhi Abdul Kareem

Written by

iOS/Mac developer | Loves food, cars, dogs & nature | Want to be a mechanic and teacher in future | Want to shout, “Helping hands are holier than praying lips”

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade