I’ve been trying to create a Workout tracking app and been coming across a weird bug that appears in my app.
When using a “Mark as Favourite” .swipeActions modifier in a ForEach and on a NavigationLink, the workout is inconsistently marked as a “Favourite”.
Here is the relevant WorkoutView code (within “var body: some View”) :
Workouts View
List {
ForEach(searchedWorkouts, id: .id) { workout in
NavigationLink {
WorkoutView(workout: workout, selectedTab: selectedTab, workoutTypes: workoutTypes, darkMode: darkMode)
} label: {
WorkoutsViewHStack(workout: workout, darkMode: darkMode)
}
.modifier(SwipeActionsAndAlertsModifiers(workout: workout, showingDeleteAlert: $showingDeleteAlert, showDeleted: $showDeleted, editingWorkout: $editingWorkout, markAsFavouriteAlert: $markAsFavouriteAlert, unmarkAsFavouriteAlert: $unmarkAsFavouriteAlert))
.fullScreenCover(isPresented: $editingWorkout) {
EditWorkoutView(workout: workout, workoutTypes: workoutTypes, selectedTab: $selectedTab, showingCompleted: $showingCompleted, showingLapsed: $showingLapsed, showingUpcoming: $showingUpcoming, darkMode: darkMode)
}
}
}
.searchable(text: $searchQuery, prompt: "Search by keyword or date")
.overlay {
if searchedWorkouts.isEmpty && searchQuery.isEmpty == false {
ContentUnavailableView {
Label("No results", systemImage: "magnifyingglass")
.padding()
Text("Check the spelling or try a new search.")
.font(.subheadline)
}
} else if searchedWorkouts.isEmpty {
ContentUnavailableView {
Label(filterType == "All" ? "No workouts found": "(filterType) workouts weren't found :( ", systemImage: "note")
.padding()
} actions: {
Button {
showingAddWorkout.toggle()
} label: {
Label("Add a new workout", systemImage: "plus.square")
}
}
}
}
}
and here is the SwipeActionsAndAlertsModifiers file:
SwipeActionsAndAlertsModifiers modifier
struct SwipeActionsAndAlertsModifiers: ViewModifier {
@Environment(.modelContext) var modelContext
@Bindable var workout: Workout
// For Deleting
@Binding var showingDeleteAlert: Bool
@Binding var showDeleted: Bool
// For Editing
@Binding var editingWorkout: Bool
// For Favouriting
@Binding var markAsFavouriteAlert: Bool
@Binding var unmarkAsFavouriteAlert: Bool
func body(content: Content) -> some View {
content
// Deleting
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button {
showingDeleteAlert.toggle()
} label: {
Image("icons8-delete-darkmode")
.resizable()
.frame(width: 28, height: 28)
.scaledToFit()
.tint(.red)
}
}
.alert("Delete Workout", isPresented: $showingDeleteAlert) {
Button("Delete", role: .destructive) {
modelContext.delete(workout)
showDeleted.toggle()
}
Button("Cancel", role: .cancel) {}
} message: {
Text("Are you sure you want to delete this workout permanently?")
}
// Favourited + Mark as Completed
.swipeActions(edge: .leading, allowsFullSwipe: false) {
Button {
if workout.favourites == false {
markAsFavouriteAlert.toggle()
} else {
unmarkAsFavouriteAlert.toggle()
}
} label: {
workout.favourites == false ?
Image(systemName: "heart.fill")
.resizable()
.frame(width: 5, height: 5)
.scaledToFit()
.tint(.yellow) :
Image(systemName: "heart.slash.fill") // padding of 60%
.resizable()
.frame(width: 5, height: 5)
.scaledToFit()
.tint(.yellow)
}
}
.alert("Mark as Favourite", isPresented: $markAsFavouriteAlert) {
Button("Mark as Favourite", action: { workout.favourites = true })
Button("Cancel", role: .cancel) {}
} message: {
Text("Mark this workout as a Favourite?")
}
.alert("Unmark as Favourite", isPresented: $unmarkAsFavouriteAlert) {
Button("Unmark as Favourite", action: { workout.favourites = false })
Button("Cancel", role: .cancel) {}
} message: {
Text("Unmark this workout as a Favourite?")
}
}
}
If any SwiftUI developer could let me know what the issue is / has the proficiency to debug it it would be much appreciated!! Let me know if more context explanation is required too. (The delete swipe action and alert works perfectly but the favouriting one doesn’t, even when it is supposed(?) to be as simple as workout.favourites = true or false)
The above is what I have tried but to no avail so far!
Gabriel Tang is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.