Protocols and Extensions

Protocols and Extensions


Protocols and extensions are essential features of Swift that enhance code modularity, flexibility, and reusability. Protocols define a blueprint of methods, properties, or other requirements, while extensions allow you to add functionality to existing types.


35. Protocol Definition and Conformance


What is a Protocol?


protocol in Swift is a blueprint of methods, properties, and other requirements that a type must implement. Protocols enable polymorphism and are central to protocol-oriented programming.


Defining a Protocol


protocol Vehicle {

    var speed: Double { get set }

    func move()

}


Properties: Protocols specify properties, but the actual implementation is left to conforming types.

Methods: Protocols declare methods without implementation.


Conforming to a Protocol


Types (classes, structs, or enums) that conform to a protocol must implement all its requirements.


Example: Class Conformance


class Car: Vehicle {

    var speed: Double = 0.0

    func move() {

        print("Car is moving at \(speed) km/h")

    }

}


let myCar = Car()

myCar.speed = 100

myCar.move() // Output: Car is moving at 100.0 km/h


Example: Struct Conformance


struct Bicycle: Vehicle {

    var speed: Double

    func move() {

        print("Bicycle is moving at \(speed) km/h")

    }

}


let myBike = Bicycle(speed: 15)

myBike.move() // Output: Bicycle is moving at 15.0 km/h


Protocol Inheritance


A protocol can inherit from other protocols.


protocol Drivable: Vehicle {

    var isElectric: Bool { get }

}


class ElectricCar: Drivable {

    var speed: Double = 0.0

    var isElectric: Bool = true


    func move() {

        print("Electric car is moving silently at \(speed) km/h")

    }

}


Optional Requirements (for @objc Protocols)


Optional protocol methods can be declared using the @objc keyword.


@objc protocol DataSource {

    @objc optional func fetchData()

}


class MyDataSource: DataSource {

    func fetchData() {

        print("Data fetched!")

    }

}


36. Extensions


What is an Extension?


Extensions in Swift allow you to add new functionality to existing types (classes, structs, enums, or protocols) without modifying the original source code.


Adding Methods with Extensions


Example: Adding a Method to Int


extension Int {

    func squared() -> Int {

        return self * self

    }

}


print(4.squared()) // Output: 16


Adding Computed Properties


Example: Adding a Property to String


extension String {

    var isPalindrome: Bool {

        return self == String(self.reversed())

    }

}


print("level".isPalindrome) // Output: true


Adding Protocol Conformance with Extensions


Extensions can make a type conform to a protocol.


protocol Printable {

    func printInfo()

}


extension Int: Printable {

    func printInfo() {

        print("This is an integer: \(self)")

    }

}


5.printInfo() // Output: This is an integer: 5


Nested Extensions


Extensions can include nested types to group related functionality.


extension Array {

    struct Summary {

        let count: Int

        let isEmpty: Bool

    }


    func summary() -> Summary {

        return Summary(count: self.count, isEmpty: self.isEmpty)

    }

}


let array = [123]

let summary = array.summary()

print(summary.count) // Output: 3


37. Protocol-Oriented Programming


What is Protocol-Oriented Programming?


Protocol-oriented programming (POP) is a design paradigm in Swift where protocols define the core behavior of your application. Instead of relying heavily on inheritance (OOP), you compose behaviors through protocol conformance.


Advantages of POP

1. Flexibility: Types can conform to multiple protocols, avoiding rigid inheritance hierarchies.

2. Modularity: Behaviors are decoupled and reusable.

3. Code Consistency: Protocols enforce consistent behavior across different types.


Protocol Extensions


Protocol extensions provide default implementations for protocol methods, enabling shared behavior across conforming types.


Example: Default Implementation


protocol Drawable {

    func draw()

}


extension Drawable {

    func draw() {

        print("Default drawing")

    }

}


struct Circle: Drawable {}

struct Square: Drawable {}


Circle().draw()  // Output: Default drawing

Square().draw()  // Output: Default drawing


Combining Protocols


Example: Composing Multiple Protocols


protocol Flyable {

    func fly()

}


protocol Swimmable {

    func swim()

}


struct Bird: Flyable, Swimmable {

    func fly() {

        print("Bird is flying")

    }


    func swim() {

        print("Bird is swimming")

    }

}


let penguin = Bird()

penguin.fly()  // Output: Bird is flying

penguin.swim() // Output: Bird is swimming


Generic Constraints with Protocols


Example: Using where with Protocols


func printDrawable<TDrawable>(_ item: T) {

    item.draw()

}


38. Delegation Pattern


What is the Delegation Pattern?


The delegation pattern is a design pattern where one object (the delegator) delegates responsibility for a task to another object (the delegate). It is commonly implemented using protocols in Swift.


Components of the Delegation Pattern

1. Delegator: The object that delegates a task.

2. Delegate: The object that performs the task.

3. Protocol: Defines the task to be performed.


Example of Delegation


Step 1: Define a Protocol


protocol TaskDelegate: AnyObject {

    func taskDidComplete(message: String)

}


Step 2: Create a Delegator Class


class Task {

    weak var delegate: TaskDelegate?


    func performTask() {

        print("Task is being performed...")

        delegate?.taskDidComplete(message: "Task completed successfully!")

    }

}


Step 3: Conform to the Protocol


class TaskHandler: TaskDelegate {

    func taskDidComplete(message: String) {

        print(message)

    }

}


Step 4: Set the Delegate


let task = Task()

let handler = TaskHandler()


task.delegate = handler

task.performTask()

// Output:

// Task is being performed...

// Task completed successfully!


Real-World Example: UITableView Delegation


In UIKit, UITableView uses the delegation pattern for tasks like rendering cells.

1. ProtocolUITableViewDelegate

2. Delegator: The UITableView object.

3. Delegate: The view controller implementing UITableViewDelegate.


Advanced Features of Delegation

1. Using @objc for Optional Methods:


@objc protocol OptionalDelegate {

    @objc optional func optionalMethod()

}



2. Avoid Retain Cycles with weak:

Always declare delegates as weak to prevent strong reference cycles.

3. Custom Delegates with Associated Types:


protocol GenericDelegate {

    associatedtype Data

    func handle(data: Data)

}



Comments

Popular posts from this blog

Complete iOS Developer Guide - Swift 5 by @hiren_syl |  You Must Have To Know 😎 

Debugging

Swift Fundamentals You Can’t Miss! A List by @hiren_syl