I’m trying to implement a Coordinator for managing a flow. The state is stored inside the CoordinatorStore. There are 2 @Published properties for managing the flow. The screen property controls which View is currently shown and path controls the navigation stack of the stack view. Details of the implementation can be found below.
With the current implementation and after the following actions: showA -> showB -> showInitial
I would expect that StoreA and StoreB would be deallocated from memory since path, which holds StoreA and StoreB via enum associated values, gets emptied.
But that doesn’t happen, and if I repeat the actions again there would be 2 StoreA and 2 StoreB in memory and so on. Am I missing something?
I will also attach a screenshot of the memory debugger snapshot after doing the initial set of actions.
enum Path: Hashable {
case a(StoreA)
case b(StoreB)
}
enum Screen {
case initial
case stack
}
final class CoordinatorStore: ObservableObject {
@Published var path: [Path] = []
@Published var screen: Screen = .stack
func showA() {
let store = StoreA()
path.append(.a(store))
}
func showB() {
let store = StoreB()
path.append(.b(store))
}
func showInitial() {
path = []
screen = .initial
}
func showStack() {
screen = .stack
}
}
struct Coordinator: View {
@ObservedObject var store: CoordinatorStore
var body: some View {
switch store.screen {
case .initial: initial
case .stack: stack
}
}
var stack: some View {
NavigationStack(path: $store.path) {
VStack {
Text("Root")
}
.toolbar {
Button(action: self.store.showA) {
Text("Push A")
}
}
.navigationDestination(for: Path.self) { path in
switch path {
case .a(let store):
ViewA(store: store)
.toolbar {
Button(action: self.store.showB) {
Text("Push B")
}
}
case .b(let store):
ViewB(store: store)
.toolbar {
Button(action: self.store.showInitial) {
Text("Show Initial")
}
}
}
}
}
}
var initial: some View {
VStack {
Text("Initial")
Button(action: store.showStack) {
Text("Go to Stack")
}
}
}
}
struct ViewA: View {
@ObservedObject var store: StoreA
var body: some View {
Text("View A")
}
}
final class StoreA: NSObject, ObservableObject {
deinit {
print("Deinit: \(String(describing: self))")
}
}
struct ViewB: View {
@ObservedObject var store: StoreB
var body: some View {
Text("View B")
}
}
final class StoreB: NSObject, ObservableObject {
deinit {
print("Deinit: \(String(describing: self))")
}
}