When you override a methods you should call super implementation of this method.
required override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
The constant let
values can be set only in initializers. If you have more than 1 initializer think how you can chain them. It's rely useful to have 1 designated initialiser and many convenience. In this way you would set you variables only in 1 place. Example
class A {
let age: Int
let name: String
let type: String
init(age: Int, name: String, type: String) {
self.age = age
self.type = type
self.name = name
}
convenience init() {
self.init(age: 0, name: "", type: "")
}
convenience init(age: Int) {
self.init(age: age, name: "", type: "")
}
convenience init(age: Int, name: String) {
self.init(age: age, name: name, type: "")
}
In swift you have to fully initialize class in init method. It means you have to set values for all your properties and call super init, if it exist. After that you can access self and call methods.
In the Cocoa Framework classes have more then 1 way to instantiate them and have many initialiser. Example UIViewController has
init(nibName: String?, bundle: NSBundle?)
and init(coder :NSCoder)
How we would initialize this class ?
- Set Variables value
This solution has 1 huge disadvantage - It's not DRY
You have the same code in 2 places. If you need to change it or fix it, you need to do the same in 2 places. Really bad architecture and code smell.
var name: String
required init(coder aDecoder: NSCoder) {
name = "Name"
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
name = "Name"
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
Can we do better? The solution looks obvious - Extract name initialisation in separate method. But we can't do that because of Swift class safety rule
Initialization Steps
- Set all you properties
- Call super.init(...)
- Now both your class and super class has been fully initialized. Only here you can access self and call methods on self
Code:
required init(coder aDecoder: NSCoder) {
setDefaultName() // Error. Can't access self yet
super.init(coder: aDecoder)
}
func setDefaultName() {
name = "Name"
}
- Default Value
Set default value for variable.
This looks better because we initialize property only in one place.
var name: String = ""
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
If you need more complicated logic to initialize your variable you can use closure for that.
I don't like that because now you have logic (a closure in the same as function) in you class variable declaration.
Can we do better? Yes we can!
var name: String = {
var name = "Name"
// ....
// Other complicated logic
name + " Is Best"
return name
}()
- Factory Methods
It would be better to take that closure and move it outside class variable definition,
so we can do something like that var name = Factory.defaultName()
Swift support inner classes. You can create a class inside a class. This way you can group some functionality inside a class. This sound perfect to group initialisers methods into Factory class
Final Example:
var name: String = Factory.defaultName()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
class Factory {
class func defaultName () -> String {
return "Name"
}
}