compose-multiplatform
compose-multiplatform copied to clipboard
Ability to define a UIViewController type
The absence of a specific type for androidx.compose.ui.window.ComposeUIViewControllerand platform.UIKit.UIViewController presents a limitation in using updateUIViewController and, consequently, the associated Composable.
In scenarios where the iOS application requires management of Composable state through an iOS ViewModel, the availability of such types would prove to be advantageous:
struct SampleUIViewController: UIViewControllerRepresentable {
@Binding var status: String
let action: () -> Void
func makeUIViewController(context: Context) -> UIViewController {
return SharedViewControllers().sampleComposable(status: status, click: action)
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
//how to update the composable state when binding values changes?
}
}
struct ComposeScreen: View {
@StateObject private var viewModel = ViewModels.sampleViewModel()
@State private var status: String = ""
var body: some View {
SampleUIViewController(
status: $status,
action: { viewModel.doSomething() }
)
.onReceive(viewModel.$state) { new in
status = new.label()
}
.ignoresSafeArea()
}
}
I find this use-case quite intriguing, particularly in its potential to attract iOS developers and facilitate incremental adoption. Furthermore, it introduces the exciting possibility of incorporating Composables into a project without the necessity of sharing ViewModels. This offers a more flexible approach compared to a full-fledged Composable + ViewModel sharing strategy.
Currently, the workaround I've discovered involves incorporating a MutableStateFlow within the ComposeUIViewController, which responds to changes in state properties:
object SharedViewControllers {
private data class ComposeUIViewState(val status: String = "")
private val state = MutableStateFlow(ComposeUIViewState())
fun sampleComposable(click: () -> Unit): UIViewController {
return ComposeUIViewController {
with(state.collectAsState().value) {
Composable(state.status, click)
}
}
}
fun updateSampleComposable(status: String) {
state.update { ComposeUIViewState(status = status) }
}
}
on iosApp side:
struct SampleUIViewController: UIViewControllerRepresentable {
@Binding var status: String
let action: () -> Void
func makeUIViewController(context: Context) -> UIViewController {
return SharedViewControllers().sampleComposable(click: action)
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
SharedViewControllers().updateSampleComposable(status: status)
}
}
It works, but it's not very graceful.
Thanks for the report. We are currently iterating on API design and real usage examples like this are quite useful.
Thanks for the report. We are currently iterating on API design and real usage examples like this are quite useful.
Hello @elijah-semyonov, you can find here a working sample 😉
@GuilhE
What's currently preventing you from using Kotlin Objective-C object (a ViewModel basically), storing MutableState which could be changed inside update method? In this case your setContent could simply reference this object and this mutable state and execute differently based on its value.
@GuilhE What's currently preventing you from using Kotlin Objective-C object (a ViewModel basically), storing MutableState which could be changed inside
updatemethod? In this case yoursetContentcould simply reference this object and this mutable state and execute differently based on its value.
I believe that's the work around I'm using, basically I've a Singleton with a Flow/mutableState that the iosApp can call to update the state. The ComposeUIViewController observes this Flow/mutableState and changes accordingly.
We are drifting toward a design, where the state is managed outside of ComposeWindow for multiple reasons (scene destruction on removal from window hierarchy, for example). So it feels like a proper way to do and not just workaround.
Thanks for the article https://proandroiddev.com/compose-multiplatform-managing-ui-state-on-ios-45d37effeda9
We will discuss it. And try to make better solution for all your needs. But it will take some time.
Thanks for your work guys and for letting the community help! 🙏🏼
This might come in handy 😇 https://github.com/GuilhE/KMP-ComposeUIViewController
@elijah-semyonov @dima-avdeev-jb latest release changelog:
[1.1.0-ALPHA]
- Adds capability to generate .swift files with UIViewControllerRepresentables.
- Adds script to include those generated files into xcodeproj to be accessible in iOS project;
😇
@elijah-semyonov @dima-avdeev-jb
Hello guys! Just dropping by to share news about my new gradle plugin that simplifies the library setup.
Example:
plugins {
id("io.github.guilhe.kmp.plugin-composeuiviewcontroller") version "1.1.0"
}
ComposeUiViewController {
iosAppName = "Gradient"
targetName = "Gradient"
}
With this, all the code generation and export is handled automatically, no more complex configurations needed! 😊
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.