SOLID Principles for Swift

Let’s learn in-depth (With Code Examples)

Mehmet Ateş
7 min readJul 23, 2022
Photo by Tyler Lastovich on Unsplash

First of all, what exactly is SOLID?

SOLID Abbreviation for 5 software principles. Let’s unpack these 5 principles.

  • The Single-responsibility principle
  • The Open–closed principle
  • The Liskov substitution principle
  • The Interface segregation principle
  • The Dependency inversion principle

It’s pretty cool. You can be sure that I will explain the following for each principle;

  • What is this principle?
  • Why do we use it?
  • How do we implement this in Swift?

Let’s dive deeper into them and look at their use with Swift. Our first stop.

Single-responsibility principle (SRP)

  • What is this SRP?

Says that a well-defined class can only have one responsibility, which is in full accordance with its name. This class can only be changed for a single purpose, and that purpose is the responsibility of this class.

  • Why do we use SRP?

If a class has a responsibility;

Testability increases. Because the purpose of the class is clear. Both the number of class-related tests is reduced, and the creation of tests is easier.

Clean and readable code. Since classes have less responsibility, the amount of code a class has will decrease. This means reducing complexity and being able to edit quickly according to your purpose. Goodbye spaghetti codes.

Easy detection of errors. Since there is only one responsible for the defined class if there is a problem fulfilling that responsibility, the first place to check is clear. This allows us to troubleshoot and resolve issues at an incredible speed.

High variability. If the class has only one responsibility, you can change the variables that affect that responsibility as you wish. Because this will not directly affect other classes.

  • How do we implement SRP in Swift?

Let’s first look at a poorly constructed class ❌;

It does indeed seem like correct, but its responsibility is to maintain both the game information and the version of the game.

Let’s create a new version class and update our class accordingly ✅.

In this way, the version model can be used directly in services and the responsibility of the game class is divided. Changes made to the game class will not affect the version class at all.

Also, we will not depend on the game class for version updates ✅.

Open–closed principle (OCP)

  • What is this OCP?

This principle tells us: Open to developing a class model function and similar programming parts should be Closed to change. This will mean: The class can expand and grow. But its direction does not change.

  • Why do we use OCP?

Preserves readability. Each object develops its properties, which makes it neat and readable in use. You will understand very well in the example, be patient.

Increases sustainability. Any functionality currently in use will not be affected, as we cannot modify the existing code. Think of it like you’re securing a method you’ve used 50 times. What happens when it changes, you decide.

Unlimited development. It is difficult to predict at the very beginning how much an entity can evolve. Futures shouldn’t be a problem, as open-closed liberates it.

  • How do we implement OCP in Swift?

Let’s imagine that we are a car company and we have a structure that calculates how much fuel our cars consume on a kilometer basis. We have three types of vehicles for now, but in the future, we want to make five types, and we will develop them.

Bad ❌

When we add a new type, we have to edit the function. So our structure is not developing, it is changing.

Better, But not best ✅

When we look at our current code, no matter how many vehicle types we add, our function will not change and it will do the calculation. The not best part I mentioned above is related to the last principle, Dependency inversion. Let’s keep going and don’t stop until we get the best.

Liskov substitution principle (LSP)

  • What is this LSP?

This principle says that subclasses can perform the same function even if they are replaced by main classes. And in addition, a derived class should be able to use all the properties of the class from which it is derived.

It is also a special case of the open-closed principle at this initial stage. So first make sure you understand it.

  • Why do we use LSP?

Flexibility. This is already the basis of the principle. Classes can change among themselves and transactions continue without any problems.

Code Security. Flexibility can often be perceived as more insecure. But as long as Liskov is applied correctly, this will bring security as well as flexibility.

  • How do we implement LSP in Swift?

It’s a difficult principle to understand. But the examples will be very helpful. We have a protocol called a computer and it has 2 functions. But as you will notice, computers cannot perform the same operations when they are swapped.

Bad ❌

Indicates that this class breaks the principle. If computers can open Xcode, they should be able to do so on Windows.

Good

Here we will be confused now. Well, when the computers change, Windows still won’t be able to open Xcode? This is exactly where we need to pay attention. The Computer protocol no longer includes Xcode. So if we need pc we are sure it will just open VSCode. And if we change it, both will do the job. If what we need is Xcode, we should do it using the Xcode protocol. In this way, a structure derived from the computer protocol will not be suitable for this and only computers with Xcode can be used.

Let me give you an example using these classes.

I hope it’s more in your head now. Let’s continue.

Interface segregation principle (ISP)

  • What is this ISP?

This protocol tells us that we should separate responsibilities as much as possible rather than lump them together. It’s a complex sentence. The questions about why we should use it and how we use it will illuminate this.

  • Why do we use ISP?

Eliminate unnecessary implementation. Owned responsibility can also be functions or values that are not used in the class. Which means necessities that will be written in vain.

Legibility. This principle can almost eliminate unnecessary code. That means you’ve deleted everything you don’t need. I guess this is what is called clean code.

  • How do we implement ISP in Swift?

For example, something like this should happen here. It should be our main link and its sub-functions. But our structures connected to our main link shouldn’t need all of these sub-functions. Animals are one of the best examples of this.

Bad ❌

The moment you see the code, I’m sure you’re saying: This is pretty wrong. It certainly is. Check out these useless uses. Isn’t it necessary for the shark to have a running function?

Better, But not best ✅

Take a look at this code. All wasted use is gone and it is now very orderly. But are you aware of the problem that occurs? Are none of them an animal anymore? We can solve this too.

Best ✅

Now they’re all animals again. You can control it as an animal only, whether you want to, depending on its specific characteristics. It depends on your usage needs.

Dependency inversion principle (DIP)

  • What is this DIP?

This principle, one of my favorites, tells us: The dependency of a structure should always be reduced to a minimum. Life cycles must be separate from each other. Superclasses should not depend on subclasses.

  • Why do we use DIP?

Memory Usage. If a superclass and a subclass are closely linked, the superclass is created in the subclass the moment it is created. Even if it’s not going to be used at all. Considering how important resource usage is, especially for mobile devices, it is a very important reason.

Flexibility. Getting rid of dependencies means making structures completely free. As long as they play by the rules, nothing goes wrong. They have to follow the Rules and you write the Rules.

Again and again, Readability. Since our codes are written regularly, it now conforms to a standard and readability reaches maximum levels.

  • How do we implement DIP in Swift?

I will use the computer example, which is one of my favorite examples in this regard.

Bad ❌

When you examine the code, you will understand that the HDD cannot live in the program without a Computer. And the computer is also connected to the HDD. When it wants to perform operations on another disk, for example, SSD it cannot do this. This is called Strict Adherence.

Good ✅

Life cycles are no longer interconnected. The computer can work with any type of disk and the disk can be independent of the computer with any computer it wants.

And we complete this principle with the usage code.

Summary and closing

As you noticed, our code layout started to conform to a standard as we started from the first principle and continued. And the principles support each other very well. They almost come to each other’s aid. In this article, I tried to explain and exemplify them as much as possible.

Thank you for reading.🙂 Lots of Swift days. 🧡

--

--