iOS folks, hello I have question regarding KMP sta...
# ios
d
iOS folks, hello I have question regarding KMP state and SUI. Can you share your experience on how you manage state in SwiftUI from a shared StateHolder? I have a StateHolder initialized in a native ViewModel (VM):
Copy code
class MyVm: ObservableObject {
    @Published private(set) var state: ScreenState

    func observeViewState() async {
        for await kmpState in stateHolder.state {
            let viewState = onEnum(of: kmpState)
            // Update state here
        }
    }
}

data class ScreenState(
    val value1: SomeClass1,
    val value2: SomeClass2
)
My screen in SwiftUI uses properties to render different parts of the screen:
value1
renders a list of items, and
value2
renders a card. As I understand it and what I see in the logs, if any property in the
@Published
property changes, it causes the entire screen to re-render (both components). That is, if
value1
changes, I expect the list of items to re-render, and the card should remain untouched since there were no changes. So far, I see that for each property in the state, I need to create its own
@Published
variable and pass it specifically to the component. This works on a simple screen, but if I have nested views and more than 3-5 such variables, to update a specific view in the hierarchy, I need to pass a specific
@Published
variable that will control the re-rendering when the corresponding variable changes. What is the preferable approach? Ty in advance
a
Hi there! I would suggest to have one observable object per UI component. But don't split every label. SwiftUI is a quite performant framework. Usually performance problems occur only drawing large lists with thousands items without proper optimisation. Another tip from architecture perspective - try to use separate set of UI models. Your SwiftUI code should know nothing about KMP models. It's better approach in terms of decoupling architectural layers of the app. As a benefit - ability to build UI without re-compiling Kotlin code.
❤️ 1
d
@Andrei Salavei thank you for the response!
@Andrei Salavei Just to verify I’ve got your point State
Copy code
KMPState(
 val user: User,
 val habits: List<Habits>,
 val address: List<Address>
)
data class User(id,name, password)
data class Habits(id, name, isActive)
data class Address(country, city, street)
VM
Copy code
class iOSViewModel : ObservableObject {
	
   @Published private(set) var user: User	
   @Published private(set) var habits: Habits
   @Published private(set) var address: Address

	func observeViewState() async {
    for await kmpState in stateHolder.state {
      let viewState = onEnum(of: kmpState)
      // Assume we map to UI models
    }
  }
 }
View
Copy code
struct ScreenView: View {

	@StateObject let viewModel = iOSViewModel.init()

	val body: View {
		VStack {
			UserView(viewModel.user)
			HabitsListView(viewModel.habits)
			AddressListView(viewModel.address)
		}
	}
}
ty
a
VM Here we should have observable object per component, not per screen. So, something like:
Copy code
class ObservableWrapper<T>: ObservableObject {
    @Published var value: T	
}

class MyScreenViewModel {
   let user: ObservableWrapper<User>	
   let habits: ObservableWrapper<Habits>
   let address: ObservableWrapper<Address>
 }
And here:
Copy code
struct MyScreenView: View {

    let viewModel: MyScreenViewModel // Should pass as a parameter

	val body: View {
		VStack {
			UserView(viewModel.user)
			HabitsListView(viewModel.habits)
			AddressListView(viewModel.address)
		}
	}
}
And from the architecture perspective, it's better to have
State
classes written in Swift.
1
d
@Andrei Salavei thank you one more time
👍 1