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 SomeSwiftUIView
Andrei 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")
}
}
}