@StateObject: o que é, diferença do @ObservedObject e exemplos

Autor: IT Sectr Publicado: 2026-06-19 Tempo de leitura: 7 min

@StateObject é um Property Wrapper no SwiftUI para criar e possuir uma instância de ObservableObject diretamente em uma view. O SwiftUI garante que o objeto seja inicializado uma vez por ciclo de vida da view e não seja recriado em renderizações subsequentes. De acordo com a Documentação para Desenvolvedores da Apple (2025), o @StateObject é recomendado para views raiz que criam uma fonte de dados. @StateObject é a escolha certa para possuir um ObservableObject na hierarquia do SwiftUI.

Pontos principais

  • @StateObject — Property Wrapper para criar e possuir ObservableObject em uma view
  • Única instância — o objeto é criado uma vez e não é recriado em renderizações
  • Fonte da verdade — @StateObject garante estabilidade de dados para toda a hierarquia
  • Diferença do @ObservedObject — @ObservedObject não possui o objeto e pode perdê-lo
  • Views raiz — @StateObject é usado na view que cria o objeto

O que é @StateObject no SwiftUI?

@StateObject é um Property Wrapper introduzido no SwiftUI 2.0 (iOS 14) que combina as capacidades de @ObservedObject e @State. Como @ObservedObject, ele assina as alterações de ObservableObject. Como @State, ele garante que os dados sobrevivam a inicializações repetidas da estrutura da view. O @StateObject cria o objeto uma vez quando a view aparece pela primeira vez e o armazena no heap do SwiftUI.

Antes do @StateObject, os desenvolvedores usavam @ObservedObject para todos os ObservableObjects, incluindo os criados em views. Isso levava a perdas frequentes de dados quando a view pai era atualizada, fazendo com que a estrutura da view fosse recriada e levando a instância do @ObservedObject junto. O @StateObject resolveu isso adicionando uma garantia de estabilidade.

A regra principal: @StateObject é usado na view que cria o objeto no inicializador padrão (let model = ViewModel()). As views filhas que recebem este objeto usam @ObservedObject. Essa separação garante uma única fonte da verdade em toda a hierarquia.

Ciclo de vida do @StateObject

O SwiftUI gerencia o ciclo de vida do @StateObject através de um gerenciador de armazenamento semelhante ao @State. Quando a view aparece pela primeira vez, o SwiftUI aloca memória para o objeto e o armazena em uma área persistente. Em renderizações subsequentes (chamadas de body), o objeto não é recriado—a instância existente é usada. O objeto vive enquanto a view estiver na hierarquia.

Quando a view é removida da hierarquia, o SwiftUI destrói o @StateObject, chamando deinit. Quando a view é adicionada novamente à hierarquia, uma nova instância é criada. Isso é importante ao projetar: se precisar preservar dados entre remoções de view, use uma camada de serviço (singleton ou DI) ou @AppStorage para persistência.

swift
class TimerViewModel: ObservableObject {
    @Published var seconds: Int = 0
    private var timer: Timer?

    func start() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            self.seconds += 1
        }
    }

    deinit {
        timer?.invalidate()
    }
}

struct TimerView: View {
    @StateObject var viewModel = TimerViewModel()

    var body: some View {
        Text("\(viewModel.seconds)s")
            .onAppear { viewModel.start() }
    }
}

No exemplo, TimerViewModel é criado via @StateObject e vive enquanto TimerView estiver na tela. O temporizador inicia em onAppear e para em deinit. Se @ObservedObject fosse usado, cada renderização de TimerView criaria um novo TimerViewModel com segundos = 0, e o temporizador nunca funcionaria corretamente. O @StateObject garante que o viewModel seja único e estável.

@StateObject vs @ObservedObject: comparação

A escolha entre @StateObject e @ObservedObject depende de quem possui o objeto. Se a view cria o objeto—@StateObject. Se a view recebe um objeto pronto—@ObservedObject. Esta regra é tão importante que o Xcode exibe um aviso ao usar @StateObject em uma view filha que recebe o objeto através de um inicializador.

SituaçãoWrapper recomendado
A view cria modelo via ViewModel()@StateObject
A view recebe modelo do pai@ObservedObject
O modelo é usado em uma view@StateObject
O modelo é passado via Environment@EnvironmentObject
O modelo é necessário para prévias@ObservedObject + mock

Na prática, no início de um projeto, @StateObject é frequentemente usado na view raiz e @ObservedObject em todas as views filhas. À medida que o aplicativo cresce, algumas instâncias de @StateObject podem ser substituídas por @EnvironmentObject para simplificar a hierarquia. No entanto, o @StateObject continua sendo a melhor escolha para telas modulares com lógica própria.

Padrões de uso do @StateObject

O primeiro padrão—MVVM com @StateObject. O ViewModel como ObservableObject é criado na view via @StateObject. O ViewModel contém propriedades @Published e lógica de negócios. A view assina as alterações e atualiza a interface. Essa abordagem fornece isolamento testável: o ViewModel pode ser testado sem UI criando uma instância diretamente.

O segundo padrão—@StateObject com dependências. Se o ViewModel exigir serviços, use inicialização com parâmetros. Por exemplo, @StateObject var viewModel = UserViewModel(api: APIClient.shared). No entanto, tenha cuidado: os parâmetros são calculados em cada renderização de body, mas o objeto é criado apenas uma vez. O SwiftUI ignora inicializações subsequentes de @StateObject.

