Tratamento de Erros em Swift: try/catch e muito mais — nível avançado
O tratamento de erros é uma parte essencial de qualquer desenvolvimento iOS/macOS sério. O Swift fornece um sistema de gerenciamento de erros poderoso e expressivo que vai muito além do simples try/catch. Neste artigo, veremos técnicas avançadas: trabalho com o protocolo Error, enumerações de erros, as palavras-chave throws, rethrows, bem como o tipo Result e abordagens funcionais. O material é voltado para desenvolvedores que já conhecem os fundamentos do Swift.
1. Protocolo Error e tipos de erro personalizados
Na base do tratamento de erros em Swift está o protocolo Error. Qualquer tipo que esteja em conformidade com este protocolo pode ser lançado como um erro. Na maioria das vezes, para isso, usam-se enumerações (enum).
import Foundation
// Definindo um tipo de erro personalizado para uma requisição de redeenum NetworkError: Error { case invalidURL case noData case decodingFailed(description: String) case serverError(statusCode: Int)}
// Função que pode lançar um errofunc fetchUserData(from urlString: String) throws -> Data { guard let url = URL(string: urlString) else { throw NetworkError.invalidURL }
// Simulação de uma requisição de rede let success = Bool.random() if !success { throw NetworkError.serverError(statusCode: 500) }
return Data()}O uso de enumerações permite agrupar erros relacionados e transmitir informações adicionais (associated values), tornando o código mais legível e autodocumentado.
2. Uso avançado do do-catch
O bloco do-catch é a ferramenta principal para capturar erros. Em um nível avançado, é importante saber como tratar diferentes tipos de erros de maneiras distintas.
func processUserData() { do { let data = try fetchUserData(from: "https://api.example.com/user") // Trabalhando com os dados print("Dados recebidos: \\(data.count) bytes") } catch NetworkError.invalidURL { print("Erro: URL inválido") } catch NetworkError.serverError(let code) where code >= 500 { print("Erro do servidor com código \\(code). Tente novamente mais tarde.") } catch let error as NetworkError { print("Erro de rede: \\(error)") } catch { // Tratamento de todos os outros erros print("Erro desconhecido: \\(error.localizedDescription)") }}Pontos-chave:
- Pattern matching com
where: permite filtrar erros por condições adicionais (por exemplo, faixa de códigos de resposta). - Type casting (
as): se você espera vários tipos de erro, pode usarcatch let error as MyErrorType. - Bloco
catchgenérico: deve ser sempre o último, pois captura qualquer erro não tratado.
3. Palavras-chave throws, rethrows e propagating errors
A palavra-chave throws indica que uma função pode lançar um erro. rethrows é um mecanismo mais sutil: ele diz que uma função pode lançar um erro apenas se um de seus parâmetros de closure (closure) lançou um erro.
// Função com throws — ela mesma pode lançar um errofunc loadConfig() throws -> [String: String] { // ... throw NetworkError.noData}
// Função com rethrows — repassa o erro da closurefunc performWithRetry(times: Int, operation: () throws -> Void) rethrows { for _ in 0..<times { try operation() }}
// Usodo { try performWithRetry(times: 3) { try loadConfig() }} catch { print("Operação falhou após 3 tentativas: \\(error)")
```