@StateObject e @ObservedObject ambos conectam um ObservableObject a uma visualização SwiftUI. A diferença-chave é a propriedade: use @StateObject quando o view cria o objeto e @ObservedObject quando o view recebe-o de outro lugar.
A escolha errada da embalagem pode causar sua model ser recriada sempre que SwiftUI reconstrói uma visualização. Eu cometi esse erro porque ambas as wrappers pareciam produzir o mesmo resultado inicialmente. A diferença só se torna visível quando seu view começa a possuir estado mutável.
@StateObject vs @ObservedObject
Você pode fazer a escolha correta respondendo uma pergunta: este view cria o objeto?
- Use
@StateObjectquando o view cria e possui umObservableObject. - Use
@ObservedObjectquando o view recebe umObservableObjectde um pai. - Nunca crie um objeto em linha usando
@ObservedObject. - Não use
@StateObjectem múltiplas visualizações para a mesma instância.
Tanto as wrappers quanto os observadores desencadeiam atualizações de visualização quando uma propriedade @Published muda. No entanto, apenas @StateObject pede ao SwiftUI que preserve a vida útil do objeto para o view.
O que é um @ObservedObject?
A propriedade wrapper @ObservedObject observa um ObservableObject que outro view possui. Mudanças publicadas pelo objeto fazem com que o SwiftUI reavalie o view observador.
@MainActor
final class CounterViewModel: ObservableObject {
@Published private(set) var count = 0
func incrementCounter() {
count += 1
}
}
struct CounterView: View {
@ObservedObject var viewModel: CounterViewModel
var body: some View {
VStack {
Text("Count is: \(viewModel.count)")
Button("Increment Counter") {
viewModel.incrementCounter()
}
}
}
}
CounterView recebe seu view model através de seu inicializador. Isso faz @ObservedObject a escolha correta, já que o view não controla a vida útil do objeto.
Qual é um @StateObject?
O wrapper de propriedade @StateObject cria armazenamento persistente para um ObservableObject. Use-o quando a visualização cria o objeto ela mesma:
struct CounterContainerView: View {
@StateObject private var viewModel = CounterViewModel()
var body: some View {
CounterView(viewModel: viewModel)
}
}
O SwiftUI pode recriar o valor CounterContainerView muitas vezes, mas preserva o objeto de estado associado à identidade da visualização. A mesma instância CounterViewModel permanece disponível durante esses updates.
A marcação da propriedade como private também comunica a posse. Um pai não pode acidentalmente injetar outro valor inicial que o SwiftUI ignorará posteriormente.
Por que um @ObservedObject inline redefine
Você pode esperar que o seguinte contador preserve seu modelo de visualização. No entanto, a visualização cria um objeto inline usando @ObservedObject, que não fornece armazenamento para a vida útil do objeto:
struct ResettingCounterView: View {
@ObservedObject private var viewModel = CounterViewModel()
var body: some View {
VStack {
Text("Count is: \(viewModel.count)")
Button("Increment Counter") {
viewModel.incrementCounter()
}
}
}
}
struct RandomNumberView: View {
@State private var randomNumber = 0
var body: some View {
VStack {
Text("Random number is: \(randomNumber)")
Button("Randomize number") {
randomNumber = Int.random(in: 0..<1000)
}
ResettingCounterView()
}
}
}
Alterar o número aleatório atualiza a visualização pai. O SwiftUI pode recriar o valor ResettingCounterView e substituir seu CounterViewModel inline, causando o contador a ser reiniciado.
Você pode corrigir a redefinição alterando o wrapper de propriedade para @StateObject:
@StateObject private var viewModel = CounterViewModel()
O SwiftUI agora preservará o modelo de visualização durante a vida útil da identidade da visualização. Alternativamente, deixe o pai possuir o objeto de estado e injete-o em um filho usando @ObservedObject.
StateObject vs. ObservableObject
StateObject e ObservableObject não são alternativas. ObservableObject é o protocolo adotado pelo seu modelo, enquanto @StateObject é a propriedade wrapper que uma view SwiftUI usa para possuir esse modelo.
Dito de outra forma, seu modelo segue ObservableObject, e a view escolhe entre @StateObject e @ObservedObject com base na posse.
@Observable
Para novos códigos direcionados a iOS 17 ou posterior, recomendo usar o Observation framework em vez disso. Você pode substituir ObservableObject, @Published e @StateObject por @Observable e @State:
import Observation
import SwiftUI
@Observable
@MainActor
final class CounterViewModel {
private(set) var count = 0
func incrementCounter() {
count += 1
}
}
struct CounterView: View {
@State private var viewModel = CounterViewModel()
var body: some View {
Text("Count is: \(viewModel.count)")
}
}
Um modelo injetado @Observable geralmente não requer wrapper de propriedade. Adicione apenas @Bindable quando a view filha precisa de vinculações às propriedades do modelo.
@StateObject e @ObservedObject ainda são relevantes ao suportar sistemas operacionais mais antigos ou trabalhar com modelos existentes ObservableObject. Leia meu guia de migração @Observable para a implementação moderna completa.
Quando você deve usar cada wrapper de propriedade?
Use as seguintes regras ao trabalhar com ObservableObject:
- A view cria o objeto: use
@StateObject private var. - A view recebe o objeto: use
@ObservedObject var. - Várias views irmãs observam um único objeto: deixe a mãe possuir e injete a mesma instância.
- Novos códigos direcionados a iOS 17 ou posterior: considere
@Observablecom@State.
Prefiro tornar a posse visível na declaração. Um objeto de estado privado pertence claramente à view, enquanto um objeto observado aparece na inicialização da view como uma dependência.
Conclusão
@StateObject e @ObservedObject ambos observam mudanças, mas apenas @StateObject possui a vida útil do modelo. Use um objeto de estado quando a visualização cria a instância e um objeto observado quando a instância é injetada.
Para projetos modernos que visam iOS 17 ou posterior, considere migrar para @Observable. A mesma regra de propriedade ainda se aplica, mas você usará @State para um modelo proprietário e uma propriedade regular ou @Bindable para um modelo injetado.
Caso queira melhorar seus conhecimentos sobre SwiftUI, consulte a página da categoria SwiftUI. Sinta-se à vontade para entrar em contato comigo ou me enviar uma mensagem no Twitter se tiver alguma dica adicional ou feedback.

