'In SwiftUI, how do you filter a list on a subview when passing data through a NavigationLink?

I am new to SwiftUI and programming in general. I am trying to pass data and create navigation between different views in my app.

For my data model, I am using MVVM format even though my data is entirely static right now. I have two data models that I am trying to connect via enumeration: CategoryModel and SubCategoryModel (see below).

CategoryModel:

import Foundation
import SwiftUI

enum Category: String, CaseIterable, Identifiable {
    var id: String { self.rawValue }
    
    case explicit = "Explicit"
    case adventure = "Adventure"
    case artists = "Artists"
    case holidays = "Holidays"
    case movies = "Movies"
    case people = "People"
    case tasks = "Tasks"
    case feelings = "Feelings"
    case lifestyle = "Lifesytle"
    case party = "Party"
    case sports = "Sports"
}

//Root data -> set up data structure
//make Identifiable for ForEach looping in other views
struct CategoryModel: Identifiable {
    
    let id = UUID().uuidString
    let category: Category
    let categoryTitle: String
    let description: String
    let isPurchase: Bool
    let categoryImage: Image
}

class CategoryViewModel: ObservableObject {

    @Published var gameCategories: [CategoryModel] = CategoryModel.all
    @Published var filteredPurchase: [CategoryModel] = []
    
    init() {
        filterByPurchase()
    }
    
    func filterByPurchase() {
        filteredPurchase = gameCategories.filter({ $0.isPurchase })
    }
}

extension CategoryModel {
    static let all = [
        CategoryModel(
            category: .explicit,
            categoryTitle: "Adults Only",
            description: "For those spicy, intimate moments.",
            isPurchase: true,
            categoryImage: Image(uiImage: #imageLiteral(resourceName: "Explicit"))
        ),
        CategoryModel(
            category: .adventure,
            categoryTitle: "Call of the Wild",
            description: "[Insert description here]",
            isPurchase: false,
            categoryImage: Image(uiImage: #imageLiteral(resourceName: "Adventure"))
        ),
        CategoryModel(
            category: .artists,
            categoryTitle: "By the Artist",
            description: "[Insert description here]",
            isPurchase: false,
            categoryImage: Image(uiImage: #imageLiteral(resourceName: "Artists"))
        ),
    ]
}

SubCategoryModel:

import Foundation
import SwiftUI

//Root data -> set up data structure
//make Identifiable for ForEach looping in other views
struct SubCategoryModel: Identifiable {
    
    let id = UUID().uuidString
    let category: Category
    let subcategory: String
    let subtitle: String
    let subdescription: String
    let instruction: String
    let cardImage: Image
}

class SubCategoryViewModel: ObservableObject {
    
    @Published var gameSubCategories: [SubCategoryModel] = SubCategoryModel.allSubs
    @Published var filteredCategory: [SubCategoryModel] = []
    @Published var categoryType: Category = .explicit

    init() {
        filterByCategory()
    }
    
    func filterByCategory() {
        DispatchQueue.global(qos: .userInteractive).async {
            
            let results = self.gameSubCategories
                .lazy
                .filter { item in
                    return item.category == self.categoryType
                }

            DispatchQueue.main.async {
                self.filteredCategory = results.compactMap({ item in
                    return item
                })
            }
        }
    }
}

extension SubCategoryModel {
    static let allSubs = [
        SubCategoryModel(
            category: .explicit,
            subcategory: "Significant Other",
            subtitle: "Bedroom Eyes",
            subdescription: "[Insert sub-description here]",
            instruction: "Instructions:\n \n1) Each player pick a song\n2) Be funny, genuine, or a maverick\n3) Enjoy the trip down memory lane",
            cardImage: Image(uiImage: #imageLiteral(resourceName: "Explicit"))
        ),
        SubCategoryModel(
            category: .explicit,
            subcategory: "Dating",
            subtitle: "First Date",
            subdescription: "[Insert sub-description here]",
            instruction: "Instructions:\n \n1) Each player pick a song\n2) Be funny, genuine, or a maverick\n3) Enjoy the trip down memory lane",
            cardImage: Image(uiImage: #imageLiteral(resourceName: "Explicit"))
        ),
        SubCategoryModel(
            category: .adventure,
            subcategory: "Hiking",
            subtitle: "Bedroom Eyes",
            subdescription: "[Insert sub-description here]",
            instruction: "Instructions:\n \n1) Each player pick a song\n2) Be funny, genuine, or a maverick\n3) Enjoy the trip down memory lane",
            cardImage: Image(uiImage: #imageLiteral(resourceName: "Adventure"))
        ),
    ]
}

My goal is to click on a card from the CategoryView screen and navigate to the SubCategoryView via a navigation link. I want the SubCategoryView to show a filtered list of subcategories based on the category selected on the CategoryView screen.

CategoryView to SubCategoryView GIF

CategoryLoop code snippet:

struct CategoryLoop: View {
    
    let categories: [CategoryModel]
    
    var body: some View {
        
        ZStack {
            ScrollView {
                VStack {
                    LazyVGrid(columns: [GridItem(.adaptive(minimum: 150), spacing: 20)], spacing: 20) {
                        ForEach(categories) { item in
                            NavigationLink(destination: SubCategoryView()
                                            .navigationTitle(item.category.rawValue)) {
                                CategoryCard(category: item)
                            }
                        }
                    }

Based on the code in my CategoryLoop file, what is the best/easiest way to pass my model data and filter the list on the SubCategoryView? I am having trouble figuring out how to use the enumeration. Is it possible to write a function that would update @Published var categoryType: Category = .explicit (see SubCategoryModel) based on the card clicked in the LazyVGrid? Also, if you have suggestion on how to better organise my data models, please let me know.



Sources

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

Source: Stack Overflow

Solution Source