Skip to content

Instantly share code, notes, and snippets.

@jakehawken
Last active July 12, 2023 20:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jakehawken/ca087ca0fd468195c6f97cb5b9e95b3f to your computer and use it in GitHub Desktop.
Save jakehawken/ca087ca0fd468195c6f97cb5b9e95b3f to your computer and use it in GitHub Desktop.
@nonnil - crash with purpose!
/// The NonNil property wrapper allows you to supply an error message at the declaration of a
/// property, and then otherwise access it like it's non-nil in your code. Any access before it's
/// set will still result in a crash the way explicity unwrapping would, but with the added benefi
/// of there being an informative crash message. Intended to be a conscientious and thoughtful
/// replacement for the `!` operator.
///
/// I hate explicitly unwrapped optionals in Swift (or as I call them, "explosive optionals"). They
/// sidestep the responsibility of the dev to handle nil values, and they crash the app without
/// any specific information to send to a logger or debugger. If the nil state isn't a valid state,
/// and it necessitates a crash, I still want there to be a message handed off to the logger/debugger
/// by way of a `fatalError` or `assertionFailure` call. I usually do that manually at the call site,
/// but sometimes you have to access that explosive optional multiple times, and it gets tedious
/// and convoluted to write a million `if let` or `guard let` statements. This type allows you to
/// avoid the boilerplate and the uninformative "unexpectedly found nil" crash messages.
@propertyWrapper
struct NonNil<T> {
private var backingValue: T?
private let errorMessage: String
var wrappedValue: T {
get {
guard let value = backingValue else {
fatalError(errorMessage)
}
return value
}
set {
backingValue = newValue
}
}
init(errorMessage: String) {
self.errorMessage = errorMessage
}
}
/*
Using NonNil is as simple as
struct MyStruct {
@NonNil(errorMessage: "MyStruct requires that myInt be set before use.")
var myInt: Int
}
let myStruct = MyStruct()
doSomeThingWithAnInt(myStruct.myInt) // Crashes with the message supplied on line 41.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment