Jak powiedzieć widokom SwiftUI, aby powiązało się z zagnieżdżonymi obiektami ObservableObjects

18

Mam widok SwiftUI, który pobiera obiekt EnvironmentObject o nazwie appModel. Następnie odczytuje wartość appModel.submodel.countw bodymetodzie. Spodziewam się to wiązać mój pogląd na własność countna submodeltak, że ponownie renderuje kiedy aktualizacje własności, ale to nie wydaje się zdarzyć.

Czy to błąd? A jeśli nie, jaki jest idiomatyczny sposób powiązania widoków z zagnieżdżonymi właściwościami obiektów środowiska w SwiftUI?

W szczególności mój model wygląda tak ...

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

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

A mój widok wygląda tak ...

struct ContentView: View {
  @EnvironmentObject var appModel: AppModel

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

Gdy uruchamiam aplikację i klikam etykietę, countwłaściwość rośnie, ale etykieta się nie aktualizuje.

Mogę to naprawić, przekazując appModel.submodeljako właściwość ContentView, ale chciałbym tego uniknąć, jeśli to możliwe.

rjkaplan
źródło
Tak też projektuję moją aplikację. Zwykle mam globalny obiekt App w poprzednich wersjach aplikacji. Czy ktoś jeszcze uważa, że ​​ten projekt klasy super „App” jako zmiennej środowiskowej stanie się standardową praktyką? Zastanawiałem się również nad użyciem wielu obiektów EnvironmentObject, ale było to trudne do utrzymania.
Michael Ozeryansky

Odpowiedzi:

22

Zagnieżdżone modele nie działają jeszcze w SwiftUI, ale możesz zrobić coś takiego

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()
        }
    } 
}

Zasadniczo twoje AppModelzdarzenie łapie wydarzenie Submodeli przesyła je dalej do Widoku

Edytować:

Jeśli nie musisz SubModelbyć klasą, możesz spróbować czegoś takiego:

struct Submodel{
    var count = 0
}

class AppModel: ObservableObject {
    @Published var submodel: Submodel = Submodel()
}
Sorin Lica
źródło
Dzięki, to jest pomocne! Kiedy mówisz „Modele zagnieżdżone jeszcze nie działają w SwiftUI”, czy wiesz na pewno, że są one zaplanowane?
rjkaplan
Nie jestem pewien, ale moim zdaniem to powinno działać, używam również czegoś podobnego w moim proj, więc jeśli znajdę lepsze podejście, przyjdę z edycją
Sorin Lica
@SorinLica powinien Submodelbyć ObservableObject typ?
Farhan Amjad
To działa! Niesamowite rozwiązanie!
Md Shahed Hossain
1

Wszystkie trzy ViewModels mogą się komunikować i aktualizować

// 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
}

Dziękuję Sorin za rozwiązanie Combine.

zdravko zdravkin
źródło
Czy możesz zaktualizować kod? ma wiele błędów kompilatora
DevB2F
-2

Wygląda jak robak. Kiedy aktualizuję xcode do najnowszej wersji, działa on poprawnie po powiązaniu z zagnieżdżonymi obiektami ObservableObjects

norains
źródło
Czy możesz wyjaśnić, która wersja xcode, na której obecnie pracujesz, działa? Obecnie mam Xcode 11.0 i mam ten problem. Mam problem z uaktualnieniem do wersji 11.1, nie da się go ukończyć w 80%.
Michael Ozeryansky