Ao criar um app iOS, você inevitavelmente encontra esta situação: uma tela mistura os estados de carregamento, sucesso, erro e dados vazios.
Gerenciar várias variáveis Bool, como isLoading, hasError e isEmpty, vira um inferno conforme elas aumentam.
Um estado de carregamento com o erro também como true. Não faz sentido lógico, mas o código pode criar essa combinação facilmente.
Indo direto ao ponto: você pode organizar isso de forma muito mais limpa criando uma FSM com um único enum do Swift, sem o padrão State.
Hoje, vamos mostrar com código como sair do inferno dos Bool.
O que é uma máquina de estados (FSM)?
Não precisa pensar nisso de forma complicada.
Uma máquina de estados, FSM (Finite State Machine), é o conceito de que o número de estados possíveis é definido e as transições seguem regras estabelecidas.
Pense em um semáforo.
Verde → amarelo → vermelho. Ele muda apenas nessa ordem; nunca pula diretamente do verde para o vermelho.
As telas do app funcionam da mesma forma. Elas passam de 로딩 → 성공 ou de 로딩 → 실패; não deveria existir um estado que seja 성공 e 로딩 ao mesmo tempo.
Mas, ao usar várias variáveis Bool, o código pode criar estados que não deveriam existir.
É exatamente isso que o enum impede.
Como criar uma máquina de estados com Swift enum
Os enum do Swift não são apenas listas de constantes.
O ponto principal é que cada case pode armazenar um valor (associated value). Assim, um único enum pode conter estado e dados juntos.
Defini os estados da tela assim.
enum LoadState {
case idle // Nada feito ainda
case loading // Carregando
case loaded([Item]) // Sucesso, com os dados
case failed(Error) // Falha, com o erro
}
Como você pode ver, loadedcarrega um array de itens, enquanto failedcarrega um erro.
Se houver sucesso, sempre haverá dados; se houver falha, sempre haverá um erro. Um estado estranho, como sucesso sem dados, simplesmente não pode ser criado.
Na View, basta observar esse único estado e renderizar a tela.
switch state {
case .idle: EmptyView()
case .loading: ProgressView()
case .loaded(let items): ItemList(items)
case .failed(let error): ErrorView(error)
}
Como switchobriga o tratamento de todos os case, o compilador avisa imediatamente quando você adiciona um estado e deixa algum caso sem tratamento.
Essa é uma grande vantagem: o compilador identifica estados esquecidos, em vez de depender das pessoas.
Dá para ficar sem o padrão State?
Nos livros de orientação a objetos, o gerenciamento de estados normalmente é ensinado com o padrão State.
Você cria uma classe para cada estado, agrupa tudo com um protocolo e coloca a lógica de transição em cada classe.
Sendo sincero, isso costuma ser exagerado para gerenciar estados de tela.
Fiz uma comparação rápida entre as duas abordagens. (Com base na minha experiência prática em 2026)
| Item | enum FSM | Padrão State |
|---|---|---|
| Número de arquivos/tipos | Um enum | Uma classe por estado |
| Prevenção de estados esquecidos | Um switch obrigatório permite que o compilador identifique o problema | A verificação precisa ser feita manualmente |
| Inclusão de dados | Natural com associated value | Gerenciada separadamente com propriedades |
| Escala adequada | Cerca de 3 a 7 estados | Quando a lógica de cada estado é muito complexa |
Se a quantidade de estados for razoável e a lógica de cada um não for pesada, uma FSM com enum é muito mais leve e segura.
Por outro lado, se cada estado tiver dezenas de linhas de comportamento complexo, vale considerar o padrão State ou separar a lógica em objetos próprios.
Não existe uma resposta certa para todos os casos.
Como gerenciar as transições de estado?
Você pode perguntar se, ao criar apenas um enum, não seria possível mudar livremente para qualquer estado.
Boa pergunta. Por isso, concentro as regras de transição em uma única função.
mutating func fetch() {
guard case .idle = self else { return } // idleIniciar apenas quando estiver idle
self = .loading
}
guard casedeixa explícito: “só passe para loading quando o estado atual for idle”.
Ao manter as condições de transição em um único lugar, os estados fluem apenas por caminhos definidos, como em um semáforo.
Assim, mesmo ao revisitar o código depois, você vê de imediato de onde cada estado pode vir e para onde pode ir.
P. E se houver mais de cinco estados?
O enum continua funcionando bem. Porém, quando as regras de transição ficam complexas, recomendo dividir a função em funções menores por estado.
P. Ele combina bem com SwiftUI?
Combina muito. Se você mantiver o estado do enum em @State ou @Published e renderizar a View com switch, o estado e a tela avançam em sincronia.
No começo, alguns Bool podem parecer suficientes, mas quando os estados começam a se entrelaçar, você acaba voltando para uma FSM com enum.
Se o gerenciamento do estado da tela está causando problemas, experimente começar pelo enum que vimos hoje. Você ficará surpreso com a rapidez com que tudo melhora.

