Skip to content

altai is a lightweight Swift library that improves the developer experience with typed throws

License

Notifications You must be signed in to change notification settings

csanfilippo/altai

Repository files navigation

altai

altai logo

altai is a simple Swift package aiming to improve the developer experience when using Swift typed throws.

The background

Swift 6 introduced typed throws, allowing functions to specify the type of error they can throw in their signature.

enum CustomError: Error {
    case oddNumber
}

func isEven(_ number: Int) -> Bool {
    return number.isMultiple(of: 2) || number == 0
}

func throwingIfOdd(_ number: Int) throws(CustomError) -> Int {
  guard !isEven(number) else {
    throws .oddNumber
  }

  return number
}

When calling the function throwingIfOdd, the Swift compiler knows that the function can throw only CustomError.

do {
  let evenNumber = try throwingIfOdd(2)
  print("\(evenNumber) is even")
} catch {
  // Here error is of type CustomError
}

A (bad) side effect of typed throws is the problem of nested errors. Consider the following variation of the throwingIfOdd function

enum CustomError: Error {
    case oddNumber
    case decodingError
}

func throwingIfOdd(_ number: String) throws(CustomError) -> Int {
    
    do {
        let number = try JSONDecoder().decode(Int.self, from: number.data(using: .utf8)!)
        
        guard isEven(number) else {
            throw CustomError.oddNumber
        }
        
        return number
    } catch {
        if let customError = error as? CustomError {
            throw customError
        } else {
            throw .decodingError
        }
    }
}

The function now accepts a String input and attempts to decode it as an Int. If the decoding is successful, it evaluates whether the resulting Int is odd or even. However, if JSONDecoder throws an error, the code must catch it and map it to a CustomError. Even worse, we need to rethrow the CustomError that the function raised.

Where altai can help

By conforming to the UpliftingErrors defined in altai, CustomError becomes

enum CustomError: Error, UpliftingErrors {
    case oddNumber
    case uplifted(any Error)
}

and the throwingIfOdd becomes

func throwingIfOdd(_ number: String) throws(CustomError) -> Int {
    
    let number = try CustomError.uplift {
        try JSONDecoder().decode(Int.self, from: number.data(using: .utf8)!)
    }
    
    guard isEven(number) else {
        throw .oddNumber
    }
    
    return number
}

altai defines the extension method uplift, that catches the inner error and maps it to the domain specific error.

Swift Package Manager

If you want to use altai in any other project that uses SwiftPM, add the package as a dependency in Package.swift:

dependencies: [
  .package(
    url: "https://github.com/csanfilippo/altai.git",
    from: "1.0.0"
  ),
]

About

altai is a lightweight Swift library that improves the developer experience with typed throws

Topics

Resources

License

Stars

Watchers

Forks

Languages