Propagating Errors
Part of the Logic & Flow section of Coddy's Swift journey — lesson 47 of 56.
A throwing function can call other throwing functions. The error propagates up to the nearest do/catch automatically, no manual rethrowing needed.
enum AuthError: Error { case badUser, badPass }
func loadUser(_ name: String) throws -> String {
if name.isEmpty { throw AuthError.badUser }
return name
}
func login(_ name: String, _ pass: String) throws -> String {
let u = try loadUser(name) // forwards the error
if pass.count < 4 { throw AuthError.badPass }
return "hello \(u)"
}
do {
print(try login("", "abcd"))
} catch AuthError.badUser {
print("missing user")
} catch AuthError.badPass {
print("weak password")
}Read top to bottom: login calls loadUser with try. If loadUser throws, the error skips past login's body and lands at the outer catch. You write each step to fail fast and let the caller decide what to do.
Challenge
MediumBuild a tiny pipeline of two throwing steps.
Define StepError: Error with two cases: negative, tooBig.
Define parseSafe(_ s: String) throws -> Int: throws .negative when the parsed integer is below 0, otherwise returns it. (Assume input parses, you've handled that elsewhere.)
Define capStrict(_ n: Int) throws -> Int: throws .tooBig when n > 100, otherwise returns n.
Define processStep(_ s: String) throws -> Int that calls parseSafe then capStrict, letting any error propagate.
Read a single line of input: a comma-separated list of strings. For each one, call processStep in a do/catch and print one line:
ok: <n>on successnegativewhenparseSafethrewtoo bigwhencapStrictthrew
For input 10,-1,200,42, the output is:
ok: 10
negative
too big
ok: 42Cheat sheet
A throwing function can call other throwing functions with try. Errors propagate automatically up to the nearest do/catch:
enum AuthError: Error { case badUser, badPass }
func loadUser(_ name: String) throws -> String {
if name.isEmpty { throw AuthError.badUser }
return name
}
func login(_ name: String, _ pass: String) throws -> String {
let u = try loadUser(name) // error propagates up if thrown
if pass.count < 4 { throw AuthError.badPass }
return "hello \(u)"
}
do {
print(try login("", "abcd"))
} catch AuthError.badUser {
print("missing user")
} catch AuthError.badPass {
print("weak password")
}If loadUser throws, execution skips the rest of login and lands directly at the outer catch. No manual rethrowing is needed.
Try it yourself
let parts = readLine()!.components(separatedBy: ",")
// TODO: enum StepError; parseSafe; capStrict; processStep;
// for each part, do/try processStep, catch each branch, print
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Logic & Flow
1Strings In Depth
Count and IndicesCase and TrimSearching in StringsSplitting and JoiningReplacing SubstringsRecap - Username Check