What Does the try? Keyword Do in Swift Error Handling?

  • Jul 9

What Does the try? Keyword Do in Swift Error Handling?

Error handling is a fundamental part of writing reliable Swift applications. Whether you're reading a file, decoding JSON, making a network request, or interacting with system APIs, many operations can fail. Swift provides several ways to deal with these failures, and one of the most convenient is the try? keyword.

If you've ever wondered when to use try? instead of try or try!, this guide explains everything you need to know.



Understanding Throwing Functions

In Swift, a function that can fail is marked with the throws keyword.

enum FileError: Error {
    case fileNotFound
}

func loadFile() throws -> String {
    throw FileError.fileNotFound
}

Since loadFile() can throw an error, Swift requires you to explicitly acknowledge that possibility whenever you call it.

What Does try? Do?

The try? keyword converts the result of a throwing expression into an optional value.

  • If the operation succeeds, you receive the returned value wrapped in an optional.

  • If the operation throws an error, the result becomes nil.

  • The error itself is silently discarded.

let content = try? loadFile()

print(content)
// nil

Instead of crashing your application or forcing you to write a do-catch block, Swift simply returns nil.

This makes try? an excellent choice when the specific reason for failure isn't important.

How try? Works

Suppose we have a function that sometimes succeeds.

enum LoginError: Error {
    case invalidCredentials
}

func authenticate(username: String, password: String) throws -> String {
    guard username == "admin", password == "1234" else {
        throw LoginError.invalidCredentials
    }

    return "Authentication Successful"
}

Using try? looks like this:

let message = try? authenticate(
    username: "admin",
    password: "1234"
)

print(message)
// Optional("Authentication Successful")

If the credentials are incorrect:

let message = try? authenticate(
    username: "john",
    password: "password"
)

print(message)
// nil

No error is thrown to the caller. The failure is represented by nil.

Using Optional Binding

Since try? returns an optional, it works naturally with optional binding.

if let message = try? authenticate(
    username: "admin",
    password: "1234"
) {
    print(message)
} else {
    print("Authentication failed.")
}

Output:

Authentication Successful

Or if authentication fails:

Authentication failed.

This approach keeps your code concise and easy to read.

try? vs do-catch

The traditional way to handle errors is with a do-catch block.

do {
    let message = try authenticate(
        username: "john",
        password: "password"
    )
    print(message)
} catch {
    print(error)
}

With try?, the same operation becomes:

if let message = try? authenticate(
    username: "john",
    password: "password"
) {
    print(message)
}

Notice the difference:

  • do-catch lets you inspect and respond to specific errors.

  • try? ignores the error completely and returns nil.

Choose the one that matches your application's needs.

try? vs try!

Swift also provides try!.

let message = try! authenticate(
    username: "admin",
    password: "1234"
)

This forcefully assumes the operation will never fail.

If an error is thrown, your application crashes with a runtime error.

Here's a quick comparison:

KeywordBehaviortryMust be handled using do-catch or propagated with throws.try?Converts success into an optional and failures into nil.try!Assumes success and crashes if an error occurs.

As a general rule:

  • Use try when the error matters.

  • Use try? when failure is acceptable.

  • Use try! only when you're absolutely certain the operation cannot fail.

Common Use Cases for try?

Parsing JSON

let user = try? JSONDecoder().decode(
    User.self,
    from: jsonData
)

If decoding fails, user simply becomes nil.

Loading Files

let text = try? String(
    contentsOf: fileURL,
    encoding: .utf8
)

No additional error handling is required if the file is optional.

Reading Cached Data

let cachedImage = try? loadCachedImage()

If no cached image exists, nil can indicate the cache miss.

Optional Feature Support

Some APIs may fail depending on device capabilities or system state. Returning nil is often sufficient to indicate the feature isn't available.

When You Should Avoid try?

Although try? is convenient, it isn't appropriate for every situation.

Avoid it when:

  • You need to know why an operation failed.

  • Different errors require different recovery strategies.

  • Logging errors is important for debugging.

  • The operation is critical to your application's workflow.

For example, payment processing, authentication, or database writes often require detailed error handling rather than silently ignoring failures.

Best Practices

When using try?, keep these recommendations in mind:

  • Use it only when failure is expected and non-critical.

  • Combine it with optional binding for clean control flow.

  • Don't use it to hide important errors.

  • Prefer do-catch when error information is valuable.

  • Avoid replacing all throwing calls with try? simply because it requires less code.

Conclusion

The try? keyword provides a concise way to handle errors in Swift by converting the result of a throwing function into an optional value. If the operation succeeds, you receive the value wrapped in an optional. If it fails, Swift returns nil instead of propagating the error.

This makes try? ideal for situations where failure is expected, acceptable, or doesn't require detailed error handling. By understanding when to use try? versus try or try!, you can write Swift code that is both cleaner and more expressive while maintaining the appropriate level of error handling for your application.