Delegate: what it is, delegation pattern and iOS protocols

Author: IT Sectr Published: 2026-02-17 Reading time: 9 min

Delegate is a design pattern where one object delegates task execution to another object. In iOS, the pattern is implemented through Swift protocols and @protocol Objective-C. Delegation is one of the fundamental Cocoa Touch patterns, used in UITableViewDelegate, UITextFieldDelegate and hundreds of other Apple APIs. According to Apple Developer Documentation (2025), about 70% of UIKit system classes use delegates for behavior customization without inheritance.

Key Takeaways

  • Delegate — a pattern where object A delegates task execution to object B through a protocol or interface
  • Swift Protocol defines a set of methods that the delegate may or must implement
  • Weak reference to the delegate is mandatory to prevent retain cycles and memory leaks
  • UITableViewDelegate — the most common example of a delegate in iOS with 20+ optional methods
  • Difference from Observer: delegate is a 1:1 relationship, while notifications work on a 1:N principle

What is the Delegate Pattern?

Delegate is a behavioral design pattern that allows an object to delegate part of its responsibilities to another object. Unlike inheritance, where a child class overrides parent methods, the delegate uses composition: the owner object holds a reference to the delegate and calls its methods at specific points.

The delegate defines a protocol — a set of methods that the delegate can implement. Methods are divided into required and optional. In Swift, optional protocol methods are marked with the @objc optional keyword.

How the Delegate Works

Object A (owner) contains a delegate property — a weak reference to object B (delegate). When an event occurs, A checks whether B implements the corresponding protocol method and calls it. Weak reference is mandatory: without it, the delegate cannot be deallocated from memory because the owner holds it with a strong reference.

Swift
protocol LoaderDelegate: AnyObject {
    func loaderDidStart(_ loader: DataLoader)
    func loader(_ loader: DataLoader, didLoad data: Data)
    func loader(_ loader: DataLoader, didFailWith error: Error)
}

class DataLoader {
    weak var delegate: LoaderDelegate?
    
    func start() {
        delegate?.loaderDidStart(self)
        // asynchronous loading
    }
}

The DataLoader class defines the LoaderDelegate protocol and calls delegate methods at key points in the loading lifecycle. AnyObject guarantees that the protocol can only be implemented by classes — this is necessary for the weak reference.

How Does Delegate Work in Swift?

Delegate implementation in Swift includes three steps: declaring the protocol, creating a weak delegate property in the owner, and implementing the protocol in the delegate class. Let's look at a custom UITextField with validation as an example.

Swift
protocol ValidatorDelegate: AnyObject {
    func validate(_ input: String) -> Bool
    func validatorDidFail(_ input: String)
}

class ValidatedTextField: UITextField {
    weak var validator: ValidatorDelegate?
    
    override func textDidChange() {
        guard let text = self.text else { return }
        if validator?.validate(text) == false {
            validator?.validatorDidFail(text)
            self.layer.borderColor = UIColor.red.cgColor
        }
    }
}

class LoginViewController: UIViewController, ValidatorDelegate {
    let textField = ValidatedTextField()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        textField.validator = self
    }
    
    func validate(_ input: String) -> Bool {
        return input.count >= 6
    }
    
    func validatorDidFail(_ input: String) {
        print("Validation failed: input too short")
    }
}

LoginViewController implements the ValidatorDelegate protocol and sets itself as the delegate for textField. On each text change, ValidatedTextField calls validate(_:), and if validation fails — validatorDidFail(_:). The controller acts as an intermediary between the View and the validation logic.

Delegate vs NotificationCenter vs Closure

Choosing between delegate, notifications, and closures depends on the number of recipients and component coupling. Each mechanism solves the problem of inter-object communication, but with different trade-offs.

CharacteristicDelegateNotificationCenterClosure
Communication type1:11:N1:1
CouplingWeak (through protocol)Very weak (string key)Medium (context capture)
Type safetyFullNone (Any?)Full
Retain cycle riskNo (weak)NoYes (self capture)
When to useComplex callbacks with multiple methodsEvents that interest manySimple closures with 1-2 callbacks

Delegate is optimal when you need to pass a series of related events to a single recipient. NotificationCenter is better for broadcast notifications. Closure is for simple asynchronous operations, such as completion handlers in URLSession.

Delegate Implementation in Objective-C

Objective-C uses @protocol and @optional for declaring delegates. Unlike Swift, all protocol methods are optional by default. The key difference is the respondsToSelector: call before sending a message to the delegate, since the method may not be implemented.

