The Liskov Substitution Principle and Swift

Subscribe to my newsletter and never miss my upcoming articles

Hello ladies and gentlemen, Leo here.

Today subject of study is the Liskov Principle implementation on Swift and why that is important to you as an iOS developer.

First we have to say that Liskov principle was created by Barbara Liskov. She is an amazing woman who is one of the first woman in United States to achieve a Doctorate in Computer Science and is a Turing Award winner. For more information her wikipedia's article. You may find more at her webpage.

The Liskov Substitution Principle is described by:

Substitutability is a principle in object-oriented programming stating that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of the program (correctness, task performed, etc.).

So let's see how Swift implement this principle. Let's code!

protocol Food {} // 1
extension Food {
    func display() { print("Yeap it's food") }
}

protocol Fruit: Food {} //2
extension Fruit {
    func display() { print("Now, its fruit..") }
}

struct FingerFood: Food {} //3
struct FingerFoodFruit: Fruit{} // This is a Fruit and a Food type.

First we are creating two protocols Food and Fruit. Fruit inherit all the Food capabilities, in this example the display func. So and also overwrite the Food's func display giving it a new implementation. And at the final part we have two structs that extends the Food and the Fruit protocol.

If you run the struct's methods the results would be:

FingerFood().display()
FingerFoodFruit().display()

Screen Shot 2020-08-10 at 17.06.24.png

The FingerFoodFruit is at the same time the type food and the type Fruit. This way we can use the Liskov principle to pass it to a func that receives a Food like the code below:

let classicFood = FingerFood()
let apple = FingerFoodFruit()

func setFood(_ food: Food) { // 1
    print("food loaded")
}

setFood(classicFood)
setFood(apple)

Screen Shot 2020-08-10 at 17.14.30.png

Now we start to use the Liskov principle. We are passing the apple which is the Fruit type but because it's inherited the super-type of Food protocol too we can pass it to the method.

Now let's see something more interesting. What if we set a variable of type Food but assign to it a real type Fruit object, what the display method will print?

let strangeFood : Food = FingerFoodFruit() // an food type variable with a Fruit object inside...
strangeFood.display() // what????

Screen Shot 2020-08-10 at 17.20.06.png

Why is that? The Swift compiler can only guarantee that the display method of the TYPE of the VARIABLE, this way we can assure the type safe and everybody goes happy to home...

But wait, what if we wanted to access the display method of the child rather than the super-type display method. Well in Swift we have some ways to do that and all include the as reserved word. Check below:

let strangeFood : Food = FingerFoodFruit() // a Food variable with a Fruit Object inside...
(strangeFood as Fruit).display() // 1 -this doesn't work... but why? 
(strangeFood as! Fruit).display() // 2 -this works...
(strangeFood as? Fruit)?.display() // 3- this works too...

Commenting the line that doesn't work we have this output:

Screen Shot 2020-08-10 at 17.33.53.png

The number 1 doesn't work because the as operator checks in compile time if the variable is convertible to the type we are trying to. At the time I'm writing this article Swift doesn't has a automatic downcast in compile time. But as you can see you can force downcast in runtime to the type you are trying to work to, this way we have access to the Fruit behaviour of the display method.

And you are ask yourself... If I can force downcast, can I upcast the fruit variable to access the super-type display method behaviour? Yes, you can and all thanks to the Liskov Principle.

let apple = FingerFoodFruit() // its a Fruit variable with a Fruit object inside...
(apple as Food).display() // now this is safe in compile time because all Fruit types are also Food types

And the output: Screen Shot 2020-08-10 at 17.40.33.png

Well, this was a little introduction to the Barbara Liskov Substitution Principle and I hope you have enjoyed discovering this language tricks like I did. And please go check other Barbara's works she is an amazing scientist.

Thanks for the reading and... That's all folks!

ps: and yes, it's all about the L in the legendary SOLID acronym. Cover image credit: Cody O’Loughlin / Quanta Magazine.

No Comments Yet