web-dev-qa-db-fra.com

Comment dire aux vues SwiftUI de se lier aux ObservableObjects imbriqués

J'ai une vue SwiftUI qui prend un EnvironmentObject appelé appModel. Il lit ensuite la valeur appModel.submodel.count dans sa méthode body. Je m'attends à ce que cela lie ma vue à la propriété count sur submodel afin qu'elle s'affiche à nouveau lorsque la propriété est mise à jour, mais cela ne semble pas se produire.

Est-ce un bug? Sinon, quelle est la manière idiomatique de lier des vues aux propriétés imbriquées des objets d'environnement dans SwiftUI?

Plus précisément, mon modèle ressemble à ceci ...

class Submodel: ObservableObject {
  @Published var count = 0
}

class AppModel: ObservableObject {
  @Published var submodel: Submodel = Submodel()
}

Et ma vue ressemble à ceci ...

struct ContentView: View {
  @EnvironmentObject var appModel: AppModel

  var body: some View {
    Text("Count: \(appModel.submodel.count)")
      .onTapGesture {
        self.appModel.submodel.count += 1
      }
  }
}

Lorsque j'exécute l'application et clique sur l'étiquette, la propriété count augmente mais l'étiquette ne se met pas à jour.

Je peux résoudre ce problème en passant appModel.submodel comme propriété de ContentView, mais j'aimerais éviter de le faire si possible.

16
rjkaplan

Les modèles imbriqués ne fonctionnent pas encore dans SwiftUI, mais vous pouvez faire quelque chose comme ça

class Submodel: ObservableObject {
    @Published var count = 0
}

class AppModel: ObservableObject {
    @Published var submodel: Submodel = Submodel()

    var anyCancellable: AnyCancellable? = nil

    init() {
        anyCancellable = submodel.objectWillChange.sink { (_) in
            self.objectWillChange.send()
        }
    } 
}

Fondamentalement, votre AppModel intercepte l'événement à partir de Submodel et l'envoie à la vue

Éditer:

Si vous n'avez pas besoin que SubModel soit une classe, vous pouvez essayer quelque chose comme ceci:

struct Submodel{
    var count = 0
}

class AppModel: ObservableObject {
    @Published var submodel: Submodel = Submodel()
}
16
Sorin Lica

Les trois ViewModels peuvent communiquer et se mettre à jour

// First ViewModel
class FirstViewModel: ObservableObject {
var facadeViewModel: FacadeViewModels

facadeViewModel.firstViewModelUpdateSecondViewModel()
}

// Second ViewModel
class SecondViewModel: ObservableObject {

}

// FacadeViewModels Combine Both 

import Combine // so you can update thru nested Observable Objects

class FacadeViewModels: ObservableObject { 
lazy var firstViewModel: FirstViewModel = FirstViewModel(facadeViewModel: self)
  @Published var secondViewModel = secondViewModel()
}

var anyCancellable = Set<AnyCancellable>()

init() {
firstViewModel.objectWillChange.sink {
            self.objectWillChange.send()
        }.store(in: &anyCancellable)

secondViewModel.objectWillChange.sink {
            self.objectWillChange.send()
        }.store(in: &anyCancellable)
}

func firstViewModelUpdateSecondViewModel() {
     //Change something on secondViewModel
secondViewModel
}

Merci Sorin pour la solution Combine.

1
zdravko zdravkin