

Compose Multiplatform — managing UI State on iOS
source link: https://proandroiddev.com/compose-multiplatform-managing-ui-state-on-ios-45d37effeda9
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Compose Multiplatform — managing UI State on iOS

Compose Multiplatform stands as a declarative framework dedicated to facilitating the seamless sharing of UIs across diverse platforms. Based on Kotlin and Jetpack Compose, JetBrains introduces its distinctive perspective on a cross-platform¹ framework.
Within the scope of this story, we shall delve into two distinct strategies for managing UI State. In essence, we explore techniques to update your Compose-driven UI, particularly when employing it to share layouts with iOS. Prior to embarking on our exploration, it is assumed that the reader possesses understanding of Kotlin Multiplatform, Jetpack Compose, and SwiftUI.
While not obligatory, familiarity with my story on the subject of Sharing UI State management with Kotlin Multiplatform Mobile could prove beneficial.
Intro
When you use Compose Multiplatform on iOS, the Kotlin code for your UI is compiled to native code using Kotlin/Native. This native code is then used to create a UIKit-based UI that runs on the iOS platform. Compose Multiplatform for iOS provides the Kotlin APIs for building Compose UIs on iOS. This library bridges the gap between the Kotlin/Native code and the UIKit framework.
Kotlin code
|
v
Kotlin/Native compiler
|
v
Native code for iOS
|
v
UIKit framework
|
v
Compose UI
The alpha² release of Compose Multiplatform introduces a prototype for bidirectional interaction at the UI level. With the help of UIKitView
, you can seamlessly integrate intricate platform-specific widgets — such as maps, web views, media players, and camera feeds — into your shared user interface. Conversely, employing ComposeUIViewController
allows you to nest Compose Multiplatform screens within SwiftUI applications, facilitating a gradual integration of Compose Multiplatform into your iOS apps.
Our emphasis will be on the latter.
No state management on iOS
By this, I’m referring to a screen implementation in which state changes are managed by the multiplatform (shared-ui) module, in Kotlin. This implies that on the iOS side, our task involves just creating a container to display it.
Let me simplify this with an example. We’ll create a Composable containing two buttons for navigating between screens. On iOS, we’ll include this Composable and set up our navigation.
In the shared-ui module, we create a function that gives us a UIViewController
. This function wraps our Composable implementation within ComposeUIViewController
:
fun selector(onClickA: () -> Unit, onClickB () -> Unit): UIViewController {
return ComposeUIViewController { /*compose implementation...*/ }
}
In iOS we create a UIViewControllerRepresentable
that will import the ComposeUIViewController
:
private struct SelectorUIViewController: UIViewControllerRepresentable {
let showScreenA: () -> Void
let showScreenB: () -> Void
func makeUIViewController(context: Context) -> UIViewController {
return SharedViewControllersKt.selector(onClickA: showScreenA, onClickB: showScreenB)
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
We finish it by setting up our navigation:
private enum Destination: Hashable {
case screenA
case screenB
}
struct HomeScreen: View {
@State var navigation = NavigationPath()
var body: some View {
NavigationStack(path: $navigation) {
SelectorUIViewController(
showScreenA: { navigation.append(Destination.screenA) },
showScreenB: { navigation.append(Destination.screenB) }
)
.navigationDestination(for: Destination.self) { destination in
switch destination {
case .screenA:
ScreenA()
case .screenB:
ScreenB()
}
}
.ignoresSafeArea()
}
}
}
And it’s done. We have a Composable inside a SwiftUI View.
Mixed state management
By this, I’m referring to a screen implementation in which state changes are not managed by the shared-ui module. This implies that on the iOS side, we must forward state changes to the shared-ui module.
In this example, we have a SwiftUI View where an observable ViewModel
handles the state, passing changes to State
and Binding
properties:
struct StatusScreen: View {
@StateObject private var viewModel = StatusViewModel()
@State private var status: String = ""
var body: some View {
StatusUIViewController(
status: $status,
action: { viewModel.changeStatus() }
)
.onReceive(viewModel.$state) { new in
status = new.status
}
.ignoresSafeArea()
}
}
Our UIViewControllerRepresentable
:
struct StatusUIViewController: UIViewControllerRepresentable {
@Binding var status: String
let action: () -> Void
func makeUIViewController(context: Context) -> UIViewController {
return SharedViewControllers().statusComposable(status: status, click: action)
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
//how to update the composable state when binding values changes...?
}
}
Despite the invocation of updateUIViewController
, the Multiplatform Compose API lacks³ a mechanism to communicate these changes to the Composable.
Thankfully, there’s a workaround we can employ. Within our ComposeUIViewController
, we gather state changes using a MutableStateFlow
from kotlinx.coroutines.flow
or mutableStateOf
from compose.runtime
. Subsequently, we expose a function for iOS to invoke, facilitating the emission of these changes:
object SharedViewControllers {
private data class ViewState(val status: String = "")
private val state = MutableStateFlow(ViewState())
fun statusComposable(click: () -> Unit): UIViewController {
return ComposeUIViewController {
with(state.collectAsState().value) {
StatusComposable(state.status, click)
}
}
}
fun updateStatusComposable(status: String) {
state.update { it.copy(status = status) }
}
}
And then our updateUIViewController
becomes:
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
SharedViewControllers().updateStatusComposable(status: status)
}
While not the most elegant approach, it’s essential to keep in mind that we’re working with an alpha version. With time, we can anticipate significant enhancements and refinements.
Conclusions
Just like Kotlin Multiplatform, Compose Multiplatform is tailored for mobile developers. Consequently, I see it as a prime candidate for becoming the go-to cross-platform solution for this community. It’s not so much a question of if, but rather a matter of when.
In its current alpha stage on iOS, Compose Multiplatform offers impressive flexibility for gradual adoption and experimentation. However, it’s important to note that this won’t be a universal solution; rather, it will be another valuable tool in our toolkit. I envision specific use cases where Compose Multiplatform can provide substantial benefits.
Let’s witness its evolution in the near future.
As always, I hope you find this article useful, thanks for reading. You can explore these strategies in a playground available here:
[1]: In the mobile realm, cross-platform frameworks enable using a single technology stack for full implementation (layouts + business logic), e.g., Flutter, React Native. Conversely, multi-platform frameworks permit sharing specific system elements (business logic), while each platform determines its own layout implementation and stack.
[2]: As of May 18, 2023, Compose Multiplatform for iOS Is in Alpha.
[3]: As of Aug 12, 2023, this capability is not yet available — Ability to define a UIViewController type #3478
I want to thank Dima Avdeev from JetBrains for taking the time to discuss and share ideas on the topic.
🎉 Featured in Android Weekly #583
Recommend
-
6
News Compose Multiplatform goes Alpha, unifying Desktop, Web, and Android UIs
-
8
News Compose Multiplatform Goes Beta: Stabilized APIs, Compatibility with Google’s Compose Artifacts, and More ...
-
4
JetBrains Compose Multiplatform Reaches Beta Oct 31, 2021 2...
-
5
The state of managing state (with Compose) Posted by Jake Wharton on November 11, 2021 Five years ago the Cash App Android client started splitting ou...
-
8
News Compose Multiplatform 1.0 is going live!
-
8
Home Android & Kotlin Tutorials Managing State in Jetpac...
-
8
Mobile News Compose Multiplatform fo...
-
9
JetBrains Compose Multiplatform for iOS Reaches Alpha May 28, 2023 2...
-
2
Compose-Multiplatform在Android和iOS上的实践 作者:狐友段梦瑶 2023-11-02 07:53:22 Compose-Multiplatform目前虽然还不成熟,但通过对其原理的分析,我们可以预见的是,结合KMM,未来将成为跨平台的有力竞争者...
-
4
Compose walk-through part 2: Managing state Android • Dec 22, 2023 In Jetpack Compose, managing state is a core aspect of buildi...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK