Last active
July 12, 2023 20:27
-
-
Save jakehawken/ca087ca0fd468195c6f97cb5b9e95b3f to your computer and use it in GitHub Desktop.
@nonnil - crash with purpose!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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