Princípios SOLID em Swift: teoria e prática para desenvolvimento iOS

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que SOLID é importante para um desenvolvedor Swift

Todo desenvolvedor iOS mais cedo ou mais tarde enfrenta o problema do "código espaguete": controladores incham para milhares de linhas, alterar uma entidade quebra outras três, e os testes se tornam um pesadelo. Os princípios SOLID são cinco regras fundamentais de design orientado a objetos que ajudam a criar código flexível, sustentável e testável em Swift.

Neste artigo, vamos analisar cada princípio SOLID com exemplos concretos do desenvolvimento iOS. Você aprenderá como aplicar esses princípios em projetos reais, evitando erros típicos. O material será útil tanto para desenvolvedores Junior quanto para profissionais experientes que desejam sistematizar seus conhecimentos.



1. Single Responsibility Principle (SRP) — Princípio da Responsabilidade Única

Essência: Cada classe deve ter apenas um motivo para mudar. Em outras palavras, a classe deve ser responsável por apenas uma tarefa.



Problema: O ViewController "Divino"

Um erro típico é colocar toda a lógica no UIViewController. Vejamos um exemplo ruim:

class UserProfileViewController: UIViewController {    @IBOutlet weak var nameLabel: UILabel!        func loadUser() {        // 1. Lógica de rede        let url = URL(string: "https://api.example.com/user")!        URLSession.shared.dataTask(with: url) { data, _, _ in            // 2. Parsing JSON            let user = try! JSONDecoder().decode(User.self, from: data!)            // 3. Salvamento no Core Data            self.saveToCoreData(user)            // 4. Atualização da UI            DispatchQueue.main.async {                self.nameLabel.text = user.name            }        }.resume()    }        private func saveToCoreData(_ user: User) { /* ... */ }}


Solução: Separação de Responsabilidades

Vamos dividir o código em componentes separados:

// 1. Serviço para trabalhar com redeclass UserNetworkService {    func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {        let url = URL(string: "https://api.example.com/user")!        URLSession.shared.dataTask(with: url) { data, _, error in            if let data = data {                do {                    let user = try JSONDecoder().decode(User.self, from: data)                    completion(.success(user))                } catch {                    completion(.failure(error))                }            } else if let error = error {                completion(.failure(error))            }        }.resume()    }}

// 2. Gerenciador de armazenamento de dadosclass UserStorageManager { func save(_ user: User) { // Lógica de salvamento no Core Data / Realm }}

// 3. ViewModel (ou Presenter)class UserViewModel { private let networkService = UserNetworkService() private let storageManager = UserStorageManager() var onUserUpdated: ((User) -> Void)? func loadUser() { networkService.fetchUser { [weak self] result in switch result { case .success(let user): self?.storageManager.save(user) self?.onUserUpdated?(user) case .failure(let error): print(error.localizedDescription) } } }}

// 4. ViewController agora apenas gerencia a UIclass UserProfileViewController: UIViewController { @IBOutlet weak var nameLabel: UILabel! private let viewModel = UserViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.onUserUpdated = { [weak self] user in DispatchQueue.main.async { self?.nameLabel.text = user.name } } viewModel.loadUser() }}

Resultado: Cada classe tem um motivo para mudar. Testar e manter esse código é significativamente

Blogs

Book Recommendations