0

I don't feel that the following example is exhaustive (from Hacking With Swift). A lot of online examples tend to keep it quite elementary like this, where NavigationLinks are implemented based off of simple Hashable models.

struct ContentView: View {
    var body: some View {
        NavigationStack {
            List(1..<50) { i in
                NavigationLink(value: i) {
                    Label("Row \(i)", systemImage: "\(i).circle")
                }
            }
            .navigationDestination(for: Int.self) { i in
                Text("Detail \(i)")
            }
            .navigationTitle("Navigation")
        }
    }
}

I have views which take arguments for ObservedObjects. For example ViewA(firstViewModel: self.firstViewModel) and ViewB(secondViewModel: self.secondViewModel. My ObservedObjects are instantiated in my root view where NavigationStack is defined. How can I use the new NavigationStack and NavigationLink paradigm in iOS 16 to support navigating to my custom SwiftUI views? This is something that was easily done in iOS 15 by just doing

NavigationLink(destination: someView(viewModel: self.viewModel) { Text("Test Link") } 

Now it's all programmatic. Can I wrap some properties in an enum which point to my SwiftUI Views? If so, can I still keep references to the correct runtime objects I want to pass in to the views? A little lost on implementing this navigation hierarchy beyond the elementary examples.

Please see my minimally producible example below to exercise the exact issue I am facing:

struct ContentView: View {

    var body: some View {
        NavigationStack {
            ProfileView()

            // there is other logic for other root views, but
            // this problem can just focus on ProfileView in NavigationStack
        }
    }

}

struct ProfileView: View {

    @StateObject var progressViewModel = ProgressViewModel()

    @StateObject var challengesViewModel = ChallengesViewModel()

    @StateObject var subscriptionViewModel = SubscriptionViewModel() 

    var body: some View {
        VStack {

            // Below is the code that is broken, and I'm not sure how to adapt it 
            // to the new iOS 16 navigation patterns since I am not reusing the same view
            // as in the above example
            NavigationLink(destination: ProgressView(progressViewModel: self.progressViewModel)) {
                Text("User progress nav link")
            }
            
            NavigationLink(destination: ChallengesView(challengesViewModel: self.challengesViewModel)) {
                Text("Challenges nav link")
            }

            NavigationLink(destination: SubscriptionView(subscriptionViewModel: self.subscriptionViewModel)) {
                Text("Subscription nav link")
            }
        }
    }

}

struct ProgressView: View {
    @ObservedObject var progressViewModel: ProgressViewModel

    var body: some View {
        VStack {
            Text(progressViewModel.overallUserProgressString)
        }
    }
}

struct ChallengesView: View {
    @ObservedObject var challengesViewModel: ChallengesViewModel

    var body: some View {
        VStack {
            Text(challengesViewModel.numberOfChallengesPerformedString)
        }
    }
}

struct SubscriptionView: View {
    @ObservedObject var subscriptionViewModel: SubscriptionViewModel

    var body: some View {
        VStack {
            Text(subscriptionViewModel.localSubscriptionPriceString)
        }
    }
}
Andre
  • 562
  • 2
  • 7
  • 18
  • https://stackoverflow.com/questions/61304700/swiftui-how-to-avoid-navigation-hardcoded-into-the-view?rq=1 The most recent answer on this post seems relevant to the question here – Andre Sep 13 '22 at 13:42
  • I recommend just learning pure SwiftUI, there is a ton of features to learn, e.g. value types (vs reference types), dependency tracking, identity, property wrappers, preferences, environment, etc. I recommend the WWDC videos and Apple samples. Sadly there is a lot of MVVM junk on blogs and Youtube, written before they learned SwiftUI properly, thats mostly Apple's fault for not explaining how it works well enough. Check out AzamSharp he used to be MVVM and has done a complete U-turn. https://azamsharp.com/2022/07/17/2022-swiftui-and-mvvm.html – malhal Sep 13 '22 at 20:14

1 Answers1

0

iOS 16 & Xcode 14

Here is a minimum answer which satisfies the design pattern for basic programmatic Navigation stack management with custom views.

enum CustomNavTypes: String, Hashable {
    case viewA = "View A"
    case viewB = "View B"
    case viewC = "View C"
}

struct CustomView: View {
    @State var navigationViewStack: [CustomNavTypes] = []
    var body: some View {
        NavigationStack {
            VStack {
                Button(action: {
                    navigationViewStack.append(.viewA)
                }) {
                    Text("View A")
                }
                Button(action: {
                    navigationViewStack.append(.viewB)
                }) {
                    Text("View B")
                }
                Button(action: {
                    navigationViewStack.append(.viewC)
                }) {
                    Text("View C")
                }
            }
            .navigationDestination(for: ProfileNavTypes.self) { value in
                switch value {
                    case .viewA: ViewA() // can still pass in view models objects if needed
                    case .viewB: ViewB()
                    case .viewC: ViewC()
                }
            }
        }
    }
}

I appreciate @malhal for showing the strength of the pure MV approach, but realistically I don't expect all developers or apps to be leveraging a codebase that is purely MV (where the SwiftUI view specifically is treated as the intended ViewModel, which it is...). While MV improves runtime performance and eliminates a lot of boilerplate code, I still imagine a lot of people (including myself) using viewmodel objects out of convenience or to manage generic paradigms, even if we are migrating to a more strict MV setup.

I hope this helps, I am certainly learning. I'm sure this isn't perfect but it's a start.

Andre
  • 562
  • 2
  • 7
  • 18