Swift: Tutorial de Enums e Correspondência de Padrões do…
Os enums no Swift são muito mais do que "um conjunto de constantes" -- são cidadãos de primeira classe que podem transportar dados, suportar métodos e até se definir recursivamente. São a melhor ferramenta para construir máquinas de estado e tipos de resultado.
1. O Que Você Vai Aprender
- Sintaxe básica de enum e uso de valores brutos
- Como os valores associados permitem que enums transportem dados adicionais
- Vários estilos de correspondência de padrões (switch, if-case, guard-case)
- Enums recursivos com a palavra-chave indirect
- Aplicação prática de enums em máquinas de estado
2. Uma História Real de uma Desenvolvedora Mobile
(1) Problema: Difícil Expressar os Estados de Resposta da API
Alice encontrou um problema complicado ao construir a camada de rede para um app de notícias. Uma requisição de API poderia retornar múltiplos resultados:
// Inelegant approach: multiple optional variables to represent state
var data: NewsResponse? = nil
var error: Error? = nil
var isLoading = false
Esta abordagem tem três problemas: significado ambíguo quando dados e erro estão ambos presentes, esquecer de tratar um estado causa falhas e lógica espalhada é difícil de manter.
(2) A Solução com Enum de Valores Associados
Use um único enum para unificar todos os estados, com cada caso transportando seus próprios dados:
enum NetworkResult<T> {
case success(T)
case failure(Error)
case loading
}
ResultadoRede<[ArtigoNoticia]> transporta dados de artigo (sucesso), informações de erro (falha) ou o estado de carregamento -- três estados mutuamente exclusivos com semântica clara.
(3) Benefícios: Gerenciamento de Estado do Caos à Clareza
| Dimensão | Múltiplas Variáveis Optional | Enum com Valores Associados |
|---|---|---|
| Quantidade de estados | 2^N combinações ilegais | N estados precisos |
| Clareza semântica | dados/erro podem coexistir | Cada estado transporta seus próprios dados |
| Completude do tratamento | Fácil de esquecer casos | Verificação de exaustividade do switch |
| Manutenibilidade do código | Espalhado em if-else | Centralizado em um tipo |
3. Fundamentos de Enum
Enums definem um conjunto de valores relacionados. Os enums do Swift são mais flexíveis que os de outras linguagens -- podem associar métodos, propriedades computadas e subscritos.
(1) Enums Básicos e Valores Brutos
graph LR
A["enum Bussola {\n case norte\n case sul\n case leste\n case oeste\n}"] --> B["Bussola.norte"]
A --> C["Bussola.sul"]
A --> D["Bussola.leste"]
A --> E["Bussola.oeste"]
Um valor bruto é um valor fixo por trás de cada caso do enum, que pode ser de tipos como String ou Int.
| Tipo de Valor Bruto | Declaração | Valor |
|---|---|---|
| Int | enum Status: Int { case ok = 200 } |
Inteiro |
| String | enum Direcao: String { case norte = "N" } |
String |
| Int Implícito | enum Nota: Int { case a, b, c } |
Incrementa a partir de 0 |
| String Implícito | enum Codigo: String { case vermelho, verde } |
Igual ao nome do caso |
▶ Exemplo: Enum de Código de Status HTTP
// ============================================
// HTTP status code enum using raw values
// ============================================
enum HTTPStatus: Int {
case ok = 200
case created = 201
case badRequest = 400
case unauthorized = 401
case notFound = 404
case serverError = 500
}
let status = HTTPStatus.notFound
print("Code: \(status.rawValue)")
print("Name: \(status)")
// Create from raw value
let parsed = HTTPStatus(rawValue: 200)
print("Parsed: \(parsed ?? .serverError)")
Saída:
TEXT 📖 Somente leituraCode: 404 Name: notFound Parsed: ok
(2) Métodos e Propriedades Computadas de Enum
Enums podem definir métodos e propriedades assim como structs.
▶ Exemplo: Enum com Métodos
// ============================================
// Custom enum method: determine status category
// ============================================
enum HTTPStatus: Int {
case ok = 200
case created = 201
case badRequest = 400
case unauthorized = 401
case notFound = 404
case serverError = 500
var isSuccess: Bool {
rawValue >= 200 && rawValue < 300
}
var description: String {
switch self {
case .ok: return "OK"
case .created: return "Created"
case .badRequest: return "Bad Request"
case .unauthorized: return "Unauthorized"
case .notFound: return "Not Found"
case .serverError: return "Internal Server Error"
}
}
}
let code = HTTPStatus.ok
print("\(code.rawValue) \(code.description) - Success: \(code.isSuccess)")
Saída:
TEXT 📖 Somente leitura200 OK - Success: true
4. Valores Associados e Correspondência de Padrões
Valores associados são uma característica central que diferencia os enums do Swift de outras linguagens -- cada caso pode transportar dados adicionais de tipos diferentes.
(1) Sintaxe de Valor Associado
graph TB
A["ResultadoRede<T>"] --> B["sucesso(T) - transporta dados"]
A --> C["falha(Error) - transporta erro"]
A --> D["carregando - sem dados"]
B --> E["switch resultado {\n case .sucesso(let dados):\n mostrar(dados)\n case .falha(let erro):\n mostrar(erro)\n case .carregando:\n mostrarSpinner()\n}"]
| Característica | Valores Brutos | Valores Associados |
|---|---|---|
| Tipo de valor | Constante fixa | Dados variáveis |
| Momento de armazenamento | Determinado na definição | Passado na criação |
| Por caso | Todos os casos têm o mesmo tipo de valor | Cada caso pode diferir |
| Caso de uso | Mapear códigos fixos | Transportar dados dinâmicos |
▶ Exemplo: Tratando Resultados de Requisição de Rede
// ============================================
// Associated value enum for handling API responses
// ============================================
enum NetworkResult<T> {
case success(T)
case failure(String)
case loading
}
func handleResponse<T>(_ result: NetworkResult<T>) {
switch result {
case .success(let data):
print("Successfully received data: \(data)")
case .failure(let error):
print("Request failed: \(error)")
case .loading:
print("Loading...")
}
}
handleResponse(NetworkResult.success("User Info"))
handleResponse(NetworkResult.failure("Network connection timed out"))
handleResponse(NetworkResult.loading)
Saída:
TEXT 📖 Somente leituraSuccessfully received data: User Info Request failed: Network connection timed out Loading...
(2) Correspondência de Padrões com if-case e guard-case
Além do switch, você pode usar if case e guard case para correspondência de caso único.
▶ Exemplo: Correspondência Concisa de Caso Único
// ============================================
// Concise usage of if-case and guard-case
// ============================================
enum Measurement {
case weight(Double)
case height(Double)
case count(Int)
}
let record = Measurement.weight(75.5)
// if-case: only care about one case
if case .weight(let kg) = record, kg > 70 {
print("Weight exceeds 70kg: \(kg)")
}
// guard-case: early exit
func process(_ m: Measurement) {
guard case .count(let n) = m else {
print("Not a count type")
return
}
print("Count is \(n)")
}
process(.weight(65))
process(.count(42))
Saída:
TEXT 📖 Somente leituraWeight exceeds 70kg: 75.5 Not a count type Count is 42
5. Enums Recursivos
Um enum recursivo é aquele cujos valores associados referenciam o próprio enum, adequado para expressar estruturas em árvore ou aninhadas.
(1) A Palavra-chave indirect
Use indirect para marcar um caso recursivo ou o enum inteiro, informando ao compilador que é necessário armazenamento indireto.
| Sintaxe | Descrição |
|---|---|
indirect case expressao(Expressao) |
Caso recursivo único |
indirect enum Expressao { ... } |
Enum inteiro permite recursão |
▶ Exemplo: Avaliando uma Expressão Matemática
// ============================================
// Recursive enum: math expression tree
// ============================================
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
func evaluate(_ expr: ArithmeticExpression) -> Int {
switch expr {
case .number(let value):
return value
case .addition(let left, let right):
return evaluate(left) + evaluate(right)
case .multiplication(let left, let right):
return evaluate(left) * evaluate(right)
}
}
// Build the expression (3 + 5) * 2
let three = ArithmeticExpression.number(3)
let five = ArithmeticExpression.number(5)
let sum = ArithmeticExpression.addition(three, five)
let product = ArithmeticExpression.multiplication(sum, .number(2))
print("(3 + 5) * 2 = \(evaluate(product))")
Saída:
TEXT 📖 Somente leitura(3 + 5) * 2 = 16
6. Exemplo Completo: Máquina de Estado de Pedido
// ============================================
// Full example: E-commerce order state machine
// Features: order state transitions + data processing
// ============================================
import Foundation
// 1. Order status enum (with associated values)
enum OrderStatus {
case pending // Pending payment
case paid(Date) // Paid (with payment time)
case shipped(trackingNumber: String) // Shipped (with tracking number)
case delivered(Date) // Delivered (with receipt time)
case cancelled(reason: String) // Cancelled (with reason)
var description: String {
switch self {
case .pending: return "Pending"
case .paid(let date): return "Paid (\(date.formatted()))"
case .shipped(let tracking): return "Shipped (Tracking: \(tracking))"
case .delivered(let date): return "Delivered (\(date.formatted()))"
case .cancelled(let reason): return "Cancelled: \(reason)"
}
}
var isFinalState: Bool {
if case .delivered = self { return true }
if case .cancelled = self { return true }
return false
}
}
// 2. Usage example
let now = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let statuses: [OrderStatus] = [
.pending,
.paid(now),
.shipped(trackingNumber: "SF1234567890"),
.delivered(now),
.cancelled(reason: "Cancelled by user")
]
for status in statuses {
let final = status.isFinalState ? " (Final)" : ""
print("\(status.description)\(final)")
}
Saída:
TEXT 📖 Somente leituraPending Paid (2026-07-30) Shipped (Tracking: SF1234567890) Delivered (2026-07-30) (Final) Cancelled: Cancelled by user (Final)
❓ Perguntas Frequentes
P: Como escolher entre enums e structs? R: Enums são adequados para "estados mutuamente exclusivos" -- em qualquer ponto, apenas um pode estar ativo. Structs são adequadas para "múltiplas propriedades coexistentes" -- todas as propriedades existem simultaneamente. A escolha depende se seu modelo de dados é uma relação OU ou uma relação E. P: Valores associados e genéricos podem ser usados juntos? R: Sim.
enum Optional<T>é um exemplo clássico de valores associados + genéricos:case noneoucase some(T). Valores associados podem referenciar parâmetros genéricos. P: Por que os enums do Swift são mais poderosos que os enums do C/Java? R: Enums do Swift podem ter métodos, propriedades computadas, valores associados, definições recursivas e conformidade com protocolos. Enums do C/Java são meramente constantes inteiras nomeadas; enums do Swift são tipos de primeira classe completos. P: Quando devo obrigatoriamente usar indirect? R: Quando o tipo de valor associado de um enum referencia a si mesmo. Por exemplo,ExpressaoAritmetica.adicaotem valores associados do tipoExpressaoAritmetica, formando uma referência recursiva que requer indirect. P: Devo sempre escrever default em um switch sobre um enum? R: É recomendado esgotar todos os casos e NÃO usar default. Dessa forma, quando você adicionar um novo caso no futuro, o compilador avisa sobre casos ausentes, prevenindo omissões acidentais. Mas se você genuinamente não precisa tratar todos os casos, pode adicionar default.
📖 Resumo
- Enums do Swift são tipos de primeira classe, suportando métodos, propriedades computadas e conformidade com protocolos
- Valores brutos vinculam um valor constante fixo a cada caso
- Valores associados permitem que cada caso transporte dados adicionais de tipos diferentes
- A correspondência de padrões (switch, if-case, guard-case) extrai dados do enum com segurança
- Enums recursivos usam a palavra-chave indirect para expressar estruturas aninhadas em árvore
- Enums são a ferramenta ideal para construir máquinas de estado, tipos de resultado e tipos optional
📝 Exercícios
- Básico: Defina um enum
Semaforocom três casos: vermelho, amarelo, verde. Adicione uma propriedade computadaduracaoque retorna a duração em segundos de cada luz. - Intermediário: Use valores associados para definir um enum
Ou<L, R>(semelhante ao Result do Rust) com casosesquerda(L)edireita(R), e adicione um métodomappara transformar o valor direito. - Desafio: Use um enum recursivo para implementar um tipo JSON (suportando null, bool, int, string, array, dicionário) e escreva uma função para formatar um valor enum JSON como uma string indentada.