0

The basic gist is I'm building a meal planner app. I'm new to Swift and SwiftUI and this is my first project so I'm probably making a noob mistake lol Basically I want to pass the current index, which is a data object for a day and it's meals, to the DailyMealPlan view when I tap on the corresponding card. I don't get an error when I pass the currentDay variable to the view, but inside the second view file I'm not sure how I handle that data. I keep getting an error that "CurrentMealPlan is not in scope". I understand how scope works and I suspect I'm just missing something when it comes to passing the data into the second view.

ForEach code

ForEach(CurrentMealPlan.indices) { index in
                                    
  let currentDay = CurrentMealPlan[index]
    NavigationLink(
      destination: DailyMealPlan(DailyMealPlan: currentDay))
    {                                  
      MealPlanCard(
       Day: "\(CurrentMealPlan[index].DayTitle)",
       Breakfast: "\(CurrentMealPlan[index].Breakfast)",
       Lunch: "\(CurrentMealPlan[index].Lunch)",
       Dinner: "\(CurrentMealPlan[index].Dinner)"
      )
    }
}

DailyMealPlan view

struct DailyMealPlan: View {
    
    var DailyMealPlan: Day = CurrentMealPlan[index]
    
    var body: some View {
    
        ZStack {
            ScrollView {
                VStack {
                   
                    SingleMealCard(Meal: "Breakfast", CalCount: 500, MealInfo: "Meal info here")
                    
                    SingleMealCard(Meal: "Lunch", CalCount: 500, MealInfo: "Meal info here")
                    
                    SingleMealCard(Meal: "Dinner", CalCount: 500, MealInfo: "Meal info here")
                }
            }
        }
    }
}

CurrentMealPlan model

struct Day: Hashable {
    var id: Int
    var Date: String
    var DayTitle: String
    var Breakfast: String
    var Lunch: String
    var Dinner: String
    
    
    init(id:Int=0,Date:String="",DayTitle:String="",Breakfast:String="",Lunch:String="",Dinner:String="") {
        self.id  = id
        self.Date = Date
        self.DayTitle = DayTitle
        self.Breakfast = Breakfast
        self.Lunch = Lunch
        self.Dinner = Dinner
    }
}

let CurrentMealPlan: [Day] = [
    Day(
        id: 0,
        DayTitle: "Sunday",
        Breakfast:"Oatmeal",
        Lunch: "Sandwich",
        Dinner: "Cheeseburger with Fries"
    )
]
6
  • 1
    Can you include your CurrentMealPlan model? It'll be easier to show what to do with access to that. By the way, it's common practice in Swift to capitalize type names, but leave variable/property names in lowercase. Commented Jun 23, 2021 at 19:16
  • 1
    Try just removing = CurrentMealPlan[index]. Commented Jun 23, 2021 at 19:17
  • @jnpdx Updated with that! Of course that's an array but for space sake I only included 1 day. Commented Jun 23, 2021 at 19:34
  • Did you try @aheze's suggestion? Looks like that might do it Commented Jun 23, 2021 at 19:40
  • 1
    Thank you @aheze, that did it! Commented Jun 23, 2021 at 19:49

2 Answers 2

3

Let's walk through your code. First, you declared the CurrentMealPlan array inside the parent view. It probably looks something like this:

@State var CurrentMealPlan = [Day]()

Note: As jnpdx commented, you should lowercase property names like var currentMealPlan and var dailyMealPlan. Also, you should not have the same name for the DailyMealPlan view and the DailyMealPlan property... it's extremely confusing.

Your code is correct. Then, you loop over CurrentMealPlan, and want to pass each element over to the DailyMealPlan view:

DailyMealPlan(DailyMealPlan: currentDay))

That's also completely fine. So where does the CurrentMealPlan is not in scope error come from? It's this line:

var DailyMealPlan: Day = CurrentMealPlan[index] /// NO!

Remember, you declared CurrentMealPlan inside the parent view, not the DailyMealPlan view. That means the DailyMealPlan view can't access CurrentMealPlan.

However, the DailyMealPlan view does not need to access CurrentMealPlan. Back in the parent view, you're already looping through CurrentMealPlan and passing in each currentDay over to the DailyMealPlan view.

So, all you need to do is define the type of the DailyMealPlan property:

struct DailyMealPlan: View {
   var DailyMealPlan: Day /// here!
   ...
}

This lets the compiler know that var DailyMealPlan takes in a Day.

Sign up to request clarification or add additional context in comments.

2 Comments

@MattYoung Also worth nothing that having a DailyMealPlan (View) with a DailyMealPlan property of type Day is super confusing. I'd suggest renaming the former to DailyMealPlanView and the property to var day: Day (note the lowercase d)
@jnpdx completely agree, just edited my answer to clarify
0

I suggest using a MVVM approach, each screen view should have it's own view model (which should be a class extending a base view model or protocol) which you pass when you create the view.

In your case you should have:

protocol BaseViewModel {
}

class DailyMealPlanViewModel: BaseViewModel {
 @Published var day: Day

}

in the above I also added day as @Published so any changes on that will refresh the view, if the day never changes you can remove @Published.

in your view:

struct DailyMealPlan: View {
 @StateObject var viewModel: DailyMealPlanViewModel
 ...
 ...

And when navigating:

NavigationLink(
      destination: DailyMealPlan(viewModel: DailyMealPlanViewModel(day: currentDay))
    ...
    ...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.