Jorn van Dijk
Jorn van Dijk

Reputation: 1993

TabView breaks navigation on Xcode 16 using TCA

I'm seeing inconsistent behavior when using NavigationStack inside a TabView. On my device (iOS 17.6.1) with Xcode 16.0, the "drilling down" stops working after navigating back to the root and tapping the button again. It works fine on the Simulator, and also on my device when building using Xcode 15.

If I remove the TabView, everything functions as expected.

Any ideas if this is a bug with Xcode, the compiler, SwiftUI, or TCA? Or am I missing something here?

import ComposableArchitecture
import SwiftUI

@main
struct DemoApp: App {
    var body: some Scene {
        WindowGroup {
            TabView {
                NavigationStack {
                    FeatureView(store: .init(initialState: .init(), reducer: {
                        Feature()
                    }))
                }
            }
        }
    }
}

@Reducer
struct Feature: Equatable {
    @Reducer(state: .equatable)
    public enum Destination  {
        case drillDown(Feature)
    }
    
    @ObservableState
    struct State: Equatable {
        @Presents var destination: Destination.State?
    }
    
    enum Action {
        case destination(PresentationAction<Destination.Action>)
        case drillDownButtonTapped
    }
    
    public var body: some Reducer<State, Action> {
        Reduce { state, action in
            switch action {
            case .destination:
                return .none
            case .drillDownButtonTapped:
                state.destination = .drillDown(Feature.State())
                return .none
            }
        }
        .ifLet(\.$destination, action: \.destination)
    }
}

struct FeatureView: View {
    @Bindable var store: StoreOf<Feature>
    var body: some View {
        Button("Drill Down") {
            store.send(.drillDownButtonTapped)
        }
        .navigationDestination(
            item: $store.scope(state: \.destination?.drillDown, action: \.destination.drillDown)
        ) { store in
            FeatureView(store: store)
        }
    }
}

Upvotes: 2

Views: 324

Answers (1)

Rush B
Rush B

Reputation: 29

I've noticed this issue as well, it only happens on XCode 16.

I can confirm the issue is also related to having a Button inside a TabView. I noticed if you long press on the button it will register the tap.

A workaround is to add a long press gesture with a minimum duration of 0 to your Button, in your body you can do this:

Note: make sure to remove the button action closure otherwise it may lead to your action being registered twice.

   Button("Drill Down") {
        // touch up
        // no longer needed
    }
   .onLongPressGesture(minimumDuration: 0, perform: {}) { pressing in
        if pressing {
            // touch down
            store.send(.drillDownButtonTapped)
        }
    }
    .navigationDestination(
        item: $store.scope(state: \.destination?.drillDown, action: \.destination.drillDown)
    ) { store in
        FeatureView(store: store)
    }

Upvotes: 0

Related Questions