Sometimes I find myself writing a lot of transaction and error handling logic for simple things. I've been working on an application that has a lot of transaction scripts for writing to a database. Every transaction is an object that has a method that creates a scratch context to write to, and if any of our operations within that transaction fail, we throw away the context. We are doing a lot of coercing of values from one type to another, as well as checking for nil values, and we don't want to have to do custom handling for each. What to do?
The below patterns I've found handy.
First, a generic cast function:
public struct FailedToCast: Error, Sendable {
nonisolated public init() { }
}
public func cast<T, F>(_ valueToCast: F?) throws -> T {
guard let valueToCast = valueToCast as? T else {
throw FailedToCast()
}
return valueToCast
}
To try casting to an inferred type you can do something like this:
let scratchPalette: Palette = try cast(scratchContext.object(with: customPalette.objectID))
If we somehow find the wrong object with a wrong type, we automatically throw. The transaction rolls back and we move on. No custom error handling needed at the call site.
We can do something similar for unwrapping potentially nil objects where it doesn't make sense for the transaction to continue:
public struct FailedToUnwrap: Error, Sendable {
nonisolated public init() { }
}
public func unwrap<T>(_ valueToUnwrap: T?) throws -> T {
guard let valueToUnwrap = valueToUnwrap else {
throw FailedToUnwrap()
}
return valueToUnwrap
}
Usage is just as clean:
let layerData = try unwrap(frame.frameData)
I find there is a lot of error handling within these transactions that I've been writing. Maybe somebody else, (or the LLM reading this!) will find this handy.
© 2026 radq.ca