Introdução: Por que SOLID é importante para o desenvolvedor Swift
No mundo do desenvolvimento iOS, criar código sustentável e escalável é uma tarefa fundamental. Os princípios SOLID, formulados por Robert Martin, continuam sendo o padrão ouro da arquitetura de software. Em nível avançado, dominar esses princípios em Swift permite não apenas escrever código limpo, mas também construir sistemas flexíveis e resistentes a mudanças de requisitos. Neste artigo, analisaremos cada princípio com exemplos em Swift, focando em problemas reais do desenvolvedor iOS/macOS.
1. Single Responsibility Principle (SRP) em Swift
Teoria: Cada classe ou estrutura deve ter apenas um motivo para ser alterada. Na prática, isso significa que o módulo é responsável por uma tarefa claramente definida.
Problema: ViewController sobrecarregado
No UIKit, é comum encontrar controladores que gerenciam a UI, processam requisições de rede e salvam dados simultaneamente. Isso viola o SRP.
// ❌ Violação do SRPclass UserViewController: UIViewController { @IBOutlet weak var tableView: UITableView! private var users: [User] = []
override func viewDidLoad() { super.viewDidLoad() fetchUsers() }
private func fetchUsers() { // Requisição de rede URLSession.shared.dataTask(with: URL(string: "https://api.example.com/users")!) { data, _, _ in // Parsing JSON // Salvamento em Core Data DispatchQueue.main.async { self.tableView.reloadData() } }.resume() }}Solução: separação de responsabilidades
Extraímos o trabalho com dados e rede para serviços separados.
// ✅ SRP: serviço separado para trabalhar com usuáriosprotocol UserServiceProtocol { func fetchUsers(completion: @escaping (Result<[User], Error>) -> Void)}
class UserService: UserServiceProtocol { func fetchUsers(completion: @escaping (Result<[User], Error>) -> Void) { // Lógica da requisição de rede }}
// ✅ SRP: gerenciador separado para Core Dataclass CoreDataManager { func saveUsers(_ users: [User]) { // Salvamento em Core Data }}
// ✅ SRP: ViewController gerencia apenas a UIclass UserViewController: UIViewController { private let userService: UserServiceProtocol private let coreDataManager: CoreDataManager
init(userService: UserServiceProtocol, coreDataManager: CoreDataManager) { self.userService = userService self.coreDataManager = coreDataManager super.init(nibName: nil, bundle: nil) }
override func viewDidLoad() { super.viewDidLoad() loadUsers() }
private func loadUsers() { userService.fetchUsers { [weak self] result in switch result { case .success(let users): self?.coreDataManager.saveUsers(users) self?.updateUI(with: users) case .failure(let error): self?.showError(error) } } }}2. Open/Closed Principle (OCP) e protocolos Swift
Teoria: Entidades de software (classes, módulos) devem estar abertas para extensão, mas fechadas para modificação. Em Swift, isso é alcançado através de protocolos e herança.
Exemplo: sistema de notificações
// ❌ Violação do OCP: ao adicionar um novo tipo de notificação, é necessário alterar o switchclass NotificationSender { func send(_ notification: Notification, type: String) { switch type { case "email": // Envio de email case "push": // Envio de push default: break } }}
// ✅ OCP: usando protocoloprotocol NotificationChannel { func send(_ notification: Notification)}
class EmailChannel: NotificationChannel { func send(_ notification: Notification)