Sync AppState with children on lower levels #1121
-
Hey! I’m struggling with some architectural issues, regarding a single source of truth with clients (e.g. Multipeer Connectivity, CoreData). I have the following app structure:
The main idea was, that my AppState is the single source of truth and I observe the changes here (like peer found or core data entity changed) and forward them to the child states. Sadly it's not as easy and straightforward as I thought. Probably the correct way would be to use computed properties for the substates - then the child automatically gets informed about the change - but there are some children between, where I don't really need the data explicitly and it becomes a lot of overhead... and for example Currently, I'm doing that by triggering child actions from the parent. It's working but I have to manually update the specific child state case let .peerFound(peer):
return Effect(value: Action.main(.tabB(.featureA(.peerFound(peer))))) So I found that comment from Brandon https://forums.swift.org/t/shared-global-state-with-infinite-derived-children/57263/3 and I'm thinking about rewriting the clients and observing the data only on the specific screens. But I'm scared because in that case, the client would be the only single source of truth, and what if I observe the same CoreData Entities on 2 different screens (the other view is also in the navigation stack), maybe this could lead to an inconsistent state. I would be interested, has anyone had the same problem, if yes, what was the solution? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
how do you handle coreData? |
Beta Was this translation helpful? Give feedback.
-
If I quote @mbrandonw correctly you should handle global state inside the environment. struct PublisherViewEnvironment {
let publisherEffect: () -> Effect<Int, GeneralError> = {
return Effect(SomeService().publisher)
}
}
class SomeService {
var publisher: AnyPublisher<Int, GeneralError> {
return subject.eraseToAnyPublisher()
}
private var subject = CurrentValueSubject<Int,GeneralError>(0)
private var timer: Timer?
init() {
self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
let newValue = self.subject.value + 1
self.subject.send(newValue)
}
}
} This is an example on how you could observer the changes inside your reducer let reducer = Reducer<PublisherViewState, PublisherViewAction, PublisherViewEnvironment> { state, action, environment in
enum CancelId {}
switch action {
case .onAppear:
return environment.publisherEffect().catchToEffect { newValue in
// update the state to reflect the changes in the Environment
}
}
} |
Beta Was this translation helpful? Give feedback.
If I quote @mbrandonw correctly you should handle global state inside the environment.
You could do this by creating a Service that provides a publisher. This publisher then can be erased to an Effect, and observed inside the reducer to update the state accordingly.