curioustechizen
02/06/2025, 9:16 AMcurioustechizen
02/06/2025, 9:17 AM// ProgressContainerIos.kt
fun ProgressContainer(
progressState: ProgressState,
createUiViewController: () -> UIViewController
) UIViewController = ComposeUIViewController {
ProgressContainer(
progressState = progressState,
modifier = Modifier.fillMaxSize()
) {
UIKitViewController(
factory = createUiViewController,
modifier = Modifier.fillMaxSize(),
)
}
}curioustechizen
02/06/2025, 9:22 AM//SomeViewUIViewControllerRepresentable.swift
struct SomeViewUIControllerRepresentable: UIViewControllerRepresentable {
let someState: SomeState
func makeUIViewController(context: Context) -> UIViewController {
ProgressContainerIoskt.ProgressContainer(
progressState: someState.progressState,
createUiViewController: {
let swiftUIView = SomeSwiftUIView(someState: someState)
return UIHostingController(rootView: swiftUIView)
}
)
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
// Perhaps I need to do something here for the state to propagate?
}
}curioustechizen
02/06/2025, 9:22 AMstruct SomeSwiftUIView: View {
let someState: SomeState
var body: some View {
Text(someState.name)
}
}curioustechizen
02/06/2025, 9:23 AMsomeState updates, I don't see my SomeSwiftUIView UI change.curioustechizen
02/06/2025, 9:24 AMlet constants from the UIVCRepresentable down into the SwiftUI View but I don't know how to fix thatRichard
02/06/2025, 9:24 AMcurioustechizen
02/06/2025, 9:30 AMRichard
02/06/2025, 9:32 AMcurioustechizen
02/06/2025, 9:42 AM@State var polygons: Polygons? in your SwiftUI View and in my case that is not even required.
In my case I have the equivalent of let polygons: Polygons because the state updates happen in a KMP ViewModel.Andrei Salavei
02/06/2025, 10:13 AMcurioustechizen
02/06/2025, 11:55 AMcurioustechizen
02/06/2025, 11:57 AMSomeViewUIControllerRepresentable passes in someState and in that struct I can see the state value changing.
However I don't see the changes inside SomeViewUIControllerRepresentable or its callees like SomeSwiftUIViewAndrei Salavei
02/06/2025, 12:16 PMcurioustechizen
02/06/2025, 1:31 PMUIViewControllerRepresentable in the tree?curioustechizen
02/06/2025, 1:36 PMlet constants to inner SwiftUI views works just fine. The key here is that the state change is happening in the outermost view (using a ViewModel which is implemented as an ObservableObject)
struct MyApp: View {
@StateObject var myViewModel = MyViewModel()
var body: some View {
One(state: myViewModel.state)
}
}
struct One: View {
let state: MyState
var body: some View {
VStack {
Text("One")
Two(state: state)
}
}
}
struct Two: View {
let state: MyState
var body: some View {
VStack {
Text("Two")
}
}
}Andrei Salavei
02/06/2025, 1:36 PMSomeSwiftUIView does not track changes in the SomeState because it does not have attribute @ObservedObject. Also, SomeState must properly implement ObservableObject to make it possible.Andrei Salavei
02/06/2025, 1:37 PMMyState in your sample?curioustechizen
02/06/2025, 1:38 PMSomeViewUIControllerRepresentable is responsible for this state management. This caller is using an ObservableObject/StateObject mechanism to be notified of state changes.curioustechizen
02/06/2025, 1:39 PMMyState is a Kotlin data class.Andrei Salavei
02/06/2025, 1:40 PMAndrei Salavei
02/06/2025, 1:52 PMupdateUIViewController will be called every time (except the very first call), SwiftUI rebuilds its view with updated someState. So you just need to either pass someState inside your ProgressContainerIoskt.ProgressContainer in the updateUIViewController somehow. Or convert SomeState to observable object, and let SwiftUI to use its refresh mechanism.curioustechizen
02/06/2025, 1:55 PMupdateUIViewController is for (for some reason I've been totally ignoring that method).curioustechizen
02/06/2025, 1:57 PMProgressContainerIoskt.ProgressContainer that I currently return from makeUIViewController
• again pass an updated state to this value in the updateUIViewController functionAndrei Salavei
02/06/2025, 2:08 PMuiViewController argument
> again pass an updated state to this value in the updateUIViewController function
Exactly, but it's a bit complicated with SwiftUI as it does not allow to change state from outside. So with current context, the only way I can see it to "convert SomeState to observable object, and let SwiftUI to use its refresh mechanism."curioustechizen
02/07/2025, 1:02 PMUIViewControllerRepresentable in the tree: This is not specific to KMP. And the answer is you cannot use stateless SwiftUI views in this case (you cannot use let states; you must replace them with @Binding var state: MyState
2. How to propagate state from the updateUIViewController function to a Composable. For this I took inspiration from this blog post.curioustechizen
02/07/2025, 1:22 PMstruct MyApp: View {
@StateObject var myViewModel = MyViewModel()
@State private var stateBinding: MyState
var body: some View {
One(state: $stateBinding)
.onChange(of: myViewModel.state) { state in
self.stateBinding = state
}
}
}
struct One: UIViewControllerRepresentable {
@Binding var state: MyState
func makeUIViewController(context: Context) -> UIViewController {
let swiftUIView = VStack {
Text("One")
Two(state: $state)
}
return UIHostingController(rootView: swiftUIView)
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
struct Two: View {
@Binding var state: MyState
var body: some View {
VStack {
Text("Two")
}
}
}