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.35k stars 1.44k forks source link

Issues with modifying or replacing a presented alert depending on iOS version #973

Closed KaiOelfke closed 1 year ago

KaiOelfke commented 2 years ago

Describe the bug The alert modifier alert<Action>( _ store: Store<AlertState<Action>?, Action>, dismiss: Action ) -> some View behaves differently on iOS 15 compared to older iOS versions. This is because the OldAlertModifier and NewAlertModifier use different SwiftUI alert APIs. And they don't behave in the same way. For iOS 15 a presented alert doesn't get updated, when the AlertState changes. On older iOS versions the alert is updated. This can be reproduced in a vanilla SwiftUI app by using the old and new alert APIs and changing e.g. the alert message. The new API ignores changes, which seems like a SwiftUI bug to me.

To Reproduce

swift-composable-architecture.zip

AlertTest.zip

For example changing the AlertAndConfirmationDialog case study like below to replace the alert at a later point works on iOS 14, but not on iOS 15.

switch action {
  case .alertButtonTapped:
    state.alert = .init(
      title: .init("Alert!"),
      message: .init("This is an alert"),
      primaryButton: .cancel(.init("Cancel")),
      secondaryButton: .default(.init("Increment"), action: .send(.incrementButtonTapped))
    )
    return Effect(value: AlertAndConfirmationDialogAction.delayedAlertChange)
      .delay(for: .seconds(2), scheduler: DispatchQueue.main)
      .eraseToEffect()

  case .delayedAlertChange:
    state.alert = .init(
      title: .init("New Alert!"),
      message: .init("This is a new alert"),
      primaryButton: .cancel(.init("Cancel")),
      secondaryButton: .default(.init("Increment"), action: .send(.incrementButtonTapped))
    )
    return .none

Expected behavior

I don't know a workaround for the new alert APIs. But the behavior should be consistent independent of the iOS version. So either it shouldn't be supported to update or replace a presented error or it should work for all versions. For older iOS versions it's also possible to modify an alert. This isn't possible currently as AlertState removes duplicates with an UUID, but the UUID isn't exposed in the AlertState initializers. So this would also need a change in order to support updating an alert.

Screenshots If applicable, add screenshots to help explain your problem.

Environment

Additional context Add any more context about the problem here.

stephencelis commented 1 year ago

@KaiOelfke Thanks for taking the time to submit this, and sorry we never got around to a fix for these specific APIs. It seems that we need dedicated APIs for each underlying SwiftUI API to avoid introducing weird behavior depending on the platform OS version, and that's not so easy to accommodate in a backwards-compatible manner.

However, these alert APIs are mostly deprecated in favor of the new TCA presentation APIs, released a few months ago. As such, we're in a better position now to create a presentation-specific API that is available to iOS 13 and uses the same underlying API regardless of iOS version.

Because of this I'm going to close this issue out as we don't intend to fix the API, and will deprecate it soon, but let us know if you have any other thoughts.