Introducción: Por qué SOLID es importante para el desarrollador de Swift
En el mundo del desarrollo iOS, crear código mantenible y escalable es una tarea clave. Los principios SOLID, formulados por Robert Martin, siguen siendo el estándar de oro de la arquitectura de software. A nivel avanzado, el dominio de estos principios en Swift permite no solo escribir código limpio, sino también construir sistemas flexibles y resistentes a los cambios de requisitos. En este artículo, analizaremos cada principio con ejemplos en Swift, enfocándonos en problemas reales del desarrollador iOS/macOS.
1. Principio de Responsabilidad Única (SRP) en Swift
Teoría: Cada clase o estructura debe tener una sola razón para cambiar. En la práctica, esto significa que un módulo se encarga de una tarea claramente definida.
Problema: ViewController sobrecargado
En UIKit, es común encontrar controladores que gestionan la UI, procesan solicitudes de red y guardan datos al mismo tiempo. Esto viola el SRP.
// ❌ Violación de SRPclass UserViewController: UIViewController { @IBOutlet weak var tableView: UITableView! private var users: [User] = []
override func viewDidLoad() { super.viewDidLoad() fetchUsers() }
private func fetchUsers() { // Solicitud de red URLSession.shared.dataTask(with: URL(string: "https://api.example.com/users")!) { data, _, _ in // Parseo de JSON // Guardado en Core Data DispatchQueue.main.async { self.tableView.reloadData() } }.resume() }}Solución: separación de responsabilidades
Extraemos la gestión de datos y red a servicios separados.
// ✅ SRP: servicio separado para trabajar con usuariosprotocol UserServiceProtocol { func fetchUsers(completion: @escaping (Result<[User], Error>) -> Void)}
class UserService: UserServiceProtocol { func fetchUsers(completion: @escaping (Result<[User], Error>) -> Void) { // Lógica de solicitud de red }}
// ✅ SRP: gestor separado para Core Dataclass CoreDataManager { func saveUsers(_ users: [User]) { // Guardado en Core Data }}
// ✅ SRP: ViewController solo gestiona la UIclass UserViewController: UIViewController { private let userService: UserServiceProtocol private let coreDataManager: CoreDataManager
init(userService: UserServiceProtocol, coreDataManager: CoreDataManager) { self.userService = userService self.coreDataManager = coreDataManager super.init(nibName: nil, bundle: nil) }
override func viewDidLoad() { super.viewDidLoad() loadUsers() }
private func loadUsers() { userService.fetchUsers { [weak self] result in switch result { case .success(let users): self?.coreDataManager.saveUsers(users) self?.updateUI(with: users) case .failure(let error): self?.showError(error) } } }}2. Principio Abierto/Cerrado (OCP) y protocolos en Swift
Teoría: Las entidades de software (clases, módulos) deben estar abiertas para extensión, pero cerradas para modificación. En Swift, esto se logra mediante protocolos y herencia.
Ejemplo: sistema de notificaciones
// ❌ Violación de OCP: al agregar un nuevo tipo de notificación, hay que modificar el switchclass NotificationSender { func send(_ notification: Notification, type: String) { switch type { case "email": // Envío de email case "push": // Envío de push default: break } }}
// ✅ OCP: usamos un protocoloprotocol NotificationChannel { func send(_ notification: Notification)}
class EmailChannel: NotificationChannel { func send(_ notification: Notification)