@StateObject é um property wrapper no SwiftUI que cria e possui uma instância de ObservableObject durante todo o ciclo de vida de uma View. Quando uma View aparece pela primeira vez na tela, @StateObject inicializa o objeto e o armazena até que a View seja removida da memória. Isso garante que os dados não sejam redefinidos ao reconstruir a interface — por exemplo, ao alterar o tema ou atualizar a View pai. De acordo com a Documentação do Desenvolvedor Apple (2025), @StateObject deve ser usado como a fonte primária de verdade (source of truth) para ObservableObject na hierarquia do SwiftUI, enquanto as Views filhas recebem o objeto já criado através de @ObservedObject ou @EnvironmentObject.
Pontos Principais
@StateObject é um property wrapper introduzido no iOS 14 que permite a uma View criar e possuir uma instância de uma classe que está em conformidade com o protocolo ObservableObject. Ao contrário do @State, que trabalha com tipos de valor (structs), @StateObject é projetado para tipos de referência — classes que podem notificar o SwiftUI sobre alterações em suas propriedades.
Quando uma View usa @StateObject var viewModel: MyViewModel, o SwiftUI cria automaticamente uma instância de MyViewModel quando a View aparece pela primeira vez e a armazena em um armazenamento especial do framework. Em cada atualização da View (por exemplo, quando o estado pai muda), o SwiftUI não recria o objeto — ele usa a instância existente até que a View seja removida da hierarquia.
De acordo com a Apple WWDC Session 10137 (2024), @StateObject resolve o problema de perda de dados que existia no iOS 13 quando as Views eram reconstruídas, forçando os desenvolvedores a criar ObservableObject na View pai e passá-lo através do inicializador. Isso levava à duplicação de código e ao risco de recriar acidentalmente o objeto.
import SwiftUI
class CounterViewModel: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}
struct CounterView: View {
@StateObject var viewModel = CounterViewModel()
var body: some View {
VStack {
Text("Count: \(viewModel.count)")
Button("Increment", action: viewModel.increment)
}
}
}
O mecanismo do @StateObject é baseado na integração do SwiftUI com o framework Combine. Quando um ObservableObject marca suas propriedades com o atributo @Published, o SwiftUI se inscreve automaticamente nas alterações através do publisher incorporado ao protocolo ObservableObject. Quando uma propriedade publicada muda, o objeto envia um sinal através do publisher objectWillChange, o que desencadeia uma redesenho de todas as Views que observam este objeto.
O SwiftUI armazena a instância de ObservableObject em um armazenamento especial vinculado a uma instância específica de View. Este armazenamento é criado uma vez durante a primeira renderização e existe até que a View seja destruída. É por isso que o @StateObject garante a estabilidade da referência — o SwiftUI gerencia a memória automaticamente, sem depender do inicializador da View.
De acordo com objc.io — Thinking in SwiftUI (2025), a implementação interna do @StateObject usa um mecanismo semelhante ao @State, mas para tipos de referência: o SwiftUI cria um invólucro (boxing) ao redor do objeto e gerencia seu ciclo de vida através de seu próprio alocador, otimizado para reconstruções frequentes da hierarquia de Views.
A principal diferença entre @StateObject e @ObservedObject está em quem possui o objeto. @StateObject cria e armazena o objeto — ele é o proprietário. @ObservedObject apenas observa o objeto que foi criado em outro lugar e passado através do inicializador ou de uma propriedade.
| Característica | @StateObject | @ObservedObject |
|---|---|---|
| Propriedade | Cria e possui o objeto | Apenas observa |
| Inicialização | Dentro da View via init/padrão | Externa, passada via parâmetro |
| Ciclo de vida | Vinculado ao ciclo de vida da View | Não controlado pela View |
| Recriação | Não é recriado na atualização | Pode ser substituído externamente |
| Versão iOS | iOS 14+ | iOS 13+ |
A regra é simples: se a View cria o ObservableObject — use @StateObject. Se a View apenas recebe um objeto já criado do pai — use @ObservedObject. Violar esta regra leva à perda de dados (se usar @ObservedObject para propriedade) ou à criação excessiva de objetos (se usar @StateObject para observação).
@StateObject deve ser usado nas Views que são a fonte de verdade para um conjunto específico de dados. Os cenários típicos incluem telas com seu próprio view model, telas raiz de pilhas de navegação e apresentações modais que gerenciam seu próprio estado.
struct ProfileView: View {
@StateObject var viewModel = ProfileViewModel()
var body: some View {
NavigationStack {
Form {
TextField("Name", text: $viewModel.name)
TextField("Email", text: $viewModel.email)
Button("Save") {
viewModel.saveProfile()
}
}
.navigationTitle("Profile")
}
}
}
Inicializar @StateObject com parâmetros requer uma sintaxe especial, já que o SwiftUI gerencia a criação do objeto por conta própria. Você não pode simplesmente passar parâmetros para o inicializador — é necessário usar um closure de escape ou um método de fábrica separado.
De acordo com Swift by Sundell (2024), a abordagem mais limpa é usar um método de fábrica ou closure que o SwiftUI chamará quando o objeto for criado pela primeira vez. Uma abordagem alternativa é inicializar o ObservableObject na View pai e passá-lo através de @StateObject usando o inicializador padrão.
class UserViewModel: ObservableObject {
@Published var user: User
init(user: User) {
self.user = user
}
}
struct UserDetailView: View {
@StateObject var viewModel: UserViewModel
init(user: User) {
_viewModel = StateObject(wrappedValue: UserViewModel(user: user))
}
var body: some View {
Text(viewModel.user.name)
}
}
É importante lembrar que o inicializador de View com @StateObject deve usar um underscore antes do nome da propriedade (_viewModel) para acessar o property wrapper em si, não seu valor. Este é um padrão Swift padrão para trabalhar com property wrappers em inicializadores.
O erro mais comum é usar @ObservedObject em vez de @StateObject para uma View que deveria possuir o objeto. Neste caso, cada vez que o pai é reconstruído, o objeto será recriado, levando à perda de todos os dados acumulados. Este erro é especialmente insidioso em hierarquias complexas com NavigationStack ou TabView.
Para evitar esses problemas, siga uma regra simples: um @StateObject por fonte de verdade. Se os dados devem ser compartilhados entre várias telas — crie @StateObject uma vez na View raiz e passe através de @ObservedObject ou @EnvironmentObject para os elementos filhos.
// ❌ Wrong: @ObservedObject for owning an object
struct BadView: View {
@ObservedObject var vm = ViewModel() // will be recreated on each update!
}
// ✅ Correct: @StateObject for owning
struct GoodView: View {
@StateObject var vm = ViewModel() // created once for View lifetime
}
Perguntas Frequentes
@State trabalha com tipos de valor (structs, strings, números) e armazena o valor diretamente no armazenamento do SwiftUI. @StateObject trabalha com tipos de referência — classes que estão em conformidade com ObservableObject. @State é adequado para estados locais simples, @StateObject para objetos complexos com lógica e propriedades publicadas.
Não, @StateObject está disponível apenas a partir do iOS 14. Para iOS 13, use @ObservedObject e crie o ObservableObject na View pai através de @State com gerenciamento manual de ciclo de vida. Uma alternativa é usar @State com um struct em vez de uma classe para dados que não exigem semântica de referência.
A View filha criará sua própria cópia do ObservableObject, completamente independente da do pai. Alterações em uma não afetarão a outra. Isso é quase sempre um erro: use @ObservedObject para receber um objeto do pai e @StateObject apenas para criar um novo objeto dentro da View.
O objeto é destruído quando a View que o criou é completamente removida da hierarquia do SwiftUI. Para uma tela no NavigationStack, isso ocorre ao fazer pop da pilha de navegação. Para uma janela modal — quando é fechada. Para TabView — ao alternar a aba, se a View não estiver em cache.
Use um init personalizado com acesso ao property wrapper através de underscore: _viewModel = StateObject(wrappedValue: MyViewModel(param: value)). Este padrão permite passar qualquer parâmetro para o ObservableObject, mantendo a garantia de criação única do objeto durante a vida da View.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também