Continuing from my previous question, what is the best way to observe several state changes from a UIViewRepresentable? Previously, I required to only observe changes to one property userWon. However, as new functionality was introduced in the app, I need to now observe new changes. Assuming these could increase even more, is the best way to do this by adding more State properties and binding them to the UIViewRepresentable? It feels somewhat hacky at this point to add more and more bindings and onChange modifiers to observe for changes.
Example code is as follows:
struct GameView: View {
@State private var userWon: Bool = false
@State private var propA: Bool = false
@State private var propB: Int = 0
@State private var propC: Int = 0
...
...
...
@State private var propZ: Int = 0
@StateObject private var gameService = GameService()
var body: some View {
ZStack {
VStack {
GameViewRepresentable(userWon: $userWon,
propA: $propA,
propB: $propB,
propC: $propC,
...
propZ: $propZ) // <- Add more bindings??
.onChange(of: userWon) { newValue in
if newValue {
gameService.updateUserScore()
}
}
.onChange(of: propA) { newValue in
gameService.doSomethingA()
}
.onChange(of: propB) { newValue in
gameService.doSomethingB()
}
.onChange(of: propZ) { newValue in
gameService.doSomethingZ()
}
}
.clipped()
}
}
}
struct GameViewRepresentable: UIViewRepresentable {
typealias UIViewType = GameView
@Binding var userWon: Bool
func makeUIView(context: Context) -> GameView {
let gameView = GameView()
gameView.delegate = context.coordinator
return gameView
}
func updateUIView(_ uiView: GameView, context: Context) {
}
func makeCoordinator() -> GameViewCoordinator {
GameViewCoordinator(self)
}
class GameViewCoordinator: GameViewDelegate {
var parent: GameViewRepresentable
init(_ parent: GameViewRepresentable) {
self.parent = parent
}
func userWon() {
self.parent.userWon = true
}
}
}
Alternate approach that I can think of is:
- Create an envelope object/model in
gameServicethat contains the aforementioned properties to be observed and pass that as binding to theUIViewRepresentable. - Have
gameServiceobserve this enveloped object, probably usingCombine? for changes but I'm not sure how to do this or if it'd work at all. Any help is appreciated on how to do this efficiently.
You can move all the
@State vars into yourGameServiceas@Published vars.Then you can pass this to
GameViewRepresentablethrough an@ObservedObject, to avoid writing out all the states the game view needs.Since this is a
UIViewRepresentable, I think you would likely be updatinggamethrough its properties' setters (rather than say, aBindingproduced from the projected value ofObservedObject). In that case, you can just usedidSetto detect the changes:Otherwise, changes can be detected using
onReceive: