Rock of Gibraltar (Had the chance to actually live there) — Source : Wikipedia

Rock SOLID Swift

Yotam Golomb
The Startup

--

As a mobile developer i assume you ponder from time to time what differentiates good code and not-so-good code. (I am less likely to call it bad code or sh*tty code since someone worked on it and it probably does what it is supposed to)

One of the criteria in my opinion is “Does the code follows SOLID principles”. For those who don’t remember, SOLID is :

  1. Single responsibility principle: a class should have only a single responsibility (i.e. changes to only one part of the software’s specification should be able to affect the specification of the class).
  2. Open/closed principle: software entities … should be open for extension, but closed for modification.
  3. Liskov substitution principle: objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.
  4. Interface segregation principle: many client-specific interfaces are better than one general-purpose interface.
  5. Dependency inversion principle: one should “depend upon abstractions, [not] concretions.”

let’s dive.

(1) Single responsibility principle

When you create/change a class, you should ask yourself: How many responsibilities does this class have?

let’s say you need something in your project that sends an API request, parses the response and saves it locally. One option is this :

At a glance, this looks good. it’s readable, understandable, also maintainable to some degree. However — this class has 3 responsibilities (API Call, Parsing, Save to Local DB) , thus violates the first principle . A better implementation looks like this :

And if we really want to do this the Swift way :

(2) Open/closed principle

In simple term, Open for extension but closed for modification. Classes should be open for extending the functionality of the class but should not allow to touch/modify the existing functionality. In the real world it is hard to follow this as time goes by and code changes, but is best to revise previously written classes and see if they meet the product demand and perhaps needs to be replaced, but under the same principle.

Let’s say i wish to add to the Handler class the capability to draw a loading animation while the API call isn’t finished. Luckily we have the best keyword in the world of Swift : “extension” !
(10/07 Edit : I have revised the code sample so that we can include a stored variable in a swift extension. plz comment if more is needed)

(3) Liskov Substitution Principle

Named after Barbara Liskov who first presented this principle in 1987, it means that 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.). In layman terms, it means that we must make sure that the new derived classes are extending the base class without changing their behavior. This principle is foundation for building code that is maintainable and reusable. It is also an extension of Open-Closed principle when you come to think of it.

For example : we have to types of house animals : Dog and Cat. Both of them greet their owner with a specific type of hello.

Wherever in our code we were using HouseAnimal class object we must be able to replace it with the Dog or Cat without exploding our code. What do we mean here is that child class (sub-class) should not implement code such that if we replace parent class with child class then the application will stop running. For ex. if the following WeirdAlien class replaces HouseAnimal by thinking that this is will also have all the attributes of HouseAnimal class - our app will crash.

FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT.

(4) Interface Segregation Principle

This principle states that clients should not be forced to implement interfaces they don’t use. Instead of one fat interface many small interfaces are preferred based on groups of methods, each one serving one submodule.

For example : Let’s say we have two types of developers — a junior developer and an senior developer. They both produce code and have lunch when they are at work.

Now let’s say we have a 3rd type of worker, a new robot that also codes. Better than the senior.

Forcing the Robot to eat is bad practice, creates a polluted interface and is against the Interface Segregation Principle. A better way to do things is to separate the writing code protocol and the eating lunch protocols and then the Robot will implement only what is needed — which is writing code.

(5) Dependency Inversion Principle

This principle refers to a specific form of decoupling software modules. These involves to key things to remember :

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.

B. Abstractions should not depend on details. Details should depend on abstractions.

For example : you need to implement a Database component in your application. There are many frameworks and libraries to use for Local Database in your Application (CoreData , Realm etc). If you built your app properly using principles and architecture, changing under the hood will be simple.

Now let’s say we have to use Realm as a Local Database Framework :

You can do this because protocols are less specific than classes. A class has a specific name and specific methods you can use. On the other hand, protocols are, by definition, abstract. More than one class can implement a protocol, making them perfect for reducing coupling.

Summary

These 5 principles, to me, are the cornerstone for the correct way to build applications (not just mobile, any type of client). Since Swift is Protocol Oriented — the LID part of SOLID is just that. With SOLID you defeat 3 enemies of code :

  • Fragility: A change may break unexpected parts — it is very difficult to detect if you don’t have a good test coverage.
  • Immobility: A component is difficult to reuse in another project — or in multiple places of the same project — because it has too many coupled dependencies.
  • Rigidity: A change requires a lot of efforts because affects several parts of the project.

Thanx for reading, i hope you find this useful. Happy Coding!

--

--

Yotam Golomb
The Startup

Black-Belted Mobile Architect and Leader, Certified LeSS Practitioner. Head of Mobile Guild & RnD Manager @ Augury