Objective-C
@protocol ImageCacheDelegate 
@optional
- (void)cacheDidStartDownload: (ImageCache *)cache;
- (void)cache: (ImageCache *)cache didCacheImage: (UIImage *)image;
@required
- (void)cache: (ImageCache *)cache didFailWithError: (NSError *)error;
@end

@interface ImageCache : NSObject
@property (nonatomic, weak) id<ImageCacheDelegate> delegate;
- (void)downloadImageAtURL: (NSURL *)url;
@end

@implementation ImageCache
- (void)downloadImageAtURL: (NSURL *)url {
    if ([self.delegate respondsToSelector:@selector(cacheDidStartDownload:)]) {
        [self.delegate cacheDidStartDownload:self];
    }
    // asynchronous image loading
}
@end

The key difference in Objective-C: before calling an optional method, a respondsToSelector: check is required. In Swift, optional protocol methods eliminate this check — optional chaining (?.) automatically handles the absence of implementation.

Common Mistakes When Working with Delegates

Delegate usage mistakes lead to memory leaks, app crashes, and non-obvious bugs. Let's look at the five most common problems.

Strong Reference to the Delegate

Retain cycle is the most common mistake. If the delegate property is declared as strong, and the delegate in turn owns the owner object, a retain cycle is formed. Both objects will never be deallocated from memory. Solution: always declare the delegate as weak var in Swift or @property (weak) in Objective-C.

Delegate Not Nullified After Dealloc

If the owner object outlives the delegate and the reference remains, calling the delegate method will result in EXC_BAD_ACCESS. Weak reference solves this problem automatically: after the delegate is deallocated, the property becomes nil. However, in multithreaded scenarios, you should additionally check the delegate on the main thread.

Excessive Number of Methods in the Protocol

A protocol with 20+ methods violates the Interface Segregation Principle (ISP). UITableViewDelegate contains about 30 optional methods — this is a historical exception. In your own protocols, it's better to split responsibility into several smaller protocols, each with its own role.

Delegate Examples in iOS System APIs

Apple's system APIs actively use the Delegate pattern. Let's look at three key examples from UIKit that appear in every iOS application.

APIProtocolKey Methods
UITableViewUITableViewDelegatedidSelectRowAt, heightForRowAt, willDisplay
UITextFieldUITextFieldDelegateshouldChangeCharactersIn, didBeginEditing, shouldReturn
URLSessionURLSessionDelegatedidReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError

Each of these protocols implements different aspects of behavior: UITableViewDelegate manages appearance and touch response, UITextFieldDelegate controls text input, URLSessionDelegate handles network events. This demonstrates the flexibility of the pattern: the delegate can be adapted to any area of responsibility.

Frequently Asked Questions

What is the difference between delegate and dataSource in iOS?

Delegate manages behavior and appearance (cell height, touch response). DataSource provides data (number of rows, cell contents). In UITableViewDelegate and UITableViewDataSource — these are two separate protocols that divide responsibility for presentation and data.

Why is delegate declared as weak?

Weak reference prevents retain cycles. The owner (e.g., UITableView) holds a reference to the delegate only as weak. If the delegate (UIViewController) owns the table, a strong reference to the delegate would create a cycle: ViewController → UITableView → Delegate (ViewController). Weak breaks this cycle.

Can delegate be used in SwiftUI?

In SwiftUI, the Delegate pattern is used less often — it's replaced by @Binding, @State and closures. However, delegate is still used for UIKit integration through UIViewRepresentable. For example, MKMapViewDelegate and WKUIDelegate remain relevant when wrapping UIKit components in SwiftUI.

What is @objc optional in Swift protocols?

@objc optional allows declaring optional methods in a Swift protocol. This is a compatibility mechanism with the Objective-C runtime. Without @objc, all Swift protocol methods are mandatory by default. Optional is used in UIKit protocols where the delegate may only implement the methods it needs.

How many delegates can one object have?

One object can have only one delegate for each delegate property. If you need to notify multiple objects, use multicast delegate, an array of delegates, or NotificationCenter. The Delegate pattern is originally designed as a 1:1 relationship.

Summary

  • Delegate — a behavioral design pattern that transfers task execution from one object to another through a protocol
  • Swift Protocol declares a set of methods; optional methods are marked with @objc optional, required methods are always implemented
  • Weak reference to the delegate is mandatory to prevent retain cycles and memory leaks
  • UITableViewDelegate is the most well-known example; its methods manage cell height, selection, and display
  • Delegate differs from NotificationCenter with a 1:1 versus 1:N relationship and full type safety
  • Objective-C requires respondsToSelector: check for optional methods, Swift handles this automatically
  • Recommendation: use delegate for complex callbacks with multiple related methods and closure for simple closures

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also