After writing about checking for nil being returned from init, a friend pointed out that I omitted nullable annotations, as well as possibly documented usage. They also raised an interesting point about failing (crashing) early vs partially recovering – given the contrived button example.
Let’s address nullability first. I ignored mentioning nullability annotations as there is a lot of existing code in the wild that hasn’t adopted them. (If you’re writing new code you absolutely should be using them). With nullability annotations, the question of if you should check for nil being returned from init is already answered. If the API specifics it is nullable – check. Otherwise you can assume you’ll get a valid initialized object. Even with nullable annotations there’s no runtime guarantee (it’s a static analyzer warning), but you should trust the API, if it doesn’t perform as advertised – that’s a bug.
As for documentation – you are writing header docs right? Docs can, and should, describe if initialization can fail, and under which conditions that might occur. If the API you’re calling doesn’t have nullability annotations, check the docs. You might find that it’s clearly documented if initialization can fail, and if you should be checking for nil.
You should absolutely be writing header docs by the way. Your team will love you for it. Even if you’re a solo developer; future you will thank you for it.
Finally, onto the topic of crash early, catch early. I used a button class as a completely arbitrary example last time. I could have, and perhaps should have, picked a better example. It’s extremely unlikely that you’d ever want a button to have a failable initializer. But it did raise and interesting question: If a button did actually return nil – what’s the “correct” behavior? Is it better to guard against crashing, or to guard against a broken UI? I think it would depend on a number of factors – what conditions in which the crash might occur, how important the button is to the functionality, is the app doing critical work that needs to be stopped safely? During development I’d be ok with permitting a crash. You might not notice that one in a dozen times the button happened to be missing, but you’d notice a crash right away.
All this is to say, when getting a value back from an initializer, stop and think for just a second, could this fail? What happens if it does? How should we react?