Refactorización de código legacy en Swift: guía paso a paso

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: ¿por qué refactorizar código legacy en Swift?



Trabajar con código heredado (legacy) es una parte inevitable de la vida del desarrollador iOS/macOS. Los proyectos escritos en versiones tempranas de Swift (2.x, 3.x) o incluso en Objective-C a menudo contienen clases monolíticas, duplicación de lógica y falta de modularidad. Sin refactorización, dicho código se convierte en una fuente de bugs y ralentiza la adición de nuevas funcionalidades.



En este artículo, analizaremos estrategias prácticas de refactorización para código legacy en Swift. Aprenderás cómo mejorar la arquitectura de forma segura sin romper la funcionalidad existente. Todos los ejemplos son aplicables tanto para aplicaciones iOS como macOS.



1. Análisis y preparación: ¿por dónde empezar?



Antes de cambiar el código, es necesario entender su estado actual. Sin análisis, corres el riesgo de romper la funcionalidad que funciona.



1.1. Cobertura con tests (Characterization Tests)



El primer paso es escribir tests de caracterización. Estos fijan el comportamiento actual del código, incluso si parece incorrecto. Solo después de esto se pueden realizar cambios de forma segura.



// Ejemplo de función legacy: UserManager.swiftclass UserManager {    func fetchUser(by id: Int) -> String {        // Implementación obsoleta con force unwrap        let dict = ["id": id, "name": "John"] as [String : Any]        return dict["name"] as! String    }}

// Test de caracterizaciónimport XCTestclass UserManagerTests: XCTestCase { func testFetchUserReturnsName() { let manager = UserManager() let result = manager.fetchUser(by: 1) XCTAssertEqual(result, "John") }}


1.2. Inventario de "malas prácticas"



Haz una lista de patrones problemáticos en el código:

  • Force unwrapping (!) — la principal fuente de crashes.
  • Singletons globales — dificultan el testing.
  • Números y cadenas mágicas — reducen la legibilidad.
  • Métodos largos (más de 50 líneas) — violan el SRP.
  • Falta de tipos (uso de Any).


2. Refactorización paso a paso: de lo simple a lo complejo



Es mejor realizar la refactorización de código legacy en pequeños pasos. Cada cambio debe ser reversible y estar acompañado de tests.



2.1. Eliminando el force unwrapping



Reemplaza ! con construcciones seguras: guard let o if let.



// Antes de la refactorizaciónclass NetworkService {    func loadData() -> Data {        let url = URL(string: "https://api.example.com")!        return try! Data(contentsOf: url)    }}

// Después de la refactorizaciónclass NetworkService { enum NetworkError: Error { case invalidURL case dataLoadingFailed } func loadData() throws -> Data { guard let url = URL(string: "https://api.example.com") else { throw NetworkError.invalidURL } do { return try Data(contentsOf: url) } catch { throw NetworkError.dataLoadingFailed } }}


2.2. Descomposición de clases "divinas"



Las clases legacy a menudo violan el principio de responsabilidad única (SRP). Divídelas en entidades más pequeñas.



// Problema: la clase lo hace todoclass ProfileViewController: UIViewController {    func fetchProfile() { /* solicitud de red */ }    func parseJSON() { /* parsing */ }    func updateUI() { /* actualización de UI */ }    func saveToCoreData() { /* guardado */ }}

// Solución: división en módulosprotocol ProfileRepository { func fetchProfile(completion: @escaping (Result<Profile, Error>) -> Void)}

class ProfileNetworkRepository: ProfileRepository { func fetchProfile(completion: @escaping (Result<Profile, Error>) -> Void) { // Lógica de red

Blogs

Book Recommendations