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

Full screen cover is not presented correctly #2413

Closed omaralbeik closed 1 year ago

omaralbeik commented 1 year ago

Description

Presenting a full screen cover after a sheet is presented and dismissed is not showing the full screen cover in full screen.

Demo video

demo

Demo project

FullScreenBug.zip

Code

RootFeature

struct RootFeature: Reducer {
  struct Destination: Reducer {
    enum State: Equatable {
      case sheet(SheetFeature.State)
      case fullScreenCover(FullScreenCoverFeature.State)
    }

    enum Action {
      case sheet(SheetFeature.Action)
      case fullScreenCover(FullScreenCoverFeature.Action)
    }

    var body: some ReducerOf<Self> {
      Scope(state: /State.sheet, action: /Action.sheet, child: SheetFeature.init)
      Scope(state: /State.fullScreenCover, action: /Action.fullScreenCover, child: FullScreenCoverFeature.init)
    }
  }

  struct State: Equatable {
    @PresentationState var destination: Destination.State?
  }

  enum Action {
    case presentSheet
    case destination(PresentationAction<Destination.Action>)
  }

  var body: some ReducerOf<Self> {
    Reduce { state, action in
      switch action {
      case .presentSheet:
        state.destination = .sheet(SheetFeature.State())
        return .none

      case .destination(.presented(.sheet(.dismissButtonTapped))):
        state.destination = .fullScreenCover(FullScreenCoverFeature.State())
        return .none

      case .destination:
        return .none
      }
    }
    .ifLet(\.$destination, action: /Action.destination, destination: Destination.init)
  }
}
struct RootView: View {
  let store: StoreOf<RootFeature>

  var body: some View {
    VStack(alignment: .center) {
      Text("Root")
      Button("Show sheet") {
        store.send(.presentSheet)
      }
    }
    .sheet(
      store: store.scope(state: \.$destination, action: RootFeature.Action.destination),
      state: /RootFeature.Destination.State.sheet,
      action: RootFeature.Destination.Action.sheet,
      content: SheetView.init
    )
    .fullScreenCover(
      store: store.scope(state: \.$destination, action: RootFeature.Action.destination),
      state: /RootFeature.Destination.State.fullScreenCover,
      action: RootFeature.Destination.Action.fullScreenCover,
      content: FullScreenCoverView.init
    )
  }
}

SheetFeature

struct SheetFeature: Reducer {
  @Dependency(\.dismiss) private var dismiss

  struct State: Equatable {}

  enum Action {
    case dismissButtonTapped
  }

  var body: some ReducerOf<Self> {
    Reduce { state, action in
      switch action {
      case .dismissButtonTapped:
        return .run { _ in await dismiss() }
      }
    }
  }
}

FullScreenCoverFeature

struct FullScreenCoverFeature: Reducer {
  @Dependency(\.dismiss) private var dismiss

  struct State: Equatable {}

  enum Action {
    case dismissButtonTapped
  }

  var body: some ReducerOf<Self> {
    Reduce { state, action in
      switch action {
      case .dismissButtonTapped:
        return .run { _ in await dismiss() }
      }
    }
  }
}

Checklist

Expected behavior

I would expect the full screen cover to be always presented as a full screen cover.

Actual behavior

The full screen cover is presented as a regular sheet.

Steps to reproduce

Present a

The Composable Architecture version information

1.2.0

Destination operating system

iOS 16

Xcode version information

Xcode 14.3.1

Swift Compiler version information

No response

stephencelis commented 1 year ago

@omaralbeik If I run the project on iOS 17 / Xcode 15 beta 7 it works as expected, which makes me believe this is a SwiftUI bug that is reproducible in vanilla SwiftUI on iOS 16 / Xcode 14. These kinds of bugs involving multiple SwiftUI presentations swapping at the same time are generally SwiftUI bugs/limitations and require more general workarounds depending on your needs. Because of this I'm going to covert this to a discussion, but if you can demonstrate that there's a bug in our library that does not affect a similar vanilla SwiftUI project we'll definitely do our best to fix it!