SwiftUI - immutable @State variable when change it "from outside" via UIHostController

60 Views Asked by At

Situation

During migration of some UIKit view classes to SwiftUI, I have faced next case:

I have a simple view class, responsible for data fetching and displaying. Its structure is pretty simple and can be recreated in swiftui in next way:

struct NewView: View {
    
    @State private var isLoader: Bool = false
    
    var body: some View {
        VStack {
            Text("Data here")
            
            if isLoader {
                Text("Loading")
            }
        }
    }
    
    func startNetworkRequest() {
        isLoader = true
            
        // Data fetching request here
        isLoader = false
    }
}

In order to use new view in UIKit env, I've made a host controller:

class HostVC: UIHostingController<NewView> {
    
    convenience
    init() {
        self.init(rootView: NewView())
    }
    
    func startNetworkRequest() {
        rootView.startNetworkRequest()
    }
}

The problem:

If we call startNetworkRequest on hostVC instance, isLoader value don't change

print("isLoading value before update: \(isLoading)")
isLoading = true
print("isLoading value before request: \(isLoading)")

It gives me:

isLoading value before update: false
isLoading value before update: false

Question:

Is there any way to implement expected behaviour:

  • To have UI written in SwiftUI
  • It should be available in UIKit classes (via UIHostController, I assume)
  • To be able to trigger some view events via host controller methods

Kind of solution:

I moved startNetworkRequest call inside the view (in onAppear) and it worked as expected. It doesn't solve my #3 expected behaviour point, but works for current task so I've to go with it for now

My assumption:

When we access view via hostVC, we are working with its immutable instance (since it's struct) and any property changes (although they are correct from syntax perspective) aren't applied.

When we work "from inside", we work with view container, which can modify its content and replace old struct instance with new one, modified.

1

There are 1 best solutions below

2
Viktor On

It seems my assumption was kind of correct and we can't edit view state from from outside

As a potential solution, we can store needed property as reference value or in another place:

class BoolObservable: ObservableObject {
    @Published var value: Bool

    init(value: Bool) {
        self.value = value
    }
}

Usage:

private var isLoading: BoolObservable = BoolObservable(value: false)

if isLoading.value { ..., isLoading.value = true etc