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:
func doSomething() throws -> String {
throw SomeRandomError()
}
When you catch it:
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:
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:
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
:
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:
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.