Principios SOLID en Swift: teoría y práctica de la A a la Z

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción a los principios SOLID en Swift

\n

Cada desarrollador de iOS, tarde o temprano, se enfrenta al problema del "código espagueti": las clases se hinchan, los cambios en un lugar rompen toda la aplicación y las pruebas se convierten en una pesadilla. La solución a estos problemas se encuentra en la base del diseño orientado a objetos: los principios SOLID. Estas cinco reglas, formuladas por Robert Martin, ayudan a crear código flexible, mantenible y escalable en Swift. En este artículo, analizaremos cada principio desde la teoría hasta ejemplos prácticos para iOS/macOS.

\n

Independientemente de si escribes en UIKit, SwiftUI o AppKit, SOLID es una base universal. Profundicemos en los detalles.

\n\n

1. Single Responsibility Principle (SRP) — Principio de Responsabilidad Única

\n

Teoría

\n

Una clase debe tener solo una razón para cambiar. En otras palabras, cada módulo es responsable de exactamente una funcionalidad. Si tu ViewController gestiona la UI, maneja solicitudes de red y analiza JSON, está violando el SRP.

\n

Práctica en Swift

\n

Consideremos un ejemplo típico: cargar y mostrar datos de un usuario.

\n
// ❌ Mal: violación del SRP\nclass UserViewController: UIViewController {\n    func loadUser() {\n        // Solicitud de red\n        let url = URL(string: "https://api.example.com/user")!\n        URLSession.shared.dataTask(with: url) { data, _, _ in\n            let user = try? JSONDecoder().decode(User.self, from: data!)\n            DispatchQueue.main.async {\n                self.updateUI(with: user)\n            }\n        }.resume()\n    }\n    \n    func updateUI(with user: User?) {\n        // Actualización de la UI\n    }\n}
\n
// ✅ Bien: separamos responsabilidades\nclass UserService {\n    func fetchUser(completion: @escaping (User?) -> Void) {\n        let url = URL(string: "https://api.example.com/user")!\n        URLSession.shared.dataTask(with: url) { data, _, _ in\n            let user = try? JSONDecoder().decode(User.self, from: data!)\n            completion(user)\n        }.resume()\n    }\n}\n\nclass UserViewController: UIViewController {\n    let userService = UserService()\n    \n    func loadUser() {\n        userService.fetchUser { [weak self] user in\n            DispatchQueue.main.async {\n                self?.updateUI(with: user)\n            }\n        }\n    }\n    \n    private func updateUI(with user: User?) {\n        // Actualización de la UI\n    }\n}
\n

Conclusión: Ahora UserService es responsable solo de la red, y UserViewController solo de la UI. Los cambios en la lógica de red no afectan al controlador.

\n\n

2. Open-Closed Principle (OCP) — Principio de Abierto/Cerrado

\n

Teoría

\n

Las clases deben estar abiertas para la extensión, pero cerradas para la modificación. En lugar de cambiar el código existente, agregamos nuevo código mediante herencia o protocolos. En Swift, la herramienta ideal para esto son los protocolos y las extensiones.

\n

Práctica en Swift

\n

Imagina que estamos escribiendo un módulo para calcular descuentos en una tienda en línea.

\n
// ❌ Mal: al agregar un nuevo descuento, hay que modificar la clase\nclass DiscountCalculator {\n    func calculate(price: Double, type: String) -> Double {\n        if type == "seasonal" {\n            return price * 0.9\n        } else if type == "loyalty" {\n            return price * 0.85\n        }\n        return price\n    }\n}
\n
// ✅ Bien: usamos un protocolo para la extensión\nprotocol DiscountStrategy {\n    func applyDiscount(to price: Double) -> Double\n}\n\nstruct SeasonalDiscount: DiscountStrategy {\n    func applyDiscount(to price: Double) -> Double {\n        price * 0.9\n    }\n}\n\nstruct LoyaltyDiscount: DiscountStrategy {\n    func applyDiscount(to price: Double) -> Double {\n        price * 0.85\n    }\n}\n\nclass DiscountCalculator {\n    func calculate(price: Double, strategy: DiscountStrategy) -> Double {\n        strategy.                

Blogs

Book Recommendations