0

How can I refresh a FetchRequest when I select a different date on a DatePicker? meaning I have a view with a datepicker, onchange of dateselected I want to see ONLY items completed on that date. My code is below but I keep getting an error that I cannot mutate a Fetchrequest.

Thanks in advance for your feedback how to solve this dynamic core data fetch request? (dynamically fetch when changing date picker).

thanks!

struct DoneView: View {
    
    @Environment(\.managedObjectContext) var viewContext
    //@EnvironmentObject var model: ContentModel
    
    @FetchRequest var fetchRequest: FetchedResults<ToDoItem>
    
    @State var dateSelected : Date = Date() //receive from prev view
    
    @State var selectedDateSub: Date
    
    @State var isPresented: Bool = false
    
    @State var taskDesc : String = ""
    @State var taskDetail : String = ""
    @State var taskUUID : UUID = UUID()
    
    
        init() {
            let calendar = Calendar.current
            let startDate = calendar.startOfDay(for: dateSelected)
            let endDate = calendar.date(byAdding: .day, value: 1, to: startDate)!
            let predicate = NSPredicate(format: "date_completion >= %@ AND date_completion < %@", argumentArray: [startDate, endDate])
    
            _fetchRequest = FetchRequest<ToDoItem>(sortDescriptors: [NSSortDescriptor(keyPath: \ToDoItem.timestamp, ascending: false)], predicate: predicate)
        }
    
    var body: some View {
        VStack{
            Text("Done Today").font(.title)
            
            //select day
            DatePicker("Select date", selection: $dateSelected, displayedComponents: .date)
                .datePickerStyle(GraphicalDatePickerStyle())
                .frame(maxHeight: 400)
                .onChange(of: dateSelected) { val in
                    
                }
            
            List {
                ForEach(getDoneItemsForThatDate(dateSelected: dateSelected), id: \.self) { task in
                    Button {
                        //Text(task.desc!)
                    } label: {
                        //Rectangle().foregroundColor(task.done ? .green : .white).frame(height: 40).cornerRadius(10)
                        Text(task.desc!)
                            .foregroundColor(task.done ? .white : task.today ? .white : .black)
                        //.foregroundColor(task.today ? .blue : .black)
                            .swipeActions(allowsFullSwipe: true) {
                                
                                Button(role: .cancel) {
                                    
                                    task.done.toggle()
                                    task.today = false
                                    task.date_completion = Date()
                                    
                                    do {try viewContext.save()} catch {
                                        let nsError = error as NSError
                                        fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
                                    }
                                    
                                    print("mark as done")
                                } label: {
                                    Label("Done", systemImage: "checkmark")
                                }
                                .tint(.green)
                            }
                    }
                    .contentShape(Rectangle())
                    .buttonStyle(PlainButtonStyle())
                    .listRowBackground(task.done ? Color.green : task.today ? Color.blue : Color.white)
                    //.listRowBackground(task.today ? Color.blue : Color.white)
                    .simultaneousGesture(LongPressGesture()
                        .onEnded { _ in
                            let impactMed = UIImpactFeedbackGenerator(style: .light)
                            impactMed.impactOccurred()
                            
                            task.today.toggle()
                            
                            do {try viewContext.save()} catch {
                                let nsError = error as NSError
                                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
                            }
                            
                            
                            print("Loooong")
                            print(task.today)
                        }
                    )
                    .highPriorityGesture(TapGesture()
                        .onEnded { _ in
                            
                            isPresented.toggle()
                            taskDesc = task.desc!
                            taskUUID = task.id!
                            print("Tap")
                        })
                    .sheet(isPresented: $isPresented, onDismiss: {
                        isPresented = false
                    }) {
                        itemDetail(itemDesc: taskDesc, itemDetail: taskDetail, itemUUID: taskUUID, isPresented: $isPresented)
                    }
                    
                    
                }
                .onDelete(perform: deleteItems)
                
                
            }
            //.listStyle(PlainListStyle())
            .listRowSeparator(.hidden)
            .listRowInsets(.init(top: 4, leading: 8, bottom: 4, trailing: 8))
            
            
        }
    }
    
    mutating func getDoneItemsForThatDate(dateSelected: Date) -> FetchRequest<ToDoItem>{
        
        let calendar = Calendar.current
        let startDate = calendar.startOfDay(for: dateSelected)
        let endDate = calendar.date(byAdding: .day, value: 1, to: startDate)!
        let predicate = NSPredicate(format: "date_completion >= %@ AND date_completion < %@", argumentArray: [startDate, endDate])
        
        return FetchRequest<ToDoItem>(sortDescriptors: [NSSortDescriptor(keyPath: \ToDoItem.timestamp, ascending: false)], predicate: predicate)
    }
Nat Serrano
  • 472
  • 1
  • 4
  • 17
  • 2
    Does this answer your question? [How to use a @FetchRequest with the new searchable modifier in SwiftUI?](https://stackoverflow.com/questions/68530633/how-to-use-a-fetchrequest-with-the-new-searchable-modifier-in-swiftui) – lorem ipsum Aug 29 '22 at 23:39
  • A better name would be `@FetchRequest var items: FetchedResults`. Use `items` directly in your SwiftUI view. Whenever picker value changes update `items.nsPredicate`. You don't need an init for the view, you don't need `getDoneItemsForThatDate`. Keep it simple – user1046037 Aug 30 '22 at 01:21
  • thanks for the comments, the searchable is a nice discovery for me, I end up using filter but I will experiemtn with your feedback – Nat Serrano Aug 30 '22 at 01:22

1 Answers1

0

I end up using filter and it works, but I will experiment with searchable as well!

List {
                ForEach(self.fetchRequest.filter{
                    
                    $0.date_completion! >= startDate ?? Date() && $0.date_completion! <= endDate ?? Date()}) { task in
                    Button {
                        isPresented.toggle()
                        taskDesc = task.desc!
                        taskUUID = task.id!
                    } label: {
                        Text(task.desc!)
                            .foregroundColor(task.done ? .white : task.today ? .white : .black)
                            .swipeActions(allowsFullSwipe: true) {
                                
                                Button(role: .cancel) {
                                    
                                    task.done.toggle()
                                    task.today = false
                                    task.date_completion = Date()
                                    
                                    do {try viewContext.save()} catch {
                                        let nsError = error as NSError
                                        fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
                                    }
                                    
                                    print("mark as done")
                                } label: {
                                    Label("Done", systemImage: "checkmark")
                                }
                                .tint(.green)
                            }
                    }
Nat Serrano
  • 472
  • 1
  • 4
  • 17
  • 2
    This makes predicate and filter redundant, @FetchRequest is to observe changes for a desired set of objects, ideally updating the `.nsPredicate` would be more appropriate. In your case a wider range of objects would be observed unnecessarily and only a subset of them would be displayed. Best to observe changes only for the objects the view wishes to show – user1046037 Aug 30 '22 at 01:57
  • noted, let me try to update the predicate when the date changes, how do you do that? CoreDataentity.nsPredicate = NSpredicate(etc...)? – Nat Serrano Aug 30 '22 at 02:10
  • Update `fetchRequest.nsPredicate` when the date changes. It is better to name your `fetchRequest` as `items` as it represents a set of items. Please refer to my first comment on the question (2nd comment under the question) – user1046037 Aug 30 '22 at 03:20