Introdução: por que a refatoração de código legado em Swift é uma necessidade
Todo desenvolvedor iOS/macOS mais cedo ou mais tarde se depara com código legado. Pode ser um projeto escrito em Objective-C e parcialmente traduzido para Swift, ou um aplicativo criado na era do Swift 2.0 usando Massive View Controller. Esse código geralmente funciona, mas sua manutenção se torna um inferno: qualquer alteração requer horas de teste, e adicionar um novo recurso quebra o antigo.
A refatoração de código legado em Swift não é apenas "pentear" o código. É um processo estratégico que torna o aplicativo mais testável, extensível e performático. Neste artigo, vamos analisar como abordar a refatoração corretamente, com exemplos em Swift, para não quebrar a produção e obter um resultado funcional.
1. Diagnóstico: como saber se o código legado precisa de refatoração
Antes de alterar o código, é preciso entender o que exatamente precisa de mudanças. Os principais sinais de código legado em Swift são:
- Massive View Controller (antipadrão MVC) — arquivos com 500+ linhas, onde lógica, UI e requisições de rede estão misturadas em uma única classe.
- Alto acoplamento (tight coupling) — classes dependem diretamente umas das outras, sem protocolos e dependency injection.
- Ausência de testes — se você tem medo de alterar uma linha de código porque não sabe o que vai quebrar.
- Variáveis globais e singletons — uso de
UserDefaults.standardouNotificationCenter.defaultpor todo o projeto. - Force unwrapping — uso generalizado de
!em vez de extração segura de opcionais.
1.1. Ferramentas para análise de código legado em Swift
Para uma avaliação objetiva do estado do código, use ferramentas:
- SwiftLint — analisador estático que identifica violações de code style.
- Xcode Instruments — para encontrar vazamentos de memória e problemas de performance.
- Code coverage — mostrará quanto código está coberto por testes (geralmente 0-10% em projetos legados).
Exemplo de código legado típico que requer refatoração:
class LegacyViewController: UIViewController { @IBOutlet weak var label: UILabel! var data: [String] = [] override func viewDidLoad() { super.viewDidLoad() // Mistura de lógica e UI let url = URL(string: "https://api.example.com/data")! let data = try! Data(contentsOf: url) let json = try! JSONSerialization.jsonObject(with: data) as! [[String: Any]] for item in json { data.append(item["name"] as! String) } label.text = data.first }}Aqui há vários problemas: force unwrapping, requisição de rede síncrona na thread principal, mistura de lógica e apresentação.
2. Estratégia de refatoração de código legado em Swift: plano passo a passo
Refatoração sem estratégia é um caminho para o desastre. Use a abordagem do "sapador": primeiro, torne a área segura, depois altere o código.
2.1. Passo 1: Crie uma "rede de segurança" de testes
Antes de mudar qualquer coisa, escreva testes para o código existente. Use Characterization Tests — testes que fixam o comportamento atual do sistema. Mesmo que o código pareça horrível, os testes mostrarão que você não quebrou a lógica.
import XCTest@testable import LegacyApp
class LegacyViewControllerTests: XCTestCase { func testViewDidLoad_SetsLabelText() { let sut = LegacyViewController() sut.loadViewIfNeeded() // Verificamos se label não é nil e contém texto XCTAssertNotNil(sut.label.text) }}Este teste pode não ser ideal, mas ele fixará o estado atual. Após a refatoração, o teste deve passar.