0

This is a followup question to How to share published model between two view models in SwiftUI?.

In order to make nested published models really publish, I used an approach described here: How to tell SwiftUI views to bind to nested ObservableObjects. So far so good, the manual objectWillChange passing works and the changes are getting published by the parent view model.

But if I'm trying to use custom publishers, which tap into the published nested model, they do not function as I am used to (With regular published properties). Here is a simplified example.

Am I missing something?

import SwiftUI
import Combine

struct ContentView: View {
    @StateObject var contentViewModel = ContentViewModel()
    var body: some View {
        VStack{
            // Both toggle together, indicating that objectWillChange works correctly
            ModelEditView(model: contentViewModel.model)
            ModelEditView(model: contentViewModel.model)
            // This toggle binds another not nested published property
            Toggle("Not Nested Toggle", isOn: $contentViewModel.isToggled)
            // Custom toggleChangedPublisher only works with the not nested property
            Text(contentViewModel.internalChangedValue ? "true" : "false")
        }
    }
}

class Model: ObservableObject {
    @Published var nestedIsToggled = false
}

class ContentViewModel: ObservableObject {
    //Nested Model
    @Published var model = Model()
    //Not nested model equivalent
    @Published var isToggled = false
    //Property only altered by the custom toggleChangedPublishers
    @Published var internalChangedValue = false
    
    var anyCancellables = Set<AnyCancellable>()
    
    init() {
        // This should notify the "parent" ContentViewModel of changes in nested model
        anyCancellables.insert(model.objectWillChange.sink { [weak self] (_) in
            self?.objectWillChange.send()
        })
        nestedToggleChangedPublisher
            .receive(on: RunLoop.main)
            .assign(to: \.internalChangedValue, on: self)
            .store(in: &anyCancellables)
        toggleChangedPublisher
            .receive(on: RunLoop.main)
            .assign(to: \.internalChangedValue, on: self)
            .store(in: &anyCancellables)
        
    }
    private var nestedToggleChangedPublisher: AnyPublisher<Bool, Never> {
        // Not working on change of model
        $model
            .map({return $0.nestedIsToggled})
            .eraseToAnyPublisher()
    }
    

    private var toggleChangedPublisher: AnyPublisher<Bool, Never> {
        // Is working when toggled
        $isToggled
            .eraseToAnyPublisher()
    }
}

Thanks for your support!

adelmachris
  • 332
  • 2
  • 13

1 Answers1

0

Maybe I'm missing the point but couldn't you just use computed properties?

class ContentViewModel: ObservableObject {
    @Published var model = Model()

    var isToggled: Bool { model.nestedIsToggled }
}
Casper Zandbergen
  • 3,419
  • 2
  • 25
  • 49
  • In the example this would be totally valid, but my point is to use combines combination of publishers, mapping etc. later on. But thanks for the suggestion. – adelmachris Jan 02 '21 at 13:23
  • Alright that's what I thought. I have never used the Combine map that you are using so I don't know how to help you. – Casper Zandbergen Jan 02 '21 at 13:28