pointfreeco / swift-composable-architecture

A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.
https://www.pointfree.co/collections/composable-architecture
MIT License
12.53k stars 1.45k forks source link

Alert state remaining when dismissing alert #2461

Closed thompsondave closed 1 year ago

thompsondave commented 1 year ago

Description

When using @PresentationState to display an alert with aButtonState(role: .cancel) if you tap on the cancel button to dismiss the alert it is dismissed, but then try to navigate somewhere else e.g tap a button to show a fullScreenCover, the alert is presented again and the navigation to fullScreenCover fails. The reduce is then no longer able to display the fullScreenCover.

The workaround for this is to always explicitly handle .alert(.dismiss) and set state.alert = nil

Checklist

Expected behavior

After dismissing the alert with ButtonState(role: .cancel) we should be able to navigate to a fullScreenCover without the alert state being shown again.

Actual behavior

After dismissing the alert with ButtonState(role: .cancel), attempting to navigate to a fullScreenCover shows the alert state again.

https://github.com/pointfreeco/swift-composable-architecture/assets/39087142/24c0d2a6-fd15-4c5b-a050-4b98b69821e9

Steps to reproduce

Attached minimal project: TestComposableArchitecture.zip

The Composable Architecture version information

ea631ce892687f5432a833312292b80db238186a

Destination operating system

iOS 16

Xcode version information

XCode 14.3.1

Swift Compiler version information

swift-driver version: 1.75.2 Apple Swift version 5.8.1 (swiftlang-5.8.0.124.5 clang-1403.0.22.11.100)
Target: arm64-apple-macosx13.0
mbrandonw commented 1 year ago

Hi @thompsondave, you are missing one piece from using the presentation tools: you must use ifLet to integrate the child feature behavior with the parent. This goes even for alerts, which don't really have much logic or behavior other than the fact that when you tap any button in an alert the state should be immediately cleared out.

So you just need this:

Reduce { state, action in 
}
.ifLet(\.$alert, action: /Action.alert)

Since this isn't an issue with the library I am going to convert it to a discussion. Feel free to continue the conversation there.