Hey, thanks for the reply. I think there is actually something more to this. I double checked and I have .binding() on both reducers as well as the actions conforming to BindableAction. I've tried to distill this down to the simplest version possible, and I'm still getting the issue with this as the edit screen reducer:
public struct EditState: Equatable, Hashable {
@BindableState var name: String = ""
}
public enum EditAction: BindableAction {
case binding(BindingAction<EditState>)
}
public struct EditEnvironment {
public init() {}
}
let editReducer = AnyReducer<EditState, EditAction, EditEnvironment>.combine(
.init { state, action, environment in
switch action {
case .binding:
return .none
}
}
)
.binding()
Which is loaded from the main screen:
public enum TileRoute: Hashable {
case edit(EditState)
}
public struct TilesState: Equatable, Hashable {
var navigation: [TileRoute] = []
var editState: EditState? {
get { navigation.find(/TileRoute.edit) }
set { newValue.map { navigation.update(/TileRoute.edit, with: $0) } }
}
public init() {}
}
public enum TilesAction: BindableAction {
case navigationPathChanged([TileRoute])
case editScreen(EditAction)
case addLocation
case binding(BindingAction<TilesState>)
}
public struct TilesEnvironment {
public init() {}
}
public let tilesReducer = AnyReducer<TilesState, TilesAction, TilesEnvironment>.combine(
.init { state, action, environment in
switch action {
case .navigationPathChanged(let navigation):
state.navigation = navigation
return .none
case .editScreen:
return .none
case .addLocation:
let route = TileRoute.edit(EditState())
state.navigation.append(route)
return .none
case .binding:
return .none
}
}
).binding()
I'm also using the this extension for navigation with iOS 16 NavigationStack:
extension Array where Element == TileRoute {
func find<Value>(_ casePath: CasePath<Element, Value>) -> Value? {
compactMap { element in
guard let value = casePath.extract(from: element) else { return nil }
return value
}.first
}
mutating func update<Value>(_ casePath: CasePath<Element, Value>, with value: Value)
where Value: Equatable {
guard
let routeIndex = firstIndex(where: { element in
guard casePath.extract(from: element) != .none else { return false }
return true
})
else {
return
}
self[routeIndex] = casePath.embed(value)
}
}
Where there is a ToolbarItemGroup with a Button that triggers the .addLocation action and .navigationDestination defined as:
.navigationDestination(for: TileRoute.self, destination: { route in
switch route {
case .edit:
IfLetStore(store.scope(state: \.editState, action: TilesAction.editScreen)) { store in
EditScreen(store: store)
}
}
})