Hello community!
I'm working on a silly little app to make a list of movies to eventually watch and just started getting this error:
A NavigationLink is presenting a value of type “Movie” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated.
This was previously working fine but something that I did seems to have broken the app and I cannot figure it out. Find the code below:
ContentView:
import SwiftData
import SwiftUI
struct ContentView: View {
@Environment(\.modelContext) var modelContext
@Query var movies: [Movie]
@State private var addingMovie = false
var body: some View {
NavigationStack {
VStack {
List {
ForEach(movies) { movie in
NavigationLink(value: movie) {
Text(movie.title)
}
}
.onDelete(perform: removeMovie)
}
}
.navigationTitle("MovieNight")
.navigationDestination(for: Movie.self) { movie in
MovieDetailView(movie: movie)
}
.toolbar {
Button("Add Movie", systemImage: "plus") {
addingMovie.toggle()
}
EditButton()
}
.sheet(isPresented: $addingMovie, content: {
AddMovie()
})
}
}
func removeMovie(at offsets: IndexSet) {
for offset in offsets {
let movie = movies[offset]
modelContext.delete(movie)
}
}
}
#Preview {
ContentView()
}
MovieDetailView:
import SwiftData
import SwiftUI
struct MovieDetailView: View {
@Environment(\.modelContext) var modelContext
@Environment(\.dismiss) var dismiss
@State private var showingDeleteAlert = false
let movie: Movie
var body: some View {
NavigationStack {
Spacer()
Text(movie.title)
.font(.title)
.foregroundStyle(.secondary)
.padding()
Text("Added on \(movie.dateAdded.formatted(date: .long, time: .omitted))")
.font(.subheadline)
.padding(.bottom, 8)
Text(movie.genre)
.font(.title2)
.foregroundStyle(.secondary)
.padding(.bottom, /*@START_MENU_TOKEN@*/10/*@END_MENU_TOKEN@*/)
RatingView(rating: .constant(movie.rating))
.padding(0.5)
Spacer()
Spacer()
}
.navigationTitle("It's Showtime...")
.navigationBarTitleDisplayMode(.inline)
.scrollBounceBehavior(.basedOnSize)
.alert("Delete Movie?", isPresented: $showingDeleteAlert) {
Button("Delete", role: .destructive, action: deleteMovie)
Button("Cancel", role: .cancel) { }
} message: {
Text("Are you sure you want to delete this movie?")
}
.toolbar {
Button("Delete this movie", systemImage: "trash") {
showingDeleteAlert = true
}
}
}
func deleteMovie() {
modelContext.delete(movie)
dismiss()
}
}
#Preview {
do {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try ModelContainer(for: Movie.self, configurations: config)
let example = Movie(title: "Test Movie", genre: "Action", rating: 3)
return MovieDetailView(movie: example)
.modelContainer(container)
} catch {
return Text("Failed to create preview: \(error.localizedDescription)")
}
}
Movie Model:
import Foundation
import SwiftData
@Model
class Movie {
var id = UUID()
var title: String
var genre = ""
var watched = false
var rating = -1
let dateAdded = Date.now
static let genres = [
"Action",
"Adventure",
"Animation",
"Anime",
"Comedy",
"Crime",
"Drama",
"Family",
"Fantasy",
"Horror",
"Mystery",
"Romance",
"Science Fiction",
"Thriller",
"Documentary",
"Musical",
"Western",
"War",
"Historical",
"Superhero"
]
init(id: UUID = UUID(), title: String, genre: String = "", watched: Bool = false, rating: Int = -1) {
self.id = id
self.title = title
self.genre = genre
self.watched = watched
self.rating = rating
}
}
RatingView:
import SwiftUI
struct RatingView: View {
@Binding var rating: Int
var label = ""
var maximumRating = 5
var offImage: Image?
var onImage = Image(systemName: "star.fill")
var offColor = Color.gray
var onColor = Color.yellow
var body: some View {
HStack {
if label.isEmpty == false {
Text(label)
}
ForEach(1..<maximumRating + 1, id: \.self) { number in
Button {
rating = number
} label: {
image(for: number)
.foregroundStyle(number > rating ? offColor : onColor)
}
}
}
.buttonStyle(.plain)
}
func image(for number: Int) -> Image {
if number > rating {
offImage ?? onImage
} else {
onImage
}
}
}
#Preview {
RatingView(rating: .constant(4))
}
Any help would be appreciated. I am omitting the AddMovieView as that is functioning totally fine. And the MovieDetailView WAS working before I added a bunch of changes, adding the ratings and whatnot.