I am creating a swiftUI app (called Interval) that uses CoreData to store Workouts and their Steps in a one-to-many relationship. Each Workout has many Steps.
ContentView -> DetailView -> EditView (as a sheet)
ContentView which shows a list of all Workouts. Tapping on a Workout in the list opens DetailView which shows a list of all steps in the selected workout.
In DetailView, tapping a button in the toolbar opens a sheet, called EditView, that lists all steps in the selected workout and allows the user to add, delete, or re-order steps.
To ensure list functionality works with CoreData, I am using a fetchRequest in EditView to fetch all steps of the selected workout. So far, I've not been able to maintain list functionality (.onDelete, .onMove) with any other method.
When I open EditView, the following warning pops up:
"Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x6000014ccea0>"
How can I remove the warning, and ensure my context is connected to the persistent store coordinator?
I assumed the EditView sheet would inherit the environment and automatically connect to the persistent store container. That doesn't seem to be the case.
import SwiftUI
@main
struct IntervalApp: App {
@StateObject var dataController = DataController()
var body: some Scene {
WindowGroup {
NavigationStack {
ContentView()
.environment(\.managedObjectContext, dataController.container.viewContext)
}
}
}
}
import Foundation
import CoreData
import SwiftUI
class DataController: ObservableObject {
let container = NSPersistentContainer(name: "Interval")
init() {
container.loadPersistentStores { description, error in
if let error = error {
print("CoreData failed to load: \(error.localizedDescription)")
}
}
}
}
import SwiftUI
import CoreData
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(fetchRequest: Workout.all()) private var workouts
var body: some View {
List {
ForEach(workouts) { workout in
NavigationLink {
DetailView(workout: workout)
} label: {
Text(workout.title)
}
}
}
.navigationTitle("Workouts")
}
}
import SwiftUI
struct DetailView: View {
@Environment(\.dismiss) var dismiss
@Environment(\.managedObjectContext) var moc
@ObservedObject var workout: Workout
@State private var showSheet: Bool = false
var body: some View {
List {
// list steps
}
.navigationBarTitle(workout.title)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button {
showSheet.toggle()
} label: {
Label("Edit", systemImage: "square.and.pencil")
}
}
}
.sheet(isPresented: $showSheet) {
NavigationStack {
EditView(workout: workout)
//.environment(\.managedObjectContext, moc) -> this did not work
}
}
}
}
import SwiftUI
import CoreData
struct EditView: View {
@Environment(\.dismiss) var dismiss
@Environment(\.managedObjectContext) var moc
@ObservedObject private var workout: Workout
@FetchRequest private var steps: FetchedResults<Step>
init(workout: Workout) {
self.workout = workout
_steps = FetchRequest(
entity: Step.entity(),
sortDescriptors: [ NSSortDescriptor(keyPath: \Step.index, ascending: true) ],
predicate: NSPredicate(format: "workout == %@", workout)
)
}
var body: some View {
List {
Section {
ForEach(steps) { step in
// list steps
}
//.onDelete()
//.onMove()
} header: {
Text("Steps")
}
}
.navigationTitle("Edit workout")
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button {
// save
dismiss()
} label: {
Text("Save")
}
}
}
}
}
I have tried passing the environment to the EditView sheet from DetailView like so:
.sheet(isPresented: $showSheet) {
NavigationStack {
EditView(workout: workout)
.environment(\.managedObjectContext, moc) // Adding this did not work
}
}
...but it did not work.
Move the line of code down 1 line
.sheet(isPresented: $showSheet) {
NavigationStack {
EditView(workout: workout)
}.environment(\.managedObjectContext, moc)
}
and
NavigationStack {
ContentView()
}.environment(\.managedObjectContext, dataController.container.viewContext)
Generally you want to inject onto the Stack
that way the NavigationLink
s have access to the values