0

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"
    }
    
    
}
jari85
  • 71
  • 5
  • https://stackoverflow.com/questions/58845896/swiftui-fetchrequest-core-data-changes-to-relationships-dont-refresh/70959918#70959918 – lorem ipsum Dec 24 '22 at 13:45
  • Also, every Note object needs to be wrapped in “@ObservedObject” – lorem ipsum Dec 24 '22 at 13:46
  • I used @ObservedObject var note: Note in my Text Editor (NoteEditor) view. Do I need to put that in my NoteView? Wouldn't I then have to pass it as a parameter for NoteView destination in my Sidebar view? – jari85 Dec 24 '22 at 14:00
  • You’ll need a subview. That example I linked as a little about all the issues you have been asking about. – lorem ipsum Dec 24 '22 at 14:14

0 Answers0