Introduction
In swift we are unable to tag a func
as required
like we can do with initializers.
This is annoying and make sometimes our code not completely developer-friendly.
What I have
class Building {
var defaultLights: Lights!
func makeItBeautiful() {
// do some stuff here
turnOnLights()
}
func turnOnLights() {
// turn on default lights....
defaultLights.turnOn()
turnOnCustomLights()
}
func turnOnCustomLights() {
fatalError("This method must be overriden") // π€
}
}
class BigBuilding: Building {
var customLights: Lights!
// and even more custom element from `BigBuilding`
override func turnOnCustomLights() {
// turn on custom lights
customLights.turnOn()
}
}
In StackOverFlow we can read thousands of topics talking about this kind of case, all solved with fatalError(_:)
solution.
What we can do using protocols
protocol CustomizableBuilding {
func turnOnCustomLights()
}
class Building: NSObject {
var defaultLights: Lights!
func makeItBeautiful() {
// do some stuff here
turnOnLights()
}
func turnOnLights() {
// turn on default lights....
defaultLights.turnOn()
if self.conforms(to: CustomizableBuilding) { // π±
(self as! Customizable).turnOnCustomLights() // π€’
}
}
}
class BigBuilding: Building, CustomizableBuilding {
var customLights: Lights!
func turnOnCustomLights() {
// turn on custom lights
customLights.turnOn()
}
}
In both ways, inheritage is not self-sufficient, we need to add some hack to alert the developer that he forgot some piece of code.
On the first solution we have to think about overriding the method turnOnCustomLights
.
On the second solution, we have to think about implementing the protocol CustomizableBuilding
each time we inherit from Building
.
This is not completely developer friendly, we can forget it.
Alternative way using protocols
protocol Building {
var defaultLights: Lights!
func makeItBeautiful()
func turnOnLights()
func turnOnCustomLights()
}
extension {
func makeItBeautiful() {
updateUI()
}
func turnOnLights() {
turnOnCustomLights()
}
}
Assuming that Building
won't be used alone, protocol
is the good way.
But Imagine that I would like to use Building
as a class
and giving me the opportunity to subclass
it if needed, you can do it in better way by adding the required
keyword as you will see below.
Proposed solution
class Building {
var defaultLights: Lights!
func makeItBeautiful() {
// do some stuff here
turnOnLights()
}
func turnOnLights() {
// updateUI....
// defaultElement
turnOnCustomLights()
}
required func turnOnCustomLights() { /* no custom lights with that building */ }
}
class BigBuilding: Building { // π΄ error: You must implement `updateCustomUI()`
var customLights: Lights!
}
var π = Building()
π .makeItBeautiful() // turn on default lights
var π’ = BigBuilding()
π’.makeItBeautiful() // turn on default lights and custom lights even simpler that before πͺ
Conclusion
Adding required
keyword to all methods can bring more readability for the developer.
It will automatically raise an error on the subclass
.
It also gives use the way to use the class without subclassing it. This is not an abstract
class.