Hi all! I have a simple to-do list app which until now consisted of two entities:
Project > Task. That worked fine for me but now I wanted wanted to put another Entity in-between: Project > Module > Task. My issue now is that each task I add will be added to ALL Modules instead of the one I'm currently in. Would anyone be able to push me into the right direction?
Here is my code:
import SwiftUI
import CoreData
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: Project.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \Project.deadline, ascending: true),
NSSortDescriptor(keyPath: \Project.dueDate, ascending: true)])
var projects: FetchedResults<Project>
@State private var showingAddProject = false
var body: some View {
NavigationView {
ZStack {
Form {
ForEach(projects, id: \.self) { project in
Section(header: dueDate1()) {
// NavigationLink(destination: ProjectDetailView(project: project)) { <<< This is the old NavigationLink from when I only had two Entities.
NavigationLink(destination: Modules(project: project)) {
VStack (alignment: .leading){
HStack {
Text(project.wrappedName)
.font(.headline)
Text("(\(project.wrappedType))")
}
}
}
}
}
.onDelete(perform: deleteProjects)
}
VStack {
Spacer()
HStack {
Spacer()
Button {
self.showingAddProject.toggle()
} label: {
Image(systemName: "plus.circle.fill")
.font(.system(size: 60))
.frame(width: 70, height: 70)
.foregroundColor(.blue)
}
}.padding(.horizontal)
}
.navigationTitle("Roadmaps")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton ()
}
}.sheet(isPresented: $showingAddProject, content: {
AddProjectView().environment(\.managedObjectContext, self.moc)
})
}
}
}
func deleteProjects(at offsets: IndexSet) {
for offset in offsets {
let project = projects[offset]
moc.delete(project)
}
try? moc.save()
}
}
struct ContentView_Previews: PreviewProvider {
static let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
static var previews: some View {
let module = Module(context: moc)
ContentView()
}
}
struct Modules: View {
@Environment(\.managedObjectContext) var moc
@Environment(\.presentationMode) var presentationMode
@ObservedObject var project: Project
@State private var name = ""
@State private var showingAddModule = false
var body: some View {
Form {
Section(header: Text("Modules")) {
ForEach(project.modulesArray) { module in
NavigationLink(destination: TaskView(project: project)) {
HStack {
if module.isFinished {
Image(systemName: "checkmark.circle")
.foregroundColor(.green)
} else if module.hasPriority {
Image(systemName: "exclamationmark.triangle")
.foregroundColor(.red)
} else {
Image(systemName: "circle")
.foregroundColor(.orange)
}
VStack(alignment: .leading, spacing: 5) {
Text(module.wrappedName)
.fontWeight(.semibold)
.padding(.leading, 10)
}
}
}
}.onDelete(perform: deleteModule)
}
}.navigationTitle(project.wrappedName)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
self.showingAddModule.toggle()
} label: {
Image(systemName: "plus")
}
}
}.sheet(isPresented: $showingAddModule, content: {
AddModuleView(project: self.project).environment(\.managedObjectContext, self.moc)
})
}
func deleteModule(at offsets: IndexSet) {
withAnimation {
for index in offsets {
let module = project.modulesArray[index]
moc.delete(module)
PersistenceController.shared.save()
}
}
}
}
struct Modules_Previews: PreviewProvider {
static let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
static var previews: some View {
let project = Project(context: moc)
project.projectName = "Test Project"
let module = Module(context: moc)
return NavigationView {
Modules(project: project)
}
}
}
struct TaskView: View {
@Environment(\.managedObjectContext) var moc
@Environment(\.presentationMode) var presentationMode
@ObservedObject var project: Project
@State private var taskName: String = ""
@State private var showingProjectEditView = false
@State private var showingAddTask = false
@State var finished = false
@State private var category = "General"
var categories = ["General", "Blue", "Green"]
var body: some View {
VStack {
List {
Section(header: Text("Tasklist")) {
ForEach(project.tasksArray) { task in
NavigationLink(destination: EditTaskView(project: project, task: task)) {
HStack {
Image(systemName: task.finished ? "checkmark.circle" : "circle")
.foregroundColor(task.finished ? .green : .red)
Text(task.wrappedName)
.fontWeight(.semibold)
.padding(.leading, 10)
}
}
}.onDelete(perform: deleteTask)
}
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("Tasks")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
self.showingProjectEditView.toggle()
}) {
Image(systemName: "pencil")
}
}
}.sheet(isPresented: $showingProjectEditView, content: {
EditProjectView(project: self.project).environment(\.managedObjectContext, self.moc)
})
VStack {
HStack {
TextField("Enter Task", text: $taskName)
.modifier(TextFieldClearButton(text: $taskName))
.textFieldStyle(.roundedBorder)
.padding(.leading)
.frame(minHeight: CGFloat(30))
Button {
if !taskName.isEmpty {
addTask()
}
} label: {
Image(systemName: "arrow.up.circle.fill")
.font(.largeTitle)
}
Spacer()
}
.padding(.bottom)
}
}
}
func addTask() {
let newTask = Task(context: moc)
newTask.taskName = taskName
newTask.finished = false
project.addToTasks(newTask)
PersistenceController.shared.save()
self.taskName = ""
hideKeyboard()
}
func deleteTask(at offsets: IndexSet) {
withAnimation {
for index in offsets {
let task = project.tasksArray[index]
moc.delete(task)
PersistenceController.shared.save()
}
}
}
}
#if canImport(UIKit)
extension View {
func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
#endif
struct ProjectDetailView_Previews: PreviewProvider {
static var previews: some View {
let moc = PersistenceController.preview.container.viewContext
let newProject = Project(context: moc)
newProject.projectName = "Apple"
let task1 = Task(context: moc)
task1.taskName = "Blue"
let task2 = Task(context: moc)
task2.taskName = "Green"
newProject.addToTasks(task1)
newProject.addToTasks(task2)
return NavigationView {
TaskView(project: newProject)
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
}
Saving Projects, Modules per Project works fine. Just Task will get added to all Modules. All possible hints are much appreciated.
Many Thanks!