The ultimate guide how to init your Swift data types, with the help of designated, convenience, failable intitializers and more.
What is initialization?
Initialization is the process of preparing an instance of a class, structure, or enumeration for use.
This process is handled through initializers, an initializer is just a special kind of function, usually the init keyword is reserved for them - so you don't have to use the func keyword - and usually you don't return any value from an initializer.
Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created.
First imagine a really simple struct, that has only two properties.
Now, the rule above says that we have to init all the properties, so let's make that by creating our very first init method.
It's just like every other Swift function. Now we're able to create our first point.
Note that you don't have to initialize implicitly unwrapped optional properties, and optional properties, if they are variables and not constants.
The same logic applies for classes, you can try it by changing the struct keyword to class. However structs are value types, classes are reference types and this difference will provide us some unique capabilities for both types.
Memberwise initializer (ONLY for structs)
The nice thing about structs is that the compiler will generate a memberwise init for free if you don't provide your own init method. However there are a quite a few catches. The generated method will contain all the properties (optionals too) except constants with default values, and it will have an internal access type, so it's not going to be visible from another modules.
The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Likewise, if any of the structure’s stored properties are file private, the initializer is file private. Otherwise, the initializer has an access level of internal.
Sometimes things can go wrong, and you don't want to create bad or invalid objects, for example you'd like filter out the origo from the list of valid points.
Enumerations that deliver from a RawRepresentable protocol can be initialized through rawValues, that's also a failable init pattern:
You can also use init! instead of init?, that'll create an implicitly unwrapped optinal type of the instance. Note that classes can also have failable initializers.
Initializing pure Swift classes
You know classes are native types in the Swift programming language. You don't even have to import the Foundation framework in order to create a brand new class. Here is the exact same Point object represented by a pure Swift class:
This time we had to provide the init method by ourselves, because classes don't have memberwise initializers. They are reference types, and inheritance logic, so it'd be more complex to generate memberwise init methods for them.
For Swift classes you will only get an internal default initializer for free if you provide default values for all the stored properties, even for optional ones. In practice it looks something like this:
Or if we follow the previous example:
This feels so wrong. Why would a point have a key and a label property? It'd be nice to have a child object which could have the extra properties. It's time to refactor this code with some class inheritance.
Designated initializers are the primary initializers for a class.
In other words, it's not marked with the convenience keyword. A class can also have mutliple designated initializers. So let's continue with our Point class, which is going to be the superclass of our NamedPoint class.
A designated initializer must always call a designated initializer from its immediate superclass, so you have to delegate up the chain. But first we had to initialize all of our properties, by the first rule of initialization. So this means that the Swift language has a two-phase initialization process.
- Every stored property is assigned an intial value by the class that introduced it.
- Each class is given the opportunity to customize it's stored properies.
So by these rules, first we had to init the label property, then delegate up and only after then we gave the opportunity to do other things.
They are initializers used to make initialization a bit easier.
So for example in our previous case if we could have an initializers for points where x and y are equal numbers. That'd be pretty handy in some cases.
A convenience initializer must call another initializer from the same class and ultimately call a designated initializer.
Stucts can also have "convenience" initializer like init methods, but you don't have to write out the keyword, actually those init methods are slightly differnet, you can just call out from one to another, that's why it looks like the same.
If you mark an initializer required in your class, all the direct - you have to mark as required in every level - subclasses of that class have to implement it too.
In Swift initializers are not inherited for subclasses by default. If you want to provide the same initializer for a subclass that the parent class already has, you have to use the override keyword.
There are two rules of init inheritance:
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.
A deinitializer is called immediately before a class instance is deallocated.
So if you want to do some manual cleanup when your class is being terminated, this is the method that you are looking for. You don't have to deal with memory management in most of the cases, because ARC will do it for you.
- Swift initialization by Apple
- The Swift Programming Language (Swift 4)
- Designated initializers and convenience initializers in Swift
- Demystifying Swift's initializers: Part 1, Part 2