'BUG navigating from a view to another in SwiftUI

I have a problem and I really don't know how can i fix this. When I'm pressing the button, I want to navigate to the next view, but at the last view, it didn't work and it pushes me out to the MainView. So, in the CartView, if the onboardingState == 1, it will create an Order and the status will be changed to "pending", and will show the WaitingOrderView(), but is not working as expected.

I will put a view under the code that I will share right know.

struct CartView: View {
        @EnvironmentObject var syncViewModel : SyncViewModel
        @State var onboardingState: Int = 0
        @State var index = 0
        @State private var timer: AnyCancellable?

        var body: some View {
                ZStack {
                    switch onboardingState {
                    case 0 :
                        VStack {
                        welcomeSection
                        Spacer()
                        bottomButton
                            .padding(30)
                        }
                    case 1 :
                        VStack {
                        welcomeSection2
                        Spacer()
                        bottomButton
                            .padding(30)
                        }

                    case 2 :
                        VStack {
                        detailOrder
                        Spacer()
                        bottomButton
                            .padding(30)
                        }
                    default:
                        EmptyView()
                    }
                }
        }
    }
    extension CartView {
        private var bottomButton : some View {
            Text("Continue")
                .font(.headline)
                .foregroundColor(.white)
                .frame(height: 55)
                .frame(maxWidth: .infinity)
                .background(Color.onboardingColor)
                .cornerRadius(20)
                .onTapGesture {
                    handleNextButtonPressed()
                }
        }

        private var welcomeSection: some View {

            PaymentView()
        }

        private var welcomeSection2: some View {

            LivrareView()
        }
        @ViewBuilder
        private var detailOrder: some View {
            ZStack {
                if syncViewModel._order.status == syncViewModel.statusList.first(where: { status in
                    status.key == StatusKey.pending.rawValue
                })?.id
                {
                    WaitingOrderView()
                }
                else
                {
                    EmptyView()
                }
            }
        }
    }

    extension CartView {
        func handleNextButtonPressed() {
            if onboardingState == 1 {
                syncViewModel.createOrder()
                onboardingState += 1
            }
            else {
                withAnimation(.spring()) {
                    onboardingState += 1
                }
            }


        }
    }

struct MainView: View {
    @EnvironmentObject var syncViewModel : SyncViewModel
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(sortDescriptors: [])
    private var menus : FetchedResults<LocalMenu>

    var body: some View {
        TabView{
            NavigationView{

             MeniuriView()
                    .toolbar {
                        ToolbarItem(placement: .navigationBarTrailing) {
                            ToolbarButtons(numberOfProducts: menus.count)
                    }
                        ToolbarItem(placement: .navigationBarLeading) {
                            Text(Texts.mainViewText1)
                                .font(.system(size: 24))
                                .fontWeight(.bold)
                                .padding()
                        }
                }
            }
            .tabItem {
                Text(Texts.mainViewText2)
                Image(systemName: "fork.knife")
            }
            NavigationView {
                AlteleView()
            }
            .tabItem {
                Text(Texts.mainViewText3)
                Image("altele", bundle: Bundle.main)
            }
        }
        .accentColor(Color.tabItemColor)
    }
}
struct ToolbarButtons: View {
    @EnvironmentObject var syncViewModel : SyncViewModel
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(sortDescriptors: [])
    private var cartOrder : FetchedResults<CartOrders>
    var numberOfProducts : Int

    var body: some View {
        ZStack {
            Spacer()
            if syncViewModel._order.status == 0 && syncViewModel._order.id == 0{
                NavigationLink(destination: CartView()
                                .toolbar {
                    ToolbarItem(placement: .navigationBarLeading) {
                        Text(Texts.cartText)
                            .font(.system(size: 24))
                            .bold()
                    }
                }
                               , label: {
                    Image("otherIcon")
                        .padding(.top, 5)
                }
                )
            }
            else  if syncViewModel._order.id == cartOrder.first?.id  ?? Int32(syncViewModel._order.id) {
                NavigationLink  {
                    WaitingOrderView()
                } label: {
                    ZStack(alignment: .topTrailing) {
                        Image("cartIcon")
                            .padding(.top, 5)
                        if numberOfProducts > 0 {
                            Text("\(numberOfProducts)")
                                .font(.caption2).bold()
                                .foregroundColor(.white)
                                .frame(width: 15, height: 15)
                                .background(Color.tabItemColor)
                                .cornerRadius(50)
                                .offset(x: 10, y: -8)
                        }
                    }
                }
            }
        }
    }
}



struct WaitingOrderView {
    @EnvironmentObject var syncViewModel : SyncViewModel
    let transition: AnyTransition = .asymmetric(
        insertion: .move(edge: .trailing),
        removal: .move(edge: .leading))
    var body: some View {
        ZStack {
            if syncViewModel._order.status == syncViewModel.statusList.first(where: { status in
                status.key == StatusKey.pending.rawValue
            })?.id {
                ChefView()
                    .environmentObject(syncViewModel)
            }
           else  if syncViewModel._order.status == syncViewModel.statusList.first(where: { status in
                status.key == StatusKey.accepted.rawValue
            })?.id
            {
                OrderConfirmedView()
            }
            else if syncViewModel._order.status == syncViewModel.statusList.first(where: { status in
                status.key == StatusKey.readyForDelivery.rawValue
            })?.id {
                OrderReadyForDeliveryView()
            }
            else if syncViewModel._order.status == syncViewModel.statusList.first(where: { status in
                status.key == StatusKey.onTheWay.rawValue
            })?.id {
                OrderOnTheWayView()
            } else if syncViewModel._order.status == syncViewModel.statusList.first(where: { status in
                status.key == StatusKey.completed.rawValue
            })?.id {
                OrderCompletedView()
            } else if syncViewModel._order.status == syncViewModel.statusList.first(where: { status in
                status.key == StatusKey.canceled.rawValue
            })?.id {
                OrderCanceledView()
            }
        }
      
     
    
    }

enter image description here



Solution 1:[1]

These might not be the reason for the exact bug but your use of SwiftUI is non-standard and will likely cause you problems. In browsing your code here is a list of issues I noticed:

  1. We don't use view model objects in SwiftUI, we use @State and we can use a custom struct to make some logic more testable and the whole reason is to take advantage of Swift's value semantics - see Managing User Interface State. If you use objects instead of value types you will have major problems. Also worth watching Data Essentials in SwiftUI WWDC 2020 the first half is about view state and shows the use of custom structs, e.g. struct EditorConfig.
  2. You are using computed vars e.g. private var detailOrder: some View instead of custom Views, e.g. DetailOrderView. This breaks SwiftUI's dependency tracking. You are supposed to break up your hierarchy into small views with only a few lets or states so body will be recomputed less often, which is called having tighter invalidation. A DetailOrderView could have let orderStatus so although it might be init many times, its body is only computed when orderStatus is different. In your current code your entire massive body is recomputed when orderStatus changes.
  3. var numberOfProducts : Int should be let.
  4. We try not to use if inside body which compiles to a _ConditionalView and breaks SwiftUI's structural identity. See Demystify SwiftUI WWDC 2021. You can avoid this with something like Text(orderStatus.complete ? "Complete" : "In-progress") so there is always a Text just with different params. We can also use modifiers conditionally by using the usual .automatic param in the else case.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1