First I should say my current code works fine. Here's what I have in my ContentView:
struct ContentView: View {
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Pokemon.id, ascending: true)],
animation: .default)
private var pokedex: FetchedResults<Pokemon>
var body: some View {
NavigationStack {
List(pokedex) { pokemon in
NavigationLink(value: pokemon) {
Text(pokemon.name!.capitalized)
}
}
.navigationTitle("Pokedex")
.navigationDestination(for: Pokemon.self, destination: { pokemon in
PokemonDetail(pokemon: pokemon) // NOTICE THIS
})
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) // ALSO THIS
}
}
The part to notice is the line inside the .navigationDestination modifier where I am passing the pokemon object to the PokemonDetail view. I don't want to have to do that, and I'll explain why in a bit.
Also notice in the preview I am able to use a sample Pokemon that I created in the PersistenceController specifically for the preview, through the .environment modifier. I want to be able to do this same thing in my PokemonDetail view, which right now looks like this:
struct PokemonDetail: View {
let pokemon: Pokemon // NOTE: I don't like this property.
var body: some View {
ZStack {
Image(pokemon.background)
.resizable()
.scaledToFit()
.shadow(color: .black, radius: 6, x: 0, y: 0)
AsyncImage(url: pokemon.sprite) { image in
image
.resizable()
.scaledToFit()
.shadow(color: .black, radius: 6, x: 0, y: 0)
} placeholder: {
ProgressView()
}
.padding(.top, 100)
}
}
}
struct PokemonDetail_Previews: PreviewProvider {
static var previews: some View {
let context = PersistenceController.preview.container.viewContext
let fetchRequest: NSFetchRequest<Pokemon> = Pokemon.fetchRequest()
fetchRequest.fetchLimit = 1
let result = try! context.fetch(fetchRequest)
return PokemonDetail(pokemon: result.first!) // NOTE: I want to use the same .environment modifier I used in ContentView instead of having to create this context and fetch request.
}
}
Notice how throughout this file I am using that pokemon property that was passed from the previous view. And in the preview I have to fetch a pokemon to use.
If I could, I'd much rather do another @FetchRequest here in the PokemonDetail view and just somehow fetch the Pokemon I tapped on from the ContentView.
And I'd also like to use that same .environment modifier in my PokemonDetail preview instead of having to set up a whole new context and fetch request just to fetch a Pokemon for my preview.
One thought I had was to add an @Environment property to the top of my ContentView, and then add it to the PokemonDetail view with an .environment modifier, like this:
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext // NEW LINE
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Pokemon.id, ascending: true)],
animation: .default)
private var pokedex: FetchedResults<Pokemon>
var body: some View {
NavigationStack {
List(pokedex) { pokemon in
NavigationLink(value: pokemon) {
Text(pokemon.name!.capitalized)
}
}
.navigationTitle("Pokedex")
.navigationDestination(for: Pokemon.self, destination: { pokemon in
PokemonDetail() // CHANGE: pokemon argument was removed
.environment(\.managedObjectContext, viewContext) // NEW LINE
})
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
But still, in the PokemonDetail, I don't know how I would identify which Pokemon was tapped on so I could show the correct information.
My question is, is this even possible, and if so, how do I do that?