Protocol Oriented Programming(POP): A Beginner Guide

Uplift iOS Interview

The Guide is for YOU if
  • You are preparing for an iOS interview and want to improve your skills and knowledge and looking to level up your interview game and land your dream job.
  • You want to gain confidence and ease during iOS interviews by learning expert tips and curated strategies.
  • You want access to a comprehensive list of iOS interview QA to practice and prepare.

Protocol Oriented Programming (POP) is a programming paradigm that emphasizes the use of protocols to define interfaces between components in a software system. It is a popular approach used by Swift developers to build flexible, modular, and reusable code. In this beginner guide, we will explore the key concepts of POP, its benefits, and how to apply it in Swift.

What is Protocol-Oriented Programming?

Protocol-oriented programming is a programming paradigm that emphasizes the use of protocols to define interfaces between different components in a software system. It is based on the idea that the behavior of a type is more important than its inheritance hierarchy or implementation details.

In protocol-oriented programming, protocols are used to define the behavior that a type should conform to. A protocol is a set of methods and properties that define a particular behavior. Types that conform to a protocol provide implementations for these methods and properties, which allows them to interact with other types that also conform to the same protocol.

Protocol-oriented programming also emphasizes the use of value types, which are types that are copied when they are assigned or passed as arguments. This contrasts with reference types, which are passed by reference and can be shared by multiple components.

By using protocols and value types, protocol-oriented programming can result in more modular and composable code, since types can be easily swapped out or composed together as needed. It can also lead to more efficient code, since value types are generally faster to copy and manipulate than reference types.

In traditional Object-Oriented Programming (OOP), objects are defined by their inheritance hierarchy. On the other hand, in POP, objects are defined by the behavior they exhibit, i.e., by the protocols they conform to. Protocols define a blueprint for methods, properties, and other requirements that a type must implement.

Advantages of Protocol-Oriented Programming

POP has several advantages, including:

Flexibility

Protocols are more flexible than inheritance because they can be adopted by any class, struct, or enum that conforms to the protocol, regardless of its inheritance hierarchy. This means that you can use protocols to define behavior across multiple types, rather than being limited to a single inheritance hierarchy.

Composition over Inheritance

Protocols encourage composition over inheritance. Rather than creating complex inheritance hierarchies, protocols enable you to define a set of behaviors or functionality that can be adopted by any class or struct that conforms to the protocol. This approach makes it easier to create smaller, more focused types, and it reduces the risk of tightly coupled code.

Testability

Protocols promote testability because they enable you to use dependency injection, which allows you to replace real objects with mock objects during testing. This means you can test the functionality of a type that depends on a protocol without needing to use real objects that may not be suitable for testing.

Code Reusability

Protocols promote code reusability because they enable you to create small, reusable components that can be used in multiple contexts. By defining behavior through protocols, you can create objects that are highly composable, which reduces code duplication and makes your code more maintainable.

Type Safety

Protocols promote type safety because they enable you to define a clear contract between different types. This means that you can ensure that the objects you are working with adhere to a certain set of rules or requirements, which reduces the risk of runtime errors and improves the reliability of your code.

Clean code

It’s a core aspect of Object-Oriented Programming (OOP) that emphasizes the importance of protocols in achieving abstraction, inheritance, polymorphism, and encapsulation. Instead of relying solely on class hierarchy trees, the protocol-first approach uses protocols as a foundation for achieving the SOLID principles.

The Issue of Rigid Coupling in Inheritance

In iOS development, inheritance is a powerful tool for creating relationships between classes, but it can also lead to rigid coupling, which is when changes to one part of the code require changes to other parts of the code that are tightly interconnected.

Inheritance is a mechanism for creating a subclass that inherits properties and methods from its parent class. While this can be useful for organizing code and sharing functionality, it can also create a rigid coupling between the parent and child classes. If the parent class changes, the child class must also change, and any other parts of the code that rely on the child class may need to change as well. This can make the code more difficult to maintain and change over time.

In contrast, other design patterns such as composition or protocols offer more flexibility and can help avoid rigid coupling. With composition, objects are created by combining smaller, independent objects together, rather than inheriting from a parent object. And with protocols, objects can conform to a set of requirements without inheriting from a parent object, allowing for more flexibility in the types of objects that can be used.

One drawback of inheritance is the restriction imposed by some object-oriented languages on multiple inheritances. This limitation exists for valid reasons, particularly due to the diamond problem.

Certainly, it does not imply that you must eliminate inheritance under all circumstances. Inheritance is still a valuable concept and can be justifiable. For instance, it’s still reasonable to assume that a UILabel inherits from UIView.

Inheritance vs POP

  • Inheritance allows you to inherit from a superclass, while protocol extensions enable interface reuse.
  • In Swift, both classes and value types like structures and enums can adopt multiple protocols and inherit the default implementation from them. This is similar to the concept of multiple inheritance found in other programming languages.
  • Unlike base classes and inheritance, which are only available for classes(Class is a reference type. Reference type may cause code unsafe. e.g. Processing collection of reference types while they are being modified.), protocols can be adopted by classes, structures, and enums. This means that you can use protocols to define common behavior across different types, regardless of their underlying implementation. By adopting multiple protocols, you can build more flexible and reusable code that’s easier to maintain and extend over time.
  • With inheritance, you can override methods while still maintaining invariants. Protocols, on the other hand, let you implement requirements and override defaults.
  • Inheritance allows for implementation reuse by inheriting from a superclass, whereas protocols offer implementation reuse by adopting protocols.
  • Inheritance is not possible with value types, but protocols can be adopted by both value types and reference types.
  • Inheritance requires upfront modeling and exclusive hierarchies, whereas protocols enable retroactive modeling and ad-hoc hierarchies.

How to Apply Protocol-Oriented Programming in Swift

Now that we know what POP is and its benefits, let’s see how to apply it in Swift.

1. Define Protocols

The first step in applying POP is to define protocols that describe the behavior of objects. Protocols define the methods, properties, and other requirements that a type must implement to conform to the protocol.

protocol Vehicle {
    var wheels: Int { get }
    func startEngine()
    func stopEngine()
}

2. Conform to Protocols

The next step is to conform to the protocols defined. Conforming to a protocol means implementing the requirements defined in the protocol.

struct Car: Vehicle {
    var wheels: Int = 4
    func startEngine() {
        print("Starting the car engine")
    }
    func stopEngine() {
        print("Stopping the car engine")
    }
}

3. Use Protocol Extensions

Protocol extensions allow you to define default implementations for methods and properties defined in a protocol. This can reduce the amount of boilerplate code needed and make the code more concise.

extension Vehicle {
    func drive() {
        print("Driving the vehicle with \(wheels) wheels")
    }
}

4. Combine Protocols/Protocol Composition

Sometimes in iOS development, you might want to require a type to conform to multiple protocols at once. This is where combine protocol comes in handy! Combine Protocols is a way of combining multiple protocols into a single requirement, almost like creating a temporary protocol that has all the requirements of the original protocols.

For example: Protocols can be combined to create larger protocols that describe more complex behavior.

protocol AmphibiousVehicle: Vehicle {
    var waterPropulsion: Bool { get }
    func startWaterPump()
    func stopWaterPump()
}

protocol Vibration {
   func lessVibrate()
}

typealias AmphibiousVibratedVehicle = AmphibiousVehicle & Vibration

struct AmphibiousCar: AmphibiousVibratedVehicle { ... }

When you create a protocol composition, it doesn’t actually define any new protocol types. Instead, it simply lists the protocols that you want to combine using the ampersand symbol (&). Protocol compositions can include as many protocols as you need, all separated by ampersands. Overall, protocol composition is a useful tool for creating flexible and modular code. By combining multiple protocols, you can create more powerful requirements for your types and ensure that they meet all the necessary criteria for your app or program to function correctly.

POP in Swift and SwiftUI

Swift and SwiftUI are two technologies that heavily rely on protocol-oriented programming.

Have you ever heard of inheritance hierarchies? They can get pretty massive, with the ancestor classes carrying the bulk of the generalized functionality and the leaf subclasses contributing very little. It’s like the ancestor classes are doing all the heavy lifting, while the descendants are just adding a dash of spice. Take the example of a Vehicle it drives, transports passengers, and shows navigation. But when you break it down, these are all different functionalities that are clumped together into one big mess. This can make it challenging to reuse code. For instance, my macbook also presents navigation, but it’s not a Vehicle. So, inheriting the navigate function from the Vehicle class isn’t possible.

That is why Swift encourages breaking down these large monolithic functionalities into smaller traits that can be easily reused. Both a Vehicle and a Macbook can use navigation traits. In Swift, protocols are a fundamental language feature and are used extensively throughout the standard library and in many third-party frameworks. Swift also includes powerful features such as protocol extensions and protocol inheritance, which allow for even more flexible and modular code.

In SwiftUI, protocols are used to define the behavior of views, which are the building blocks of user interfaces in the framework. SwiftUI includes several built-in protocols for defining the behavior of views, such as the View protocol, which defines the basic interface for a view, and the ObservableObject protocol, which defines an object that can be observed for changes.

In addition, SwiftUI makes extensive use of value types such as structs and enums, which are copied when they are passed around in code. This allows for more efficient code and can help prevent issues such as unexpected shared state.

Overall, protocol-oriented programming is a key part of the design philosophy of both Swift and SwiftUI, and plays a crucial role in enabling their flexible and composable architectures.

Retroactive Modeling in POP

Imagine you’re designing a program or app, and you’re faced with the daunting task of figuring out all the different types of entities or concepts you’ll need to represent. You might feel like you’re being forced to make decisions about these key abstractions before you even know what you really need.

But fear not! With retroactive modeling, you can break free from the constraints of inflexible inheritance hierarchies. Swift protocols and protocol extensions allow you to start with concrete code and then work your way up to abstractions when you need to assign roles or personas to different parts of your program.

Retroactive modeling is essentially the art of using existing types to represent new concepts, without having to change those original types. This technique is super important for reusing existing structures while still being able to work with what’s currently being used. And thanks to Swift extensions, you can even add new functionality to existing types without needing to access the original source code.

Think of Swift extensions as tools that let you supercharge your existing code, whether it’s a class, struct, enum, or protocol. It’s kind of like discovering a secret stash of power-ups that you can use to make your program or app even better.

And the best part? Retroactive modeling means you don’t have to make those tough decisions about key abstractions until you’re ready. So go ahead, design with confidence and creativity!

To continue learning more about protocols, read the official Swift documentation. Also, Take a look at an excellent WWDC session on protocol-oriented programming on Apple’s dev portal.



✍️ Written by Ishtiak Ahmed

👉 Follow me on XLinkedIn



Get Ready to Shine: Mastering the iOS Interview




Enjoying the articles? Get the inside scoop by subscribing to my newsletter.

Get access to exclusive iOS development tips, tricks, and insights when you subscribe to my newsletter. You'll also receive links to new articles, app development ideas, and an interview preparation mini book.

If you know someone who would benefit from reading this article, please share it with them.