How to use IndexSet to find item in a custom Struct

199 Views Asked by At

I'm trying to use SwiftUI's .delete method on a list to identify what HKWorkout the user is trying to delete, but since I package my workouts into a custom WorkoutMonth object, I'm having difficulty figuring out how I can dig down into the specific workout the user is tapping on? In UIKIT i was able to use indexPath.section and indexPath.row to find it, but here I am lost.

struct MonthsWorkoutsListView: View {
    
    
    @Environment(\.colorScheme) var colorScheme
    @EnvironmentObject var trackerDataStore: TrackerDataStore
    @State var workouts: [TrackerWorkout]
    @State var workoutMonths = [WorkoutMonth]()
    
    var body: some View {
        VStack {
            if workoutMonths.count == 0 {
                Text("No workouts logged yet.  Open the Athlytic App on your Apple Watch to start and save a new workout.")
                    .multilineTextAlignment(.center)
                    .padding(.horizontal)
            } else {
                List {
                    ForEach(workoutMonths) { workoutMonth in
                        Section(header: Text(getFormattedYearMonth(workoutMonth: workoutMonth))) {
                            ForEach(workoutMonth.trackerWorkouts) { workout in
                                NavigationButton(
                                    action: {
                                        trackerDataStore.selectedWorkout = workout //this line is needed so that the image  at the top of WorkoutDetailHeaderCard gets updated,  otherwise the view loads before the async called and you end up with the  image of the last selected workout
                                        trackerDataStore.loadDataForSelectedWorkout(selectedWorkoutToBeUpdated: workout)
                                    },
                                    destination: {
                                        WorkoutDetailView().environmentObject(trackerDataStore)
                                    },
                                    workoutRow: { WorkoutRow(trackerWorkout: workout) }
                                )
                            }
                        }
                    }
                    .onDelete(perform: delete)
                }
                .navigationBarTitle(Text("Workouts"))
            }
        }
        .onAppear {
            workoutMonths = buildWorkoutsIntoYearMonths(workouts: workouts)
        }
    }
    
    func delete(at offsets: IndexSet) {

        //How to look up the workout user wants to delete?

    }

struct YearMonth: Comparable, Hashable {
    
    let year: Int
    let month: Int
    
    init(year: Int, month: Int) {
        self.year = year
        self.month = month
    }
    
    init(date: Date) {
        let comps = Calendar.current.dateComponents([.year, .month], from: date)
        self.year = comps.year!
        self.month = comps.month!
    }
    
    static func < (lhs: YearMonth, rhs: YearMonth) -> Bool {
        if lhs.year != rhs.year {
            return lhs.year < rhs.year
        } else {
            return lhs.month < rhs.month
        }
    }
}

struct WorkoutMonth: Identifiable {
    var id = UUID()
    var yearMonth: YearMonth
    var trackerWorkouts: [TrackerWorkout]
}
2

There are 2 best solutions below

0
GarySabo On BEST ANSWER

I figured it out, I had my .delete under the outside forEach and placing it here gave me access to the workoutMonth

ForEach(workoutMonth.trackerWorkouts) { workout in
    NavigationButton(
        action: {
            trackerDataStore.selectedWorkout = workout
            trackerDataStore.loadDataForSelectedWorkout(selectedWorkoutToBeUpdated: workout)
        },
        destination: {
            WorkoutDetailView().environmentObject(trackerDataStore)
        },
        workoutRow: { WorkoutRow(trackerWorkout: workout) }
    )
}
.onDelete { (offSets) in
    for offset in offSets {
    print("user wants to delete workout \(workoutMonth.trackerWorkouts[offset].startDate)")
    }
}
3
Grzegorz Krukowski On

You have IndexSet passed as parameter. This one can be used to remove proper workouts

workoutMonths.remove(atOffsets: offsets)

If you want to gather all objects that should be deleted, you can iterate using foreach loop as well. Then you get access to each of objects that will be removed.

for offset in offsets {
   var workoutMonth: WorkoutMonth = workoutMonths[offset]
}