BLACK FRIDAY SALE: Save big on all my Swift books and bundles! >>

SOLVED: Cannot find variable in scope

Forums > SwiftUI

I'm trying to pass along information from ExerciseDetailView to MyWorkouts using @ObservableObject but I get an error under the my preview provider in MyWorkouts that says "Cannot find 'listedWorkouts' in scope. I know my code is kinda messy but please help!

MyWorkouts:

import SwiftUI
/*
struct globalVariables {
    static var globalWorkoutArray = ["pushup"]
}
*/
struct MyWorkouts: View {

   // let myWorkouts = chosenWorkouts
    let workouts = chestWorkouts
    @ObservedObject var listedWorkouts: ListedWorkoutsViewModel

    var body: some View {
        NavigationView {
            ScrollView(.horizontal, showsIndicators: false) {
                HStack {
                    ForEach(listedWorkouts.listedWorkouts) { card in
                        NavigationLink(destination: ExerciseDetailView(exercise: card)) {
                            ZStack {
                                Image(card.image)
                                    .resizable()
                                    .aspectRatio(contentMode: .fill)
                                    .frame(width: 55, height: 225)

                                Text(card.name.uppercased() + "\n\(card.name)")
                                    .foregroundColor(.white)
                                    .font(.title)
                                    .multilineTextAlignment(.center)
                                    .frame(width: 185, height: 225)
                                    .background(Color.black.opacity(0.42))

                            }
                            .clipped()
                        .cornerRadius(20)
                        }

                    }
                }
            }
            .offset(x: 0, y: -25)
            .padding()

        }

    }
}

struct MyWorkouts_Previews: PreviewProvider {
    static var previews: some View {
        MyWorkouts(listedWorkouts: listedWorkouts)
    }
}
import SwiftUI

class ListedWorkoutsViewModel: ObservableObject {
    @Published var listedWorkouts = [Exercise]()
}

struct ExerciseDetailView: View {

    @Environment(\.presentationMode) var BackWorkoutPresentationMode
    var exercise: Exercise
    @State var setSelection: Int = 1
    @State var repSelection: String = "8"
    @State var showAlert: Bool = false
    @State var reps: Int = 8
    @State public var closedChecker = false
    @State var checker1 = false

  //  let savedExercise = globalVariables.globalWorkoutArray

    @StateObject var listedWorkoutsViewModel: ListedWorkoutsViewModel = ListedWorkoutsViewModel()

    var body: some View {
        NavigationView {
            VStack(alignment: .center) {
                Text("\(exercise.name)")
                    .font(.largeTitle)
                    .offset(y: -25)
                    .multilineTextAlignment(.center)
                Image("\(exercise.image)")
                    .resizable()
                    .scaledToFit()
                    .cornerRadius(10)
                    .frame(width: 340, height: 240)
                    .shadow(radius: 5)
                    .offset(y: -25)
                Text("Muscle: \(exercise.focus)")
                    .padding(.leading, 30)
                    .font(.title2)
                    .offset(y: -25)
                HStack {
                Text("Sets:")
                        .font(.headline)
                    Menu {
                        Picker(selection: $setSelection) {
                            ForEach(1..<11) { set in
                                Text("\(set)")
                            }
                        } label: {
                            Text("Sets:")
                        }

                    } label: {
                        Text("\(setSelection + 1)")
                            .foregroundColor(.red)
                    }

                }
                HStack {
                Text("Reps/Time:")
                    .font(.headline)
                    TextField("Enter reps or time here...", text: $repSelection)
                        .padding(8)
                        .frame(width: 50, height: 30)
                        .background(Color.gray.opacity(0.3).cornerRadius(10))
                        .foregroundColor(.red)

                }
                Spacer()

                Button {
                    saveButton()
                    //listedWorkoutsViewModel.listedWorkouts.append(Exercise(focus: exercise.focus, name: exercise.name, image: exercise.image, description: exercise.description, sets: exercise.sets))

                } label: {
                    Text("Save Workout")
                        .foregroundColor(.black)
                        .font(.title)
                        .padding()
                        .padding(.horizontal, 20)
                        .background(Color.red)
                        .cornerRadius(10)

                }
                Spacer()

                }
            .alert(isPresented: $showAlert) {
                Alert(title: Text("Reps must be a whole number!"))
            }

            }
        }

    func saveButton() {
        // make sure repSelection is an Integer
        if let checker = NumberFormatter().number(from: repSelection) {
            reps = checker.intValue
            listedWorkoutsViewModel.listedWorkouts.append(Exercise(focus: exercise.focus, name: exercise.name, image: exercise.image, description: exercise.description, sets: exercise.sets))

            //myWorkouts.append(exercise)
         //   globalVariables.globalWorkoutArray.append(contentsOf: Exercise(focus: exercise.focus, name: exercise.name, image: exercise.image, description: exercise.description, sets: exercise.sets))
            BackWorkoutPresentationMode.wrappedValue.dismiss()
            checker1 = true
        } else {
            showAlert.toggle()
        }
    }

    }

struct ExerciseDetailView_Previews: PreviewProvider {
    static var previews: some View {
        ExerciseDetailView(exercise: Exercise(focus: "random", name: "random", image: "push-ups", description: "random", sets: 3))
    }
}

   

