Clean Code em Swift: princípios e exemplos na prática
\n\nCódigo limpo (Clean Code) não é apenas um termo da moda, mas uma habilidade fundamental para o desenvolvedor profissional. No ecossistema Swift (iOS/macOS), a qualidade do código impacta diretamente a velocidade de desenvolvimento, a quantidade de bugs e a facilidade de manutenção do projeto. Neste artigo, vamos analisar os princípios-chave do Clean Code aplicados ao Swift e mostrar sua implementação com exemplos práticos.
\n\n1. Nomenclatura: fale a linguagem do domínio
\n\nUm bom nome para variável, função ou classe substitui comentários. Em Swift, com seu rico sistema de tipos e valores opcionais, a nomenclatura correta é especialmente importante.
\n\nExemplo ruim:
\nfunc calc(a: [String: Any]) -> Double {\n var x = 0.0\n for (_, v) in a {\n if let n = v as? Double {\n x += n\n }\n }\n return x\n}\n\nExemplo bom (Clean Code):
\nfunc calculateTotalPrice(of products: [Product]) -> Double {\n return products.reduce(0) { $0 + $1.price }\n}\n\nPrincípios de nomenclatura em Swift:
\n- \n
- Clareza é mais importante que brevidade:
remove(at:)é melhor querem(at:). \n - Use substantivos para tipos:
UserViewController,NetworkManager. \n - Verbos para funções:
fetchData(),saveToCache(). \n - Variáveis booleanas: comece com
is,has,should—isEnabled,hasContent. \n - Evite abreviações:
backgroundColorem vez debgColor. \n
2. Princípio da Responsabilidade Única (SRP) em Swift
\n\nCada classe, estrutura ou função deve ter apenas um motivo para ser alterada. No desenvolvimento iOS, isso é especialmente relevante para ViewControllers, que frequentemente se tornam "objetos divinos".
\n\nViolando o SRP (ViewController típico):
\nclass ProfileViewController: UIViewController {\n @IBOutlet weak var nameLabel: UILabel!\n @IBOutlet weak var avatarImageView: UIImageView!\n \n override func viewDidLoad() {\n super.viewDidLoad()\n // Carregamento de dados\n let url = URL(string: "https://api.example.com/user")!\n let data = try! Data(contentsOf: url)\n let json = try! JSONSerialization.jsonObject(with: data) as! [String: Any]\n nameLabel.text = json["name"] as? String\n \n // Carregamento do avatar\n let imageUrl = URL(string: json["avatar"] as! String)!\n let imageData = try! Data(contentsOf: imageUrl)\n avatarImageView.image = UIImage(data: imageData)\n \n // Salvando no cache\n UserDefaults.standard.set(json, forKey: "cachedUser")\n }\n}\n\nRefatoração seguindo o SRP:
\n// 1. Serviço separado para trabalhar com rede\nclass UserNetworkService {\n func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {\n // ... lógica de rede\n }\n}\n\n// 2. Gerenciador de cache separado\nclass UserCacheManager {\n func saveUser(_ user: User) {\n // ... lógica de cache\n }\n}\n\n// 3. ViewModel para lógica de negócios\nclass ProfileViewModel {\n private let networkService = UserNetworkService()\n private let cacheManager = UserCacheManager()\n \n func loadProfile(completion: @escaping (User) -> Void) {\n networkService.fetchUser { [weak self] result in\n switch result {\n case .success(let user):\n self?.cacheManager.saveUser(user)\n completion(user)\n case .failure:\n // Carrega do cache em caso de erro\n if let cachedUser = self?.cacheManager.lo