O terceiro padrão—@StateObjects aninhados. No SwiftUI, você pode ter vários @StateObjects em uma view, mas isso raramente se justifica. Normalmente, um @StateObject gerencia todo o conjunto de dados da view. Se a lógica se tornar muito complexa, divida-a em uma composição de serviços @ObservedObject dentro de um @StateObject.

swift
struct AppView: View {
    @StateObject var router = NavigationRouter()
    @StateObject var auth = AuthViewModel()

    var body: some View {
        ContentView()
            .environmentObject(router)
            .environmentObject(auth)
    }
}

No exemplo, AppView cria dois @StateObjects: NavigationRouter para gerenciar navegação e AuthViewModel para autenticação. Ambos os objetos são injetados no Environment via environmentObject. Qualquer view filha pode acessá-los através de @EnvironmentObject sem passar pela cadeia de inicializadores.

@StateObject e inicialização com parâmetros

@StateObject suporta inicialização com qualquer parâmetro, mas com uma ressalva importante: o inicializador é chamado apenas uma vez. Em renderizações subsequentes de body, novos valores nos parâmetros são ignorados. Isso significa que se você passar @State var id: Int = 5 para @StateObject var vm = ViewModel(id: id), quando id mudar, o ViewModel não receberá o novo valor.

Para resolver este problema, use onReceive ou onAppear para sincronização. Assine as alterações de parâmetros dentro do ViewModel via Combine ou passe parâmetros através do método .onChange(of:) no nível da view. Uma alternativa é usar @ObservedObject em vez de @StateObject se o objeto deve responder dinamicamente a alterações externas.

swift
struct DetailView: View {
    let itemId: Int
    @StateObject var viewModel = DetailViewModel()

    var body: some View {
        Text(viewModel.title)
            .onAppear { viewModel.load(id: itemId) }
    }
}

A abordagem correta: DetailView recebe itemId como uma propriedade let (passada através do inicializador da estrutura), e @StateObject cria DetailViewModel sem parâmetros. Em onAppear, o método load(id:) é chamado para carregar dados para o ID recebido. Isso garante que o ViewModel seja criado pelo mecanismo @StateObject, mas os dados são carregados em cada aparição da view com o ID atual.

Erros comuns com @StateObject

O principal erro—usar @StateObject em views filhas que recebem o objeto de um pai. Se ParentView cria @StateObject model, e ChildView declara @StateObject var model: ModelType (com um parâmetro padrão), a ChildView criará sua própria instância independente. Os objetos pai e filho não estarão conectados, e as alterações em um não refletirão no outro.

O segundo erro—colocar @StateObject em List ou ForEach. Cada elemento da lista cria seu próprio @StateObject, levando a múltiplas instâncias independentes. Para listas, a abordagem correta é passar um único ObservableObject para todos os elementos via @ObservedObject ou usar estruturas Identifiable com @State dentro de List.

O terceiro problema—falta de limpeza em deinit. O @StateObject vive por todo o ciclo de vida da view. Se o objeto cria temporizadores, assinaturas Combine ou solicitações de rede, o deinit deve cancelá-los. Caso contrário, vazamentos de memória e trabalho contínuo em segundo plano após o fechamento da tela são inevitáveis. Sempre use um armazenamento Cancellable do Combine ou invalide temporizadores em deinit.

Perguntas frequentes

Quando o @StateObject foi introduzido no SwiftUI?

@StateObject foi adicionado no SwiftUI 2.0 na WWDC 2020 junto com iOS 14, macOS 11, watchOS 7 e tvOS 14. Antes disso, o @ObservedObject era a única maneira de trabalhar com ObservableObject, o que frequentemente levava a bugs de perda de dados.

O @StateObject pode ser opcional?

Não, @StateObject não suporta tipos Optional. O objeto deve ser inicializado na declaração. Se precisar de um objeto opcional, use @ObservedObject ou @EnvironmentObject com um tipo opcional.

Como verificar se o @StateObject é criado apenas uma vez?

Adicione print(#function) ao inicializador e deinit do ObservableObject. Se init não for chamado em renderizações—@StateObject está funcionando corretamente. Se init for chamado toda vez—substitua @ObservedObject por @StateObject.

O @StateObject pode ser usado com UIKit via UIHostingController?

Sim, @StateObject funciona em views SwiftUI incorporadas no UIKit via UIHostingController. O ciclo de vida do objeto está vinculado à view SwiftUI, não ao UIViewController. Se a view SwiftUI for substituída, o @StateObject é destruído.

O que é melhor: um @StateObject com ViewModel grande ou vários pequenos?

Vários @StateObjects pequenos com responsabilidades separadas. Isso melhora a testabilidade, reutilização e desempenho—quando um objeto muda, apenas as partes subscritas da interface são redesenhadas, não a view inteira.

Resumo

  • @StateObject — Property Wrapper para criar e possuir ObservableObject em uma view
  • Única instância — o objeto não é recriado em renderizações subsequentes de body
  • Fonte da verdade — @StateObject na view raiz garante estabilidade de dados para a hierarquia
  • Regra de seleção — @StateObject para criar, @ObservedObject para receber um objeto pronto
  • Inicialização — parâmetros em @StateObject são calculados uma vez, atualizações não são rastreadas
  • Deinit — limpeza obrigatória de temporizadores e assinaturas no deinit do ObservableObject
  • iOS 14+ — @StateObject está disponível desde iOS 14, macOS 11, watchOS 7, tvOS 14

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.

Discutir o projeto

Leia também