Делегат — это паттерн проектирования, который позволяет одному объекту (делегату) реагировать на события или выполнять задачи от имени другого объекта. Это реализация принципа инверсии зависимостей (D из SOLID).
// 1. Определяем протокол делегата
protocol DataLoaderDelegate: AnyObject {
func dataDidLoad(_ data: [String])
func dataDidFail(with error: Error)
}
class DataLoader {
// 2. Объявляем слабую ссылку на делегат
weak var delegate: DataLoaderDelegate?
func loadData() {
// Имитация загрузки данных
DispatchQueue.global().async {
let success = Bool.random()
DispatchQueue.main.async {
if success {
let data = ["Item1", "Item2", "Item3"]
// 3. Вызываем метод делегата при успехе
self.delegate?.dataDidLoad(data)
} else {
let error = NSError(domain: "com.example.error", code: 500)
// 3. Вызываем метод делегата при ошибке
self.delegate?.dataDidFail(with: error)
}
}
}
}
}
class ViewController: UIViewController {
let loader = DataLoader()
override func viewDidLoad() {
super.viewDidLoad()
// 4. Устанавливаем делегат
loader.delegate = self
loader.loadData()
}
}
// 5. Реализуем протокол делегата
extension ViewController: DataLoaderDelegate {
func dataDidLoad(_ data: [String]) {
print("Данные загружены: \(data)")
// Обновляем UI
}
func dataDidFail(with error: Error) {
print("Ошибка загрузки: \(error.localizedDescription)")
// Показываем ошибку
}
}
Синтаксис weak var delegate
:
weak
для предотвращения retain cycleAnyObject
)Опциональные методы:
@objc protocol CustomTableViewDelegate: AnyObject {
@objc optional func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
}
UIKit компоненты:
UITableViewDelegate
, UICollectionViewDelegate
UITextFieldDelegate
, UIScrollViewDelegate
Системные сервисы:
CLLocationManagerDelegate
URLSessionTaskDelegate
Жизненный цикл:
UIApplicationDelegate
UINavigationControllerDelegate
✅ Четкое разделение ответственности
✅ Типобезопасность (благодаря протоколам)
✅ Поддержка опциональных методов
✅ Понятный стек вызовов
❌ Может привести к Massive View Controller
❌ Один делегат на объект (в отличие от нотификаций)
❌ Сложнее для наблюдения за множеством объектов
class DataLoader {
var onDataLoaded: (([String]) -> Void)?
var onError: ((Error) -> Void)?
func loadData() {
// ...
onDataLoaded?(data)
}
}
// Использование:
loader.onDataLoaded = { data in
print(data)
}
class DataLoader {
let dataSubject = PassthroughSubject<[String], Error>()
func loadData() {
// ...
dataSubject.send(data)
}
}
// Подписка:
loader.dataSubject
.sink(receiveCompletion: { /* ошибка */ },
receiveValue: { /* данные */ })
.store(in: &cancellables)
weak
class MyViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
}
extension MyViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Обработка нажатия
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60.0
}
}
Делегаты — это фундаментальный паттерн iOS разработки, который:
Для простых задач можно использовать замыкания, для сложных — комбинировать делегаты с другими паттернами. Главное — соблюдать правила (weak делегаты, понятные протоколы) и использовать паттерн там, где он действительно уместен.
Помните: "Если ваш ViewController реализует 10+ протоколов делегатов — возможно, стоит пересмотреть архитектуру."