Comment dire aux vues SwiftUI de se lier à des objets observables imbriqués

18

J'ai une vue SwiftUI qui prend un EnvironmentObject appelé appModel. Il lit ensuite la valeur appModel.submodel.countdans sa bodyméthode. Je pense que cela lie mon avis à la propriété countsur submodelafin qu'il re-Renders lorsque les mises à jour de propriété, mais cela ne semble pas se produire.

Est-ce un bug? Et sinon, quelle est la manière idiomatique de faire en sorte que les vues soient liées 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 countpropriété augmente mais l'étiquette ne se met pas à jour.

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

rjkaplan
la source
Je conçois également mon application comme ça. J'ai généralement un objet App global dans le développement d'applications passé. Est-ce que quelqu'un d'autre pense que cette conception d'une super classe "App" comme variable d'environnement deviendra une pratique standard? J'envisageais également d'utiliser plusieurs EnvironmentObjects, mais cela a été difficile à maintenir.
Michael Ozeryansky du

Réponses:

22

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 AppModelcapture l'événement Submodelet l'envoyez à la vue

Éditer:

Si vous n'avez pas besoin SubModeld'être en classe, vous pouvez essayer quelque chose comme ceci:

struct Submodel{
    var count = 0
}

class AppModel: ObservableObject {
    @Published var submodel: Submodel = Submodel()
}
Sorin Lica
la source
Merci, c'est utile! Lorsque vous dites "Les modèles imbriqués ne fonctionnent pas encore dans SwiftUI", savez-vous avec certitude qu'ils sont planifiés?
rjkaplan
Je ne suis pas sûr, mais à mon avis, cela devrait fonctionner, j'utilise également quelque chose de similaire dans mon projet, donc si je trouve une meilleure approche, je viendrai avec un montage
Sorin Lica
@SorinLica Doit Submodelêtre de ObservableObject type?
Farhan Amjad
Ça marche! Super solution!
Md Shahed Hossain
1

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.

zdravko zdravkin
la source
Pourriez-vous mettre à jour le code? il contient de nombreuses erreurs de compilation
DevB2F
-2

Cela ressemble à un bug. Lorsque je mets à jour le xcode vers la dernière version, il fonctionne correctement lors de la liaison à des ObservableObjects imbriqués

norains
la source
Pouvez-vous clarifier la version de xcode sur laquelle vous vous trouvez actuellement et qui fonctionne? J'ai actuellement Xcode 11.0 et je rencontre ce problème. J'ai eu du mal à obtenir la mise à niveau vers 11.1, elle ne passera pas à 80%.
Michael Ozeryansky