The listedWorkouts property is part of MyWorkouts, not the MyWorkouts_Previews preview struct. The preview struct has no knowledge of listedWorkouts in MyWorkouts so you can't use it in the preview.

To fix the error, supply an instance of ListedWorkoutsViewModel in the preview.

struct MyWorkouts_Previews: PreviewProvider {
    static var previews: some View {
        MyWorkouts(listedWorkouts: ListedWorkoutsViewModel())
    }
}

You may need to pass arguments to the ListedWorkoutsViewModel init.

1      

Thank you so much for the solution. I have another view named "CreateWorkoutView" and I have a ForEach loop in it. When I set "listedWorkouts" into the ForEach loop, I get an error that says, "Generic struct 'ForEach' requires 'ListedWorkoutsViewModel' conform to 'RandomAccessCollection'"

How would I set ListedWorkoutsViewModel to a RandomAccessCollection if it is already an ObservableObject? I tried using a comma and the & sign but I get thrown many errors.

CreateWorkoutView:

struct CreateWorkoutView: View {

    @Environment(\.presentationMode) var BackToHomePresentationMode
    @State var workoutNameTextField: String = ""
    let dayOptions: [String] = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "Every Day", "Rest Day", "Any Day"]
    @State var workoutDaySelection: String = "Any Day"
    @State var showSheet: Bool = false
    @State var listOfExercises = []

    var body: some View {
        NavigationView {
            VStack {
                Button {
                    BackToHomePresentationMode.wrappedValue.dismiss()
                } label: {
                    Image(systemName: "xmark")
                        .foregroundColor(Color.black)
                        .offset(x: 170, y: -35)

                }

                // MARK: WORKOUT TEXTFIELD
                Spacer()
                TextField("Enter Workout name...", text: $workoutNameTextField)
                    .padding()
                    .background(Color.gray.opacity(0.3).cornerRadius(10))
                    .foregroundColor(.black)
                    .font(.headline)

                // MARK: DAY PICKER

                Picker(selection: $workoutDaySelection) {
                    ForEach(dayOptions, id: \.self) { option in
                        Text(option)
                            .tag(option)
                    }
                } label: {
                    Text("Label")
                }

                // MARK: ADD WORKOUT
                Spacer()
                VStack(alignment: .leading) {
                    Text("Add Workout")
                        .padding(.leading, 30)
                    Button {
                        showSheet.toggle()
                    } label: {
                        Image(systemName: "plus")
                            .frame(width: UIScreen.main.bounds.width, height: 25, alignment: .leading)
                            .padding(.leading, 30)
                            .foregroundColor(.black)

                    }
                    .sheet(isPresented: $showSheet) {
                        PlusWorkoutView()
                    }

                    HStack {
                        Color.gray.opacity(0.3)
                            .shadow(color: .gray, radius: 10)
                            .frame(width: UIScreen.main.bounds.width, height: 250, alignment: .center)
                            .padding()
                            .overlay {
                                ExtractedView(listedWorkouts: ListedWorkoutsViewModel())
                                    .offset(y: 25)
                                    .padding()
                            }

                    }

                }

                // MARK: SAVE BUTTON
                Spacer()
                Button {
                    // saveWorkout()
                } label: {
                    Text("Save Workout")
                        .foregroundColor(.black)
                        .padding()
                        .padding(.horizontal, 25)
                        .background(.red)
                        .cornerRadius(10)
                }
                Spacer()
            }
            .navigationTitle("Create a Workout")
        }
    }

   /* func saveWorkout() {
        MyWorkoutsWorkout.init(
    }*/
}

struct AddWorkoutView_Previews: PreviewProvider {
    static var previews: some View {
        CreateWorkoutView()
    }
}

struct ExtractedView: View {

    let workouts = chestWorkouts
    @ObservedObject var listedWorkouts: ListedWorkoutsViewModel

    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack {
                ForEach(listedWorkouts) { card in
                    NavigationLink(destination: ExerciseDetailView(exercise: card)) {
                        ZStack {
                            Image(card.image)
                                .resizable()
                                .aspectRatio(contentMode: .fill)
                                .frame(width: 55, height: 225)

                            Text(card.name.uppercased() + "\n\(card.name)")
                                .foregroundColor(.white)
                                .font(.title)
                                .multilineTextAlignment(.center)
                                .frame(width: 185, height: 225)
                                .background(Color.black.opacity(0.42))

                        }
                        .clipped()
                        .cornerRadius(20)
                    }

                }
            }
        }
        .offset(x: 0, y: -25)
        .padding()
    }
}

   

I'm assuming this is the code that is causing the error:

ForEach(listedWorkouts)

The reason you are getting the error is the following declaration in ExtractedView:

@ObservedObject var listedWorkouts: ListedWorkoutsViewModel

The listedWorkouts property is a single view model, not an array of view models. You cannot pass a single instance to ForEach so you are getting the error message about not conforming to RandomAccessCollection.

What you want to show is the array of workouts inside the view model. The following change should fix the error:

ForEach(listedWorkouts.listedWorkouts)

You should consider changing the name of the @ObservedObject to something besides listedWorkouts, as the view model has a listedWorkouts property.

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED In-app subscriptions are a pain to implement, hard to test, and full of edge cases. RevenueCat makes it straightforward and reliable so you can get back to building your app. Oh, and it's free if your app makes less than $10k/mo.

Learn more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.