Code review en Swift: qué verificar y cómo dar retroalimentación
Code review no es solo un procedimiento formal, sino una herramienta poderosa para mejorar la calidad del código y el crecimiento profesional del equipo. Para desarrolladores iOS/macOS en Swift, una revisión bien realizada ayuda a evitar errores, mejorar la arquitectura y mantener un estilo de código uniforme. En este artículo, analizaremos en qué prestar atención durante el code review de proyectos Swift y cómo dar retroalimentación para que sea útil, no desmotivadora.
1. Arquitectura y separación de responsabilidades
Lo primero que se debe evaluar en una revisión es qué tan bien el código se ajusta a la arquitectura elegida (MVC, MVVM, VIPER, SwiftUI + Combine). Una mala arquitectura conduce a "God-objects" y dificulta el mantenimiento.
Qué verificar:
- ¿El ViewController mezcla lógica de negocio y código de UI?
- ¿La lógica de red está separada en servicios independientes (NetworkService)?
- ¿Se utilizan protocolos para la abstracción de dependencias?
- ¿Una sola clase tiene demasiadas responsabilidades?
// ❌ Mal: ViewController conoce URLSession y parseo JSONclass UserViewController: UIViewController { func fetchUser() { URLSession.shared.dataTask(with: URL(string: "https://api.example.com/user")!) { data, _, _ in let user = try? JSONDecoder().decode(User.self, from: data!) DispatchQueue.main.async { self.updateUI(with: user) } }.resume() }}
// ✅ Bien: lógica separada en un servicioprotocol UserServiceProtocol { func fetchUser(completion: @escaping (Result<User, Error>) -> Void)}
class UserViewModel { private let userService: UserServiceProtocol init(userService: UserServiceProtocol) { self.userService = userService } func loadUser() { userService.fetchUser { [weak self] result in // procesar resultado } }}Consejo para la retroalimentación: En lugar de "Esta clase es demasiado grande", escribe: "Veo que UserViewController se encarga tanto de las solicitudes de red como de la UI. ¿Quizás podríamos mover la solicitud a un servicio separado, como discutimos en la retrospectiva? Esto simplificaría las pruebas".
2. Seguridad de memoria y gestión del ciclo de vida
Swift es un lenguaje con conteo automático de referencias (ARC), pero esto no exime al desarrollador de preocuparse por los ciclos de referencias fuertes. Los errores en esta área son una causa común de fugas de memoria en aplicaciones iOS.
Qué verificar:
- ¿Se usa
[weak self]o[unowned self]en closures, especialmente al trabajar con async/await y Combine? - ¿Se crea una referencia cíclica entre el delegado y el propietario?
- ¿Se liberan correctamente los recursos (observadores, temporizadores)?
// ❌ Potencial fuga: referencia fuerte a self en el closureclass LocationManager { var onLocationUpdate: ((CLLocation) -> Void)? func startUpdating() { someAsyncAPI { location in self.onLocationUpdate?(location) // self capturado fuertemente } }}
// ✅ Corregido con [weak self]class LocationManager { var onLocationUpdate: ((CLLocation) -> Void)? func startUpdating() { someAsyncAPI { [weak self] location in guard let self = self else { return } self.onLocationUpdate?(location) } }}Consejo para la retroalimentación: Señala no solo el problema, sino también las consecuencias: "Aquí podría ocurrir una fuga de memoria si LocationManager vive más de lo necesario. Agreguemos [weak self] para romper el ciclo".
3. Manejo de errores y opcionalidad
Swift es un lenguaje fuertemente tipado con un potente sistema Optional. El mal manejo de valores opcionales es una de las principales