Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dismiss and present on presenting UIViewController #3570

Closed
2 of 3 tasks
v-slobodianiuk opened this issue Jan 29, 2025 · 3 comments
Closed
2 of 3 tasks

Dismiss and present on presenting UIViewController #3570

v-slobodianiuk opened this issue Jan 29, 2025 · 3 comments
Labels
bug Something isn't working due to a bug in the library.

Comments

@v-slobodianiuk
Copy link

v-slobodianiuk commented Jan 29, 2025

Description

Hello!
I get incorrect behavior when leaving the screen and opening a new one in a modal window. Sometimes everything works correctly, but other times all my windows just close. I wrote a Unit test and a UI test. The UI test sometimes fails.

Here is a link to the repository with an Example project

Steps:

  1. Present AViewController on RootViewController
  2. Present BViewController on AViewController
  3. Dismiss BViewController and present CViewController on AViewController

Image

Error:
Image

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

Present CViewController

Actual behavior

Dissmis AViewController

Reproducing project

import ComposableArchitecture
import UIKit

@Reducer
struct AFeature {
    @ObservableState
    struct State: Equatable {
        @Presents var destination: Destination.State?
    }

    @Reducer
    enum Destination {
        case bFeature(BFeature)
        case cFeature(CFeature)
    }

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

    var body: some Reducer<State, Action> {
        Reduce { state, action in
            switch action {
            case .showB:
                state.destination = .bFeature(.init())
                return .none

            case .showC:
                state.destination = .cFeature(.init())
                return .none
                
            case .destination(.presented(.bFeature(.delegate(.closeAndShowC)))):
                return .send(.showC)

            default:
                return .none
            }
        }
        .ifLet(\.$destination, action: \.destination)
    }
        
}

extension AFeature.Destination.State: Equatable {}

final class AViewController: UIViewController {
    lazy var button = UIButton(type: .system, primaryAction: .init(handler: { [weak self] _ in
        self?.store.send(.showB)
    }))

    @UIBindable private var store: StoreOf<AFeature>
    
    init(store: StoreOf<AFeature>) {
        self.store = store
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
        logger.info("✅ Deinit: \(String(describing: Self.self))")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemBackground
        button.configureTintedButton(withTitle: "Show B")
        button.center = view.center
        view.addSubview(button)
        
        present(item: $store.scope(state: \.destination?.bFeature, action: \.destination.bFeature)) { store in
            BViewController(store: store)
        }
        
        present(item: $store.scope(state: \.destination?.cFeature, action: \.destination.cFeature)) { store in
            CViewController(store: store)
        }
    }
}

Image

Example project

The Composable Architecture version information

1.17.1

Destination operating system

iOS 18

Xcode version information

Xcode 16.2

Swift Compiler version information

swift-driver version: 1.115.1 Apple Swift version 6.0.3 (swiftlang-6.0.3.1.10 clang-1600.0.30.1)
Target: arm64-apple-macosx15.0
@v-slobodianiuk v-slobodianiuk added the bug Something isn't working due to a bug in the library. label Jan 29, 2025
@BastianKusserow
Copy link

Hey @v-slobodianiuk, we currently observe the same issue and I wasn't able to find a fix. Did you by any chance a way around that behaviour?

@v-slobodianiuk
Copy link
Author

v-slobodianiuk commented Apr 8, 2025

Hey @v-slobodianiuk, we currently observe the same issue and I wasn't able to find a fix. Did you by any chance a way around that behaviour?

Set current destination to nil. Add delay about 0.5 s to set the new destination.

@stephencelis
Copy link
Member

@v-slobodianiuk I believe this is vanilla behavior and reproducible in UIKit if you were to simultaneously dismiss and present a view controller without waiting for it to complete.

One workaround is to introduce a delay, as you've done, and another might be to try to group all sheet presentation into a single present method that switches over the destinations instead of using two separate presents that overlap.

If you do believe this is a bug with our present helper, please reopen the issue on our Swift Navigation library, instead. For now I'm going to convert this to a discussion till it is determined to be a bug with us and not Apple.

@pointfreeco pointfreeco locked and limited conversation to collaborators Apr 9, 2025
@stephencelis stephencelis converted this issue into discussion #3650 Apr 9, 2025

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
bug Something isn't working due to a bug in the library.
Projects
None yet
Development

No branches or pull requests

3 participants