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 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.
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.
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.
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.
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.
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.
| Characteristic | Delegate | NotificationCenter | Closure |
|---|---|---|---|
| Communication type | 1:1 | 1:N | 1:1 |
| Coupling | Weak (through protocol) | Very weak (string key) | Medium (context capture) |
| Type safety | Full | None (Any?) | Full |
| Retain cycle risk | No (weak) | No | Yes (self capture) |
| When to use | Complex callbacks with multiple methods | Events that interest many | Simple 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.
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.
@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
}
@endThe 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.
Delegate usage mistakes lead to memory leaks, app crashes, and non-obvious bugs. Let's look at the five most common problems.
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.
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.
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.
Apple's system APIs actively use the Delegate pattern. Let's look at three key examples from UIKit that appear in every iOS application.
| API | Protocol | Key Methods |
|---|---|---|
| UITableView | UITableViewDelegate | didSelectRowAt, heightForRowAt, willDisplay |
| UITextField | UITextFieldDelegate | shouldChangeCharactersIn, didBeginEditing, shouldReturn |
| URLSession | URLSessionDelegate | didReceiveChallenge, 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
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.
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.
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.
@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.
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
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.
Read also