Joris
Joris

Reputation: 6296

Create a Publishable property in a view controller, pass it to a SwiftUI View and listen to changes inside view controller?

This is what I am trying to achieve:

class MyVC: UIViewController {
    @State var myBoolState: Bool = false

    private var subscribers = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()

        myBoolState.sink { value in .... }.store(in:&subscribers)
    }

    func createTheView() {
        let vc = UIHostingController(rootView: MySwiftUIView(myBoolState: $myBoolState))
        self.navigationController!.pushViewController(vc, animated: true)
    }
}

struct MySwiftUIView: View {
    @Binding var myBoolState: Bool

    var body: some View {
          Button(action: {
              myBoolState = true
          }) {
              Text("Push Me")
          }

    }

}

But the above of course does not compile.

So the question is: can I somehow declare a published property inside a view controller, pass it to a SwiftUI View and get notified when the SwiftUI view changes its value?

Upvotes: 3

Views: 1353

Answers (1)

Asperi
Asperi

Reputation: 257789

The @State wrapper works (by design) only inside SwiftUI view, so you cannot use it in view controller. Instead there is ObsevableObject/ObservedObject pattern for such purpose because it is based on reference types.

Here is a demo of possible solution for your scenario:

import Combine

class ViewModel: ObservableObject {
    @Published var myBoolState: Bool = false
}

class MyVC: UIViewController {
    let vm = ViewModel()
    
    private var subscribers = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()

        vm.$myBoolState.sink { value in
            print(">> here it goes")
        }.store(in:&subscribers)
    }

    func createTheView() {
        let vc = UIHostingController(rootView: MySwiftUIView(vm: self.vm))
        self.navigationController!.pushViewController(vc, animated: true)
    }
}

struct MySwiftUIView: View {
    @ObservedObject var vm: ViewModel

    var body: some View {
          Button(action: {
            vm.myBoolState = true
          }) {
              Text("Push Me")
          }
    }
}

Upvotes: 4

Related Questions