Swift Protocols

This chapter explores swift-protocols , a cornerstone of protocol-oriented programming (POP). Protocols define a blueprint of methods, properties, and other requirements that conforming types must implement. They enable flexible and reusable code, making Swift highly adaptable for various programming paradigms.

Chapter Goals

  • Understand what protocols are and their role in Swift programming.
  • Learn how to define and adopt protocols.
  • Explore advanced features like protocol inheritance, associated types, and extensions.
  • Implement real-world examples using protocols.

Key Characteristics of Swift Protocols

  • Blueprint of Requirements: Define methods, properties, and requirements without implementation.
  • Conformance: Types adopt protocols by implementing their requirements.
  • Protocol Inheritance: Protocols can inherit from other protocols.
  • Extensions: Add default implementations and functionality to protocols.
  • Associated Types: Provide type placeholders for conforming types.

Basic Rules for Protocols

  • Use the protocol keyword to define a protocol.
  • Define methods and properties without implementation.
  • Mark conforming types with a : followed by the protocol name.
  • Use extensions to provide default implementations or additional functionality.

Syntax Table

Serial No Feature Syntax/Example Description
1 Protocol Declaration protocol ProtocolName { … } Declares a new protocol with requirements.
2 Adopting a Protocol struct TypeName: ProtocolName { … } A type conforms to a protocol.
3 Protocol with Properties var propertyName: Type { get set } Requires conforming types to implement properties.
4 Protocol with Methods func methodName() { … } Requires conforming types to implement methods.
5 Protocol Inheritance protocol SubProtocol: SuperProtocol { … } A protocol inherits from another protocol.
6 Protocol Extensions extension ProtocolName { … } Adds default implementation to a protocol.
7 Associated Types associatedtype TypeName Declares a type placeholder in a protocol.

Syntax Explanation

1. Protocol Declaration

What is a Protocol Declaration?

A protocol declaration defines a blueprint for methods, properties, and requirements.

Syntax

protocol Drawable {

    func draw()

}

 

Detailed Explanation

  • Use the protocol keyword followed by the protocol name.
  • Define methods and properties without implementation.
  • Protocols act as contracts for conforming types.

Example

protocol Describable {

    func describe() -> String

}

 

Example Explanation

  • Declares a Describable protocol requiring a describe method.

2. Adopting a Protocol

What is Adopting a Protocol?

Types conform to a protocol by implementing its requirements.

Syntax

struct Circle: Drawable {

    func draw() {

        print(“Drawing a circle”)

    }

}

 

Detailed Explanation

  • Use : to specify protocol conformance.
  • Implement all required methods and properties.
  • A type can conform to multiple protocols by separating them with commas.

Example

struct Square: Drawable {

    func draw() {

        print(“Drawing a square”)

    }

}

 

let shape: Drawable = Square()

shape.draw()

 

Example Explanation

  • Implements the Drawable protocol in the Square struct.
  • Invokes the draw method defined in the protocol.

3. Protocol with Properties

What is a Protocol with Properties?

Protocols can define properties that conforming types must implement.

Syntax

protocol Identifiable {

    var id: String { get }

}

 

Detailed Explanation

  • Define properties with get or get set to specify read-only or read-write requirements.
  • Conforming types must implement these properties.

Example

struct User: Identifiable {

    var id: String

}

 

let user = User(id: “12345”)

print(user.id)

 

Example Explanation

  • Declares an Identifiable protocol with a read-only id property.
  • Implements the protocol in the User struct.

4. Protocol with Methods

What is a Protocol with Methods?

Protocols can define method requirements for conforming types.

Syntax

protocol Greetable {

    func greet()

}

 

Detailed Explanation

  • Define methods without implementation.
  • Conforming types must provide implementations for these methods.

Example

struct Robot: Greetable {

    func greet() {

        print(“Hello, I am a robot.”)

    }

}

 

let bot = Robot()

bot.greet()

 

Example Explanation

  • Implements the Greetable protocol in the Robot struct.
  • Calls the greet method on an instance of Robot.

5. Protocol Inheritance

What is Protocol Inheritance?

A protocol can inherit requirements from another protocol.

Syntax

protocol Movable {

    func move()

}

 

protocol Flyable: Movable {

    func fly()

}

 

Detailed Explanation

  • Use : to inherit from one or more protocols.
  • The inheriting protocol includes all requirements of its parent protocols.

Example

struct Airplane: Flyable {

    func move() {

        print(“The airplane moves on the runway.”)

    }

 

    func fly() {

        print(“The airplane is flying.”)

    }

}

 

Example Explanation

  • Implements the Flyable protocol in the Airplane struct.
  • Provides implementations for both move and fly methods.

6. Protocol Extensions

What are Protocol Extensions?

Protocol extensions add default implementations for protocol methods.

Syntax

extension Greetable {

    func greet() {

        print(“Hello from default implementation!”)

    }

}

 

Detailed Explanation

  • Use extension to add methods or computed properties to a protocol.
  • Conforming types inherit the default implementation unless overridden.

Example

struct Human: Greetable {}

 

let person = Human()

person.greet()

 

Example Explanation

  • Provides a default implementation of greet in a Greetable extension.
  • The Human struct inherits and uses this implementation.

7. Associated Types

What are Associated Types?

Associated types define placeholders for types used in protocol requirements.

Syntax

protocol Container {

    associatedtype Item

    func add(_ item: Item)

    func count() -> Int

}

 

Detailed Explanation

  • Use associatedtype to declare a placeholder type.
  • Conforming types specify the actual type when implementing the protocol.

Example

struct IntContainer: Container {

    typealias Item = Int

    private var items: [Int] = []

 

    func add(_ item: Int) {

        items.append(item)

    }

 

    func count() -> Int {

        return items.count

    }

}

 

Example Explanation

  • Implements the Container protocol in IntContainer with Int as the associated type.
  • Defines methods to add items and count them.

Real-Life Project: Payment Processing System

Project Goal

Create a payment processing system using protocols to handle different payment methods.

Code for This Project

protocol PaymentMethod {

    func processPayment(amount: Double)

}




struct CreditCard: PaymentMethod {

    func processPayment(amount: Double) {

        print("Processing credit card payment of $\(amount)")

    }

}




struct PayPal: PaymentMethod {

    func processPayment(amount: Double) {

        print("Processing PayPal payment of $\(amount)")

    }

}




func makePayment(using method: PaymentMethod, amount: Double) {

    method.processPayment(amount: amount)

}




let card = CreditCard()

makePayment(using: card, amount: 100.0)




let paypal = PayPal()

makePayment(using: paypal, amount: 200.0)

Steps

  1. Define a PaymentMethod protocol with a processPayment method.
  2. Implement the protocol in CreditCard and PayPal types.
  3. Create a makePayment function to process payments using the protocol.

Save and Run

Steps to Save and Run

  1. Write the code in your Swift IDE (e.g., Xcode).
  2. Save the file using Command + S (Mac) or the appropriate save command.
  3. Click “Run” or press Command + R to execute the program.

Benefits

  • Demonstrates protocol-oriented design.
  • Simplifies adding new payment methods.
  • Highlights polymorphism through protocol conformance.

Best Practices

Why Use Protocols?

  • Enable flexible and reusable code.
  • Promote polymorphism and abstraction.
  • Encourage composition over inheritance.

Key Recommendations

  • Use protocols to define shared behavior across unrelated types.
  • Leverage protocol extensions for default implementations.
  • Adopt associated types for generic and adaptable requirements.

Example of Best Practices

protocol Resettable {

    func reset()

}

 

extension Resettable {

    func reset() {

        print(“Default reset behavior”)

    }

}

 

struct Timer: Resettable {}

let timer = Timer()

timer.reset()

 

Insights

Swift protocols provide a powerful mechanism for abstraction and polymorphism. By combining protocols with extensions and associated types, developers can design flexible, modular, and reusable code.

Key Takeaways

  • Protocols define blueprints for shared behavior.
  • Use protocol extensions for default implementations.
  • Associated types enhance protocol flexibility and adaptability.