I am new to Core Data and am using a one-to-many relationship for a simple Notes app. A folder can have "many" notes. When a folder is created, notes can be created in that folder. My problem is that when I press the icon that's supposed to add a note, it does nothing. It used to add a note but ever since I attempted a one-to-many relationship amongst folders and notes, it does nothing. I know I must have a logic error somewhere.
To "filter" the notes by folder I passed in an ObservedObject in the Note view (which is the subview of the Folder view). I tried to set up a notes array but I've had no luck. I feel like there's a problem with my Core Dara Properties
FolderView:
struct Sidebar: View {
@Environment(\.managedObjectContext) private var viewContext
@State private var isPresented: Bool = false
@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Folder.folderName, ascending: true)])
var folders: FetchedResults<Folder>
@State private var selectedFoldersIds: Set<Folder.ID> = []
private var selectedFolder: Folder?{
guard let selectedFolderId = selectedFoldersIds.first,
let selectedFolder = folders.filter ({$0.id == selectedFolderId}).first else{
return nil
}
return selectedFolder
}
var body: some View {
Button(){
isPresented = true
} label: {
HStack{
Image(systemName: "folder.badge.plus")
Text("Add Folder")
}
.cornerRadius(50)
}
.sheet(isPresented: $isPresented){
AddFolderView{ name in
saveNewFolder(name: name)
}
}
.padding()
Divider()
Text("Folders")
.frame(maxWidth: .infinity, alignment: .leading)
.font(.caption)
.foregroundColor(.secondary)
List(folders, selection: $selectedFoldersIds){folder in
HStack{
Image(systemName: "folder")
NavigationLink(folder.folderName!, destination:NoteView(rootFolder: folder))
}
}
.onDeleteCommand(perform: deleteSelectedFolders)
.toolbar{
ToolbarItem(placement: .primaryAction){
Button(action: toggleSidebar) {
Image(systemName: "sidebar.left")
}
}
ToolbarItem(placement: .primaryAction){
Button(action: deleteSelectedFolders){
Label("Delete note", systemImage: "trash")
}
}
}
}
private func toggleSidebar() {
NSApp.keyWindow?.firstResponder?
.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
}
func saveNewFolder(name: String){
let folder = Folder(context: viewContext)
folder.folderId = UUID()
folder.folderName = name
do{
try viewContext.save()
} catch{
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
private func deleteSelectedFolders(){
withAnimation{
let selectedFolders = folders.filter {selectedFoldersIds.contains($0.id)}
deleteFolders(folders: selectedFolders)
}
}
private func deleteFolders(folders: [Folder]){
viewContext.perform{ folders.forEach(viewContext.delete)}
}
}
Note view:
struct NoteView: View {
@ObservedObject var rootFolder: Folder
@Environment(\.managedObjectContext) private var viewContext
@State private var isPresented: Bool = false
@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Note.name, ascending: true)])
var notes: FetchedResults<Note>
@State private var selectedNotesIds: Set<Note.ID> = []
private var selectedNote: Note?{
guard let selectedNoteId = selectedNotesIds.first,
let selectedNote = notes.filter ({$0.id == selectedNoteId}).first else{
return nil
}
return selectedNote
}
var body: some View {
List(rootFolder.notesArray, selection: $selectedNotesIds){ note in // is the problem here?
VStack{
NavigationLink(note.name!, destination: NoteEditor(note: note))
HStack{
Image(systemName: "folder")
Text(rootFolder.folderName!)
}
Divider()
}
}
.toolbar{
ToolbarItem(placement: .primaryAction){
Button(){
isPresented.toggle()
} label: {
Image(systemName: "square.and.pencil")
}
.sheet(isPresented: $isPresented){
AddNoteView{ name in
saveNewNote(name: name, text: "")
}
}
}
ToolbarItem(placement: .primaryAction){
Button(action: deleteSelectedNotes){
Label("Delete note", systemImage: "trash")
}
}
}
}
func saveNewNote(name: String, text: String){
let note = Note(context: viewContext)
note.id = UUID()
note.name = name
note.text = text
do{
try viewContext.save()
} catch{
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
private func deleteSelectedNotes(){
withAnimation{
let selectedNotes = notes.filter {selectedNotesIds.contains($0.id)}
deleteNotes(notes: selectedNotes)
}
}
private func deleteNotes(notes: [Note]){
viewContext.perform{ notes.forEach(viewContext.delete)}
}
}
My Folder+CoreDataProperties:
extension Folder {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Folder> {
return NSFetchRequest<Folder>(entityName: "Folder")
}
@nonobjc class func fetchRequest(forID folderId: UUID) -> NSFetchRequest<Folder> {
let request = Folder.fetchRequest()
request.predicate = NSPredicate(format: "id = %@", folderId as CVarArg)
return request
}
@NSManaged public var folderId: UUID?
@NSManaged public var folderName: String?
@NSManaged public var notes: NSSet?
public var notesArray: [Note] {
let set = notes as? Set<Note> ?? []
return set.sorted {
$0.wrappedName > $1.wrappedName
}
}
}
Notes+CoreDataProperties:
extension Note {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Note> {
return NSFetchRequest<Note>(entityName: "Note")
}
@nonobjc class func fetchRequest(forID id: UUID) -> NSFetchRequest<Note> {
let request = Note.fetchRequest()
request.predicate = NSPredicate(format: "id = %@", id as CVarArg)
return request
}
@NSManaged public var id: UUID?
@NSManaged public var name: String?
@NSManaged public var text: String?
@NSManaged public var folder: Folder?
public var wrappedName: String {
name ?? "folderName"
}
}