Паттерны проектирования в Swift: от теории к практике
Паттерны проектирования — это проверенные решения типовых задач в разработке. Для iOS-разработчика на Swift владение паттернами — это не просто плюс в резюме, а необходимость. Они помогают писать чистый, переиспользуемый и тестируемый код. В этой статье мы разберем 5 ключевых паттернов с реальными примерами на Swift.
1. Singleton (Одиночка)
Один из самых популярных паттернов в iOS. Гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему.
Реализация в Swift
class NetworkManager {
static let shared = NetworkManager()
private init() {} // предотвращаем создание других экземпляров
func requestData(from url: URL) {
// логика запроса
print("Запрос к \(url.absoluteString)")
}
}
// Использование
NetworkManager.shared.requestData(from: URL(string: "https://api.example.com")!)
Когда использовать: для управления общими ресурсами (сеть, база данных, UserDefaults). Минус: усложняет юнит-тестирование, поэтому злоупотреблять не стоит.
2. Factory Method (Фабричный метод)
Определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта. Отлично подходит, когда заранее неизвестно, какой именно объект понадобится.
Пример: создание UI-элементов
protocol Button {
func render()
}
class iOSButton: Button {
func render() { print("Кнопка в стиле iOS") }
}
class macOSButton: Button {
func render() { print("Кнопка в стиле macOS") }
}
enum Platform {
case iOS, macOS
}
class ButtonFactory {
static func createButton(for platform: Platform) -> Button {
switch platform {
case .iOS:
return iOSButton()
case .macOS:
return macOSButton()
}
}
}
// Использование
let button = ButtonFactory.createButton(for: .iOS)
button.render() // Кнопка в стиле iOS
3. Observer (Наблюдатель)
Позволяет объектам подписываться на события и получать уведомления об изменениях. В Swift нативно реализован через NotificationCenter и Combine.
Пример с NotificationCenter
extension Notification.Name {
static let userDidLogin = Notification.Name("userDidLogin")
}
class AuthManager {
func login() {
// логика входа
NotificationCenter.default.post(name: .userDidLogin, object: nil)
}
}
class ProfileViewController {
init() {
NotificationCenter.default.addObserver(
self,
selector: #selector(updateUI),
name: .userDidLogin,
object: nil
)
}
@objc func updateUI() {
print("Обновляем профиль после входа")
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
Современная альтернатива: используйте Combine (iOS 13+) с @Published и PassthroughSubject для реактивного программирования.
4. Delegate (Делегат)
Один из фундаментальных паттернов Cocoa Touch. Позволяет одному объекту делегировать выполнение задач другому. Широко используется в UIKit (например, UITableViewDelegate).
Создание собственного делегата
protocol DownloadDelegate: AnyObject {
func didFinishDownloading(data: Data)
func didFailWithError(error: Error)
}
class Downloader {
weak var delegate: DownloadDelegate?
func download(from url: URL) {
// имитация загрузки
let data = Data()
delegate?.didFinishDownloading(data: data)
}
}
class ViewController: UIViewController, DownloadDelegate {
let downloader = Downloader()
override func viewDidLoad() {
super.viewDidLoad()
downloader.delegate = self
downloader.download(from: URL(string: "https://example.com/file")!)
}
func didFinishDownloading(data: Data) {
print("Файл загружен: \(data.count) байт")
}
func didFailWithError(error: Error) {
print("Ошибка: \(error.localizedDescription)")
}
}
Важно: свойство delegate должно быть weak, чтобы избежать цикла сильных ссылок (retain cycle).
5. MVVM (Model-View-ViewModel)
Хотя MVVM — это архитектурный паттерн, он тесно связан с проектированием. Отлично работает со SwiftUI и Combine.
Пример на SwiftUI
// Модель
struct User {
let name: String
let age: Int
}
// ViewModel
class UserViewModel: ObservableObject {
@Published var userName: String = ""
@Published var userAge: String = ""
func loadUser(_ user: User) {
userName = user.name
userAge = "\(user.age) лет"
}
}
// View
struct UserView: View {
@StateObject var viewModel = UserViewModel()
let user = User(name: "Анна", age: 28)
var body: some View {
VStack {
Text("Имя: \(viewModel.userName)")
Text("Возраст: \(viewModel.userAge)")
}
.onAppear {
viewModel.loadUser(user)
}
}
}
Заключение
Паттерны проектирования — это инструменты, а не догмы. В Swift и iOS-разработке особенно важно понимать, когда какой паттерн применить. Singleton удобен для глобального состояния, но не злоупотребляйте им. Factory Method помогает создавать гибкие иерархии объектов. Observer и Delegate — основа событийно-ориентированного программирования в Apple-экосистеме. А MVVM с SwiftUI — современный стандарт для чистого разделения ответственности.
Практикуйтесь, пишите код и со временем выбор правильного паттерна станет интуитивным. Удачи в разработке!