Hidden feature: initializers
You’re more of a video kind of person? I’ve got you covered! Here’s a video with the same content than this article 🍿
I’m certain you’ve already written an initializer before.
But did you know that initializers in Swift have some hidden features?
Let me show you!
Consider this initializer that creates a User
and takes the user’s name
as its argument.
Before we go ahead and create the User
, we wan’t to make sure that its name
isn’t empty.
But what should we do in the case where the user’s name
is actually empty?
A first solution is to make the initializer failable.
You do it by adding a question mark just after the keyword init
.
And once the initializer has been made failable, it becomes possible to return nil
inside its implementation.
This will work perfectly, but there’s a small drawback: the reason why the initializer failed will not be obvious at the call site.
So if needed, we can improve on this by turning the failable initializer into a throwing initializer.
Now, our initializer has gained the ability to throw
errors that will explain why the initializer failed.
And if the caller is not interested in the details of the error, it can get back to the behavior of a failable initializer, by using the keyword try?
That’s all for this article, I hope you’ve enjoyed learning about these advanced features of initializers!
Here’s the code if you want to experiment with it:
// Failable init
struct User {
let name: String
init?(name: String) {
guard name.isEmpty == false else {
return nil
}
self.name = name
}
}
let user = User(name: "") // nil
// Throwing init
enum UserCreationError: Error {
case emptyName
}
struct User {
let name: String
init(name: String) throws {
guard name.isEmpty == false else {
throw UserCreationError.emptyName
}
self.name = name
}
}
do {
let user = try User(name: "")
} catch {
print(error) // emptyName
}