Typed Throws in Swift 6

Hi Swifters 👋🏻, Today i am going to talk about Typed Throws that were introduced in Swift 6. If you have written any Swift code, you might have probably bumped into try, catch, and throws. Or maybe you have wrapped something in a do block and caught errors like a pro. Well, That’s great! But Swift 6 takes this one giant step further with this brand new feature. Let’s break this down like a casual dev chat over a coffee ☕.

Starting with Catching Everything but Knowing Nothing 😕

Imagine you’ve got a function that throws an error:

Swift
func doSomething() throws -> String { 
    throw SomeRandomError() 
}

When you catch it:

Swift
do { 
    let result = try doSomething() 
} catch { 
    print(error) 
    // This could be ANYTHING conforming to Error 
}

That error is just some Error. This could literally be anything. And you have no idea what exactly you are dealing with unless you manually check its type. If you are building robust systems, like a networking layer etc. vague errors aren’t helpful. You want to know exactly what went wrong and handle it accordingly. And that’s where Type Throws come in handy. Now in Swift 6, you can declare exactly what kind of error your function might throw:

Swift
enum ParseError: Error { 
    case invalidFormat 
    case missingField(String)
} 

func parseRecord(from string: String) throws(ParseError) -> Record { 
    // parsing logic... 
    throw .invalidFormat 
}

Now when you call it:

Swift
do { 
    let record = try parseRecord(from: myString) 
} catch { 
    // error is inferred to be ParseError! 
    print(error) 
}

Your catch block knows what it’s dealing with. It’s not just Error. It’s a ParseError Or whatever type you have defined. Sounds fun right? But the fun is not over yet. Let’s look at how it can make generics more powerful in terms of forwarding specific error types.

Let’s look at a custom version of map:

Swift
extension Sequence { 
    func map<T, E>(_ body: (Element) throws(E) -> T) throws(E) -> [T] { 
        var results = [T]() 
        for element in self { 
            results.append(try body(element)) 
        } 
        return results 
    } 
}

Depending on what you pass into map, Swift infers what kind of error it might throw:

Swift
let data = ["good", "bad"] 
let result = try data.map { item in 
    if item == "bad" { 
        throw ParseError.invalidFormat 
    } 
    return item.uppercased() 
}

Now map knows it throws ParseError, not just some generic Error. That’s real power. If the closure doesn’t throw? E becomes Never and map just doesn’t throw.

You Might Still be Wondering Why This Matters 🤔

Well, Typed throws gives you better compiler checks, clearer APIs, simpler error handling logic and more predictable code behavior, especially for things like network layers, parsing systems, framework APIs with tight contracts and so on.

Well that was a quick intro of Type Throws in Swift 6 and i hope you liked it. Stay tuned for more Swift 😉 and i will see you in the next one.


Posted

in

Tags: