Failable initializers (init?) in Swift allow describing a situation where creating an instance of a type may fail and return nil. They are often used for validating input data or transformations that may not succeed. In a failable initializer, you can explicitly return nil, indicating that the object creation was unsuccessful.
Example:
struct User { let name: String let age: Int init?(name: String, age: Int) { guard !name.isEmpty, age > 0 else { return nil } self.name = name self.age = age } }
This way, you can prevent the creation of incorrect objects.
compactMap), it is convenient for filtering invalid instances.Question: What is the difference between init? and init!, and when should you use a failable initializer with implicit unwrapping?
Answer: init? returns an optional (<type?>), and if the initialization failed, it will return nil, requiring safe handling. init! returns an implicitly unwrapped optional (<type!>), and if the initialization failed, it will also return nil, but using such an object without checking will lead to a runtime crash. Use init! only if you are sure that the initialization cannot fail in your context (e.g., when working with storyboard in UIKit).
let value = Int("abc") // value will be nil
Story
When parsing JSON manually, a regular initializer was used instead of a failable one. This led to the creation of "empty" users, as validation did not work, and the application did not filter invalid data.
Story
Using
init!with potentially invalid data caused the application to crash after an API update: the input data format changed, and an exception occurred during object extraction due to implicit unwrapping of nil.
Story
In a custom implementation of failable init, we forgot to explicitly return nil for certain scenarios, resulting in the structure being initialized with "dirty" fields, which later caused bugs in the business logic.