-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Description
if the child state is Identifiable
, the dismiss action is not be sent when dismissed.
I think #3309 introduced this issue.
Checklist
- I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
- If possible, I've reproduced the issue using the
main
branch of this package. - This issue hasn't been addressed in an existing GitHub issue or discussion.
Expected behavior
Send dismiss action when dismissed whatever the child state is Identifiable
.
Actual behavior
swift-composable-architecture/Sources/ComposableArchitecture/Observation/Store+Observation.swift
Lines 395 to 398 in fc5cbee
set { | |
if newValue == nil, | |
let childState = self.state[keyPath: state], | |
id == _identifiableID(childState), |
The
id
that determines whether or not to send the dismiss action is set by the following.swift-composable-architecture/Sources/ComposableArchitecture/Observation/Store+Observation.swift
Lines 338 to 348 in fc5cbee
public func scope<State: ObservableState, Action, ChildState, ChildAction>( | |
state: KeyPath<State, ChildState?>, | |
action: CaseKeyPath<Action, PresentationAction<ChildAction>>, | |
fileID: StaticString = #fileID, | |
filePath: StaticString = #filePath, | |
line: UInt = #line, | |
column: UInt = #column | |
) -> UIBinding<Store<ChildState, ChildAction>?> | |
where Value == Store<State, Action> { | |
self[ | |
id: wrappedValue.currentState[keyPath: state].flatMap(_identifiableID), |
However, when scoping for
present
, if the child state is nil
which is the most of the time, the id will also be nil
, but _identifiableID(childState)
will not be. So the dismiss action will not be sent.
This issue doesn't affect SwiftUI implementations because the scoped Binding
is created every time when body
is called, including the time the child state becomes non-nil.
I can work around this by mimicking SwiftUI's behavior, but it's so weird.
observe { [weak self] in
guard let self, store.alert != nil else { return }
var token: ObserveToken!
token = present(
item: $store.scope(state: \.alert, action: \.alert),
onDismiss: { token.cancel() }
) { store in
return UIAlertController(store: store)
}
}
Reproducing project
TicTacToe
Lines 94 to 96 in fc5cbee
present(item: $store.scope(state: \.alert, action: \.alert)) { store in | |
UIAlertController(store: store) | |
} |
The
alert
is an AlertState
which is Identifiable
.
The Composable Architecture version information
1.14.0