Delegate in Swift


Introduction to Delegates in Swift

A delegate in Swift is a design pattern that allows one object to communicate with another in a loosely coupled manner. It is widely used in iOS development for handling:

  • Custom Callbacks
  • TableView & CollectionView Delegation
  • Passing Data Between View Controllers

This guide explains Swift Delegates with practical examples.

1. What is a Delegate?

A delegate is a protocol-based communication pattern. It allows a class (delegate) to handle tasks on behalf of another class (delegator).

Key Characteristics of Delegates:

  • Uses a protocol to define the communication.
  • Assigns a delegate property in the delegating class.
  • The delegate class implements the protocol to handle tasks.

2. Creating a Delegate in Swift


Step 1: Define a Protocol

A protocol defines the methods the delegate must implement.

protocol TaskDelegate {
     func taskCompleted()
}

Step 2: Create a Delegating Class

This class has a delegate property to call methods from the delegate.

class TaskManager {
     var delegate: TaskDelegate? // Weak reference prevents retain cycles

     func startTask() {
         print("Task started")
         // Simulating task completion
         delegate?.taskCompleted()
     }
}

Step 3: Implement the Delegate in Another Class

The class adopting the protocol implements the required method.

class Worker: TaskDelegate {
     func taskCompleted() {
         print("Task completed by Worker")
     }
}

// Assigning the delegate
let taskManager = TaskManager()
let worker = Worker()

taskManager.delegate = worker
taskManager.startTask()

Output

Task started
Task completed by Worker

Loose coupling: TaskManager does not directly depend on Worker.

3. Passing Data with Delegates (ViewController Example)

Delegates are commonly used for passing data between view controllers.

Step 1: Define the Delegate Protocol

protocol DataPassingDelegate {
     func sendData(_ data: String)
}

Step 2: Assign Delegate in First View Controller

import UIKit

class FirstViewController: UIViewController {
     var delegate: DataPassingDelegate?

     func sendDataToSecondVC() {
         delegate?.sendData("Hello from FirstViewController")
     }
}

Step 3: Implement Delegate in Second View Controller

class SecondViewController: UIViewController, DataPassingDelegate {
     func sendData(_ data: String) {
         print("Received data: \(data)")
     }
}

Step 4: Set the Delegate Before Navigation

let firstVC = FirstViewController()
let secondVC = SecondViewController()

firstVC.delegate = secondVC
firstVC.sendDataToSecondVC()

Output

Received data: Hello from FirstViewController

Efficient data transfer between ViewControllers.

4. Using Delegates in UITableView

Delegates are used in UITableView to handle row selection, scrolling, and cell rendering.

Implementing TableView Delegate Methods

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
     let tableView = UITableView()

     override func viewDidLoad() {
         super.viewDidLoad()

         tableView.delegate = self
         tableView.dataSource = self
         view.addSubview(tableView)
     }

     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         return 3
     }

     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = UITableViewCell()
         cell.textLabel?.text = "Row \(indexPath.row + 1)"
         return cell
     }

     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         print("Selected Row: \(indexPath.row + 1)")
     }
}

Output (On Row Selection):

Selected Row: 1

UITableViewDelegate handles user interactions.
UITableViewDataSource manages data display.

5. Weak Delegate to Avoid Retain Cycles

Using weak for the delegate prevents memory leaks.

protocol DownloadDelegate: AnyObject { // Use AnyObject for weak reference
     func downloadFinished()
}

class Downloader {
     weak var delegate: DownloadDelegate? // Avoids strong reference cycle

     func startDownload() {
         print("Downloading...")
         delegate?.downloadFinished()
     }
}

class ViewController: DownloadDelegate {
     func downloadFinished() {
         print("Download completed")
     }
}

let downloader = Downloader()
let viewController = ViewController()

downloader.delegate = viewController
downloader.startDownload()

weak var delegate prevents retain cycles.

6. Real-World Use Cases of Delegates

  • Handling User Interactions (Button Clicks, TableView Selections)
  • Data Transfer Between View Controllers
  • Custom UI Component Communication
  • Network Callbacks & Background Task Completion

7. When to Use Delegates Over Closures?

Feature Delegate Closure
Multiple Methods Best for multiple callbacks Limited
Loose Coupling Decouples classes Tightly coupled
Lightweight No memory overhead Can cause retain cycles
Complex Workflows Better for structured communication Hard to manage

Use Delegates for structured multiple callbacks.
Use Closures for one-time execution.

Conclusion

Delegates in Swift provide a powerful and flexible way to communicate between objects. They are widely used in UIKit, data transfer, and event handling.