Initializers in Swift

      5 Comments on Initializers in Swift

In Swift you'll be constructing objects all the time. It might seem simple at first glance but initializers in Swift are a pretty deep topic.In Swift you’ll be constructing objects all the time. It might seem simple at first glance but initializers in Swift are a pretty deep topic. In this article we’ll cover designated and convenience initializers. We’ll touch a bit on the failable initializers, required initializers and initializers in structs. We’ll finish with a deinit šŸ™‚

Initialization

Initialization is a process of preparing your class (or struct) for use. Swift is a bit strict on initialization. All your properties have to be initialized in order for you to use the class (there are some exceptions to this, we’ll cover them later).

When you define your class and your variables, you can assign default values to your variables, if that makes sense to you. Or you can use the initializer to set the values of your properties. For example, you could set default values for your properties like this:

class Movie {
    var title: String = "No Title"
    var year: Int = 1999
}

let unknownMovie = Movie()

print(unknownMovie.title) // prints: No Title

If you provide default values for all the non-optional properties you will get this default initializer for free. Another way to initialize your properties is to create your own initializer:

class Movie {
    var title: String
    var year: Int
    
    init(withTitle title: String, andYear year: Int) {
        self.title = title
        self.year = year
    }
}

let knownMovie = Movie(withTitle: "Be Creative", andYear: 1991)

print(knownMovie.title) // prints: Be Creative

Optional Properties

The properties you don’t have to initialize are optional properties. It kinda makes sense when you think about it, because they can be nil anyway. If you added an optional property to our example above, your code would compile just fine:

class Movie {
    var title: String
    var year: Int
    var story: String?
    
    init(withTitle title: String, andYear year: Int) {
        self.title = title
        self.year = year
    }
}

The same applies to implicitly unwrapped optional properties. If we were to make the ‘story’ property of ‘String!’ type the code would still compile.

Constant Properties

You can set your constant properties in the initializers, as you would expect:

class Movie {
    let title: String
    var year: Int
    var story: String!
    
    init(withTitle title: String, andYear year: Int) {
        self.title = title
        self.year = year
    }
}

You can set them only once, and a subclass can’t set the constant properties of its superclass.

Just a note on setting the properties. If you’re setting your properties directly or in an initializer your property observers won’t get called.

Designated Initializers and Convenience Initializers

If you don’t provide all the default values for your properties you must have an initializer or your code won’t compile. In swift we have multiple types of initializers. Designated initializerĀ is an initializer thatĀ  initializes all the non-optional properties. You must have at least one designated initializer. From the example above, we created a designated initializer:

class Movie {
    let title: String
    var year: Int
    var story: String?
    
    init(withTitle title: String, andYear year: Int) {
        self.title = title
        self.year = year
    }
}

Another type of initializer is a convenience initializer. You create a convenience initializer by adding a keyword ‘convenience’ in front of it. Convenience initializer must call a designated initializer. Or it can call another convenience initializer, as long as the last convenience initializer in the chain calls the designated initializer. Let’s see an example of a convenience initializer:

class Movie {
    let title: String
    var year: Int
    var story: String!
    
    init(withTitle title: String, andYear year: Int) {
        self.title = title
        self.year = year
    }
    
    convenience init(withTitle title: String) {
        self.init(withTitle: title, andYear: 2017)
    }
}

let aMovie = Movie(withTitle: "Be Creative")

print("\(aMovie.title) : \(aMovie.year)") // prints: Be Creative : 2017

Inheritance and Initializers

For classes, initializers get a bit more interesting. Convenience initializers can only call other initializers within the same class, they can’t call the superclass initializers. Only designated initializers can call the superclass initializers. There’s a great graphic from Apple that illustrates this:

Source: apple.com

Subclasses don’t inherit a superclasses’ initializers. So you will have to provide overrides yourself. There are two cases where your subclass will automatically inherit the superclass initializers: If you provide all the default values for the properties, or if you implement all of the superclasses designated initializers. For example:

class Movie {
    let title: String
    var year: Int
    var story: String?
    
    init(withTitle title: String, andYear year: Int) {
        self.title = title
        self.year = year
    }
    
    convenience init(withTitle title: String) {
        self.init(withTitle: title, andYear: 2017)
    }
}

class DigitalMovie: Movie {
    var medium: String
    
    override init(withTitle title: String, andYear year: Int) {
        self.medium = "Digital"
        super.init(withTitle: title, andYear: year)
    }
}

let aMovie = DigitalMovie(withTitle: "Be Creative")

print("\(aMovie.title) : \(aMovie.medium)") // prints: Be Creative : Digital

Here we added a new property, we don’t have a default value, and we’re overriding a designated initializer of our superclass. We can see that we can use the superclass convenience initializer when creating an instance of the ‘DigitalMovie’ class.

Required Initializers

If you want every subclass to implement a certain initializer, just mark it with the ‘required’ keyword. For example:

class Movie {
    let title: String
    var year: Int
    var story: String?
    
    required init(withYear year: Int) {
        self.title = "No Title"
        self.year = year
    }
    
    init(withTitle title: String, andYear year: Int) {
        self.title = title
        self.year = year
    }
    
    convenience init(withTitle title: String) {
        self.init(withTitle: title, andYear: 2017)
    }
}

class DigitalMovie: Movie {
    var medium: String
    
    required init(withYear year: Int) {
        self.medium = "Digital"
        super.init(withYear: year)
    }
    
    override init(withTitle title: String, andYear year: Int) {
        self.medium = "Digital"
        super.init(withTitle: title, andYear: year)
    }
}

We have a required initializer in the ‘Movie’ class and we had to implement it in our ‘DigitalMovie’ class as well.

Failable Initializers

When you’re creating your objects, maybe you don’t have all the data that you need, or some of it is invalid. What ever your case is, you have an option to abort object initialization. You can easily do this with a failable initializer. Failable initializers have a question mark after the init keyword, you must have seen it all around the place. You can fail at any time during the initialization by returning nil. Technically speaking, swift initializers don’t return a value (unlike objective-c). Let’s see a quick example of a failable initializer:

class Movie {
    let title: String
    var year: Int
    var story: String?
    
    init?(withTitle title: String, andYear year: Int) {
        if year < 1900 {
            return nil
        }
        
        self.title = title
        self.year = year
    }
}

let aMovie = Movie(withTitle: "Be Creative", andYear: 1800)

print("\(aMovie?.title) : \(aMovie?.year)") // prints: nil : nil

We’re checking for a year here, and if it’s less than 1900 we fail the initialization. Notice how ‘aMovie’ property is now an optional type.

This initializer will create an optional instance of a ‘Movie’ type. You could also define your initializer to return an implicitly unwrapped instance of a ‘Movie’ type by using the exclamation mark operator:

class Movie {
    let title: String
    var year: Int
    var story: String?
    
    init!(withTitle title: String, andYear year: Int) {
        if year < 1900 {
            return nil
        }
        
        self.title = title
        self.year = year
    }
}

let aMovie: Movie = Movie(withTitle: "Be Creative", andYear: 1800) // fatal error
print("\(aMovie.title) : \(aMovie.year)")

Throwable Initializers

Another type of a failable initializer is a throwable initializer. Perhaps for your use case it would make more sense to throw an error. Especially if you want to provide a bit more details about why the initialization failed. We can modify our example a bit so it would throw an error:

enum MovieError: Error {
    case InvalidYear
    case TitleTooShort
}

class Movie {
    let title: String
    var year: Int
    var story: String?
    
    init(withTitle title: String, andYear year: Int) throws {
        if year < 1900 {
            throw MovieError.InvalidYear
        }
        
        if title.characters.count < 3 {
            throw MovieError.TitleTooShort
        }
        
        self.title = title
        self.year = year
    }
}

do {
    let aMovie: Movie = try Movie(withTitle: "Be Creative", andYear: 1800)
    print("\(aMovie.title) : \(aMovie.year)")
} catch {
    print("Failed to create a movie: \(error)")
}

// prints: Failed to create a movie: InvalidYear

I won’t go into details about error handling in swift, because it’s a topic on its own. You can see from the example that you’re getting a lot more details now when your initialization fails.

Memberwise Initializers in Structs

These initializers are specific for structures. If you don’t define your custom initializers in structs you get a free initializer that will take in all the properties of the struct as parameters and initialize it. Let’s change our example a bit to see this in action:

struct Movie {
    let title: String
    var year: Int
    var story: String?
}

let aMovie = Movie(title: "Be Creative", year: 1999, story: nil)

print("\(aMovie.title) : \(aMovie.year)") // prints: Be Creative : 1999

You will get this free initializer even if you provide default values for your properties, unless one of your properties is a constant, then you get nothing šŸ™‚

Deinitializers

What better way to end a post than with a deinit šŸ™‚ Deinitializers are quite simple. They are methods that take no parameters and will be called just before your class is deallocated, they are available only on class types. You can use them to perform your final cleanups before your class is destroyed. For example, you can remove yourself as an observer if you registered for notifications. You can’t call deinit directly, and your superclass deinit is called just before your own deinit implementation returns. Let’s see this on a quick example:

class Movie {
    let title: String
    var year: Int
    var story: String?
    
    init(withTitle title: String, andYear year: Int) {
        self.title = title
        self.year = year
    }
    
    convenience init(withTitle title: String) {
        self.init(withTitle: title, andYear: 2017)
    }
    
    deinit {
        print("This is the end")
    }
}

class DigitalMovie: Movie {
    var medium: String
    
    override init(withTitle title: String, andYear year: Int) {
        self.medium = "Digital"
        super.init(withTitle: title, andYear: year)
    }
    
    deinit {
        print("Goodbye world")
    }
}

func makeAndBrakeAMovie() {
    let aMovie = DigitalMovie(withTitle: "Be Creative")
    print("\(aMovie.title) : \(aMovie.medium)")
}

makeAndBrakeAMovie()

// prints
// Be Creative : Digital
// Goodbye world
// This is the end

Here we can see that our deinit is being called first, and then the super deinit is being called automatically.

Conclusion

Object construction might seem like a simple topic, but it can be quite complex. There are quite a few rules to follow in swift, but once you understand them you’ll have no problems. In this article we covered the basics, and I hope I shed some light on object construction (and destruction) in swift.

Have a nice day šŸ™‚

Dejan.

More resources

5 thoughts on “Initializers in Swift

      1. Antonio Tanzola

        It’s not true that a class must have a `required` initializer. A class must have at least one designated initializer – either a `default` one or one that you provide. That’s the only required thing.

        A good example of when the use of ‘required’ initializers is needed is when a class implements a protocol that lists some initializers. In this case any initializers listed in the protocol must be ‘required’ – this is necessary to guarantee that any subclasses will still provide all the initializers needed to keep conforming to that protocol.

        Reply
  1. Sanjay Kumar Morya

    class Movie {
    let title: String
    var year: Int
    var story: String?

    init(withTitle title: String, andYear year: Int) {
    self.title = title
    self.year = year
    }

    convenience init(withTitle title: String) {
    self.init(withTitle: title, andYear: 2017)
    }
    }

    class DigitalMovie: Movie {
    var medium: String

    override init(withTitle title: String, andYear year: Int) {
    self.medium = ā€œDigitalā€
    super.init(withTitle: title, andYear: year)
    }
    }

    let aMovie = DigitalMovie(withTitle: ā€œBe Creativeā€)

    print(ā€œ\(aMovie.title) : \(aMovie.medium)ā€)

    Here we added a new property, we donā€™t have a default value, and weā€™re overriding a designated initializer of our superclass. We can see that we can use the superclass designated initializer (Not convenience) when creating an instance of the ā€˜DigitalMovieā€™ class. So Need To correct blog here.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.