Initializers in swift part-2: Failable Initializers in swift

This is the part two of my “Initializers in swift” article.

Here is the first part: Initializers in swift part-1 : (intro, convenience and designated intializers)

Let’s start:

Failable Initializers :

We cannot always assume that the initialization will always succeed for a struct, enum or class. It can fail for several reasons. It is sometimes useful to define a class, structure, or enumeration for which initialization can fail.

Note: You cannot define a failable and a nonfailable initializer with the same parameter types and names.

A failable initializer creates an optional value of the type it initializes. You write return nil within a failable initializer to indicate a point at which initialization failure can be triggered. ie; if a condition fails, you can return nil .

Example: We have a failable initializer for converting Double to Int . This is a standard method which returns an optional Int or nil .

// failable initializerlet wholeNumber: Double = 12345.0if let valueMaintained = Int(exactly: wholeNumber) {print(“\(wholeNumber) conversion to Int maintains value of \(valueMaintained)”)}

Consider another example:

struct Animal {let species: Stringinit?(species: String) {if species.isEmpty { return nil }self.species = species}}

You can use this failable initializer to try to initialize a new Animal instance and to check if initialization succeeded:

let someCreature = Animal(species: "Giraffe")// someCreature is of type Animal?, not Animalif let giraffe = someCreature {print("An animal was initialized with a species of \(giraffe.species)")}// Prints "An animal was initialized with a species of Giraffe"

Failable Initializers for Enumerations

Read my article about enums and raw values here.

You can use a failable initializer to select an appropriate enumeration case based on one or more parameters. The initializer can then fail if the provided parameters do not match an appropriate enumeration case.

enum TemperatureUnit {case kelvin, celsius, fahrenheitinit?(symbol: Character) {switch symbol {case "K":self = .kelvincase "C":self = .celsiuscase "F":self = .fahrenheitdefault:return nil}}
__________________________
guard let fahrenheitUnit = TemperatureUnit(symbol: "X") else {print("This is not a defined temperature unit, so initialization failed.")}// Prints "This is not a defined temperature unit, so initialization failed."

Failable Initializers for Enumerations with Raw Values

Enumerations with raw values automatically receive a failable initializer, init?(rawValue:), that takes a parameter called rawValue of the appropriate raw-value type and selects a matching enumeration case if one is found, or triggers an initialization failure if no matching value exists.

enum TemperatureUnit: Character {case kelvin = "K", celsius = "C", fahrenheit = "F"}let fahrenheitUnit = TemperatureUnit(rawValue: "F")if fahrenheitUnit != nil {print("This is a defined temperature unit, so initialization succeeded.")}// Prints "This is a defined temperature unit, so initialization succeeded."

Propagation of Initialization Failure

If your subclass is having a failable initializer which in turn call its superclass failable designated initializer, then if either of the initialization failed means the entire initialization process fails immediately, and no further initialization code is executed.

class Product {let name: Stringinit?(name: String) {if name.isEmpty { return nil }self.name = name}}class CartItem: Product {let quantity: Intinit?(name: String, quantity: Int) {if quantity < 1 { return nil }self.quantity = quantitysuper.init(name: name)}}
_____________________
if let zeroShirts = CartItem(name: "shirt", quantity: 0) { // failsprint("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")} else {print("Unable to initialize zero shirts")}// Prints "Unable to initialize zero shirts"______________________if let oneUnnamed = CartItem(name: "", quantity: 1) { // failsprint("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")} else {print("Unable to initialize one unnamed product")}// Prints "Unable to initialize one unnamed product"

Overriding a Failable Initializer

You can override a superclass failable initializer in a subclass, just like any other initializer. Alternatively, you can override a superclass failable initializer with a subclass nonfailable initializer. This enables you to define a subclass for which initialization cannot fail, even though initialization of the superclass is allowed to fail.

Note that if you override a failable superclass initializer with a nonfailable subclass initializer, the only way to delegate up to the superclass initializer is to force-unwrap the result of the failable superclass initializer.

The init! Failable Initializer

You typically define a failable initializer that creates an optional instance of the appropriate type by placing a question mark after the init keyword (init?). Alternatively, you can define a failable initializer that creates an implicitly unwrapped optional instance of the appropriate type. Do this by placing an exclamation mark after the init keyword (init!) instead of a question mark.

You can delegate from init? to init! and vice versa, and you can override init? with init! and vice versa. You can also delegate from init to init!, although doing so will trigger an assertion if the init! initializer causes initialization to fail.

Where to go from here:

Initializers in swift part-3: Required Initializers in swift

If you enjoyed reading this post and found it useful, please share and recommend it so others can find it 💚💚💚💚💚💚 !!!!

Thanks!!

Written by

iOS and tvOS developer, dreamer, photographer 🤨

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store