Hello
I have a list of data in SwiftUI. The data shown in the list can be saved or deleted by using Core Data. In the @FetchRequest property that I am using to display data, I initialized an NSPredicate and in the view, I gave the possibility to the user to change the value of the predicate so that he can filter data, and that is all working, the problem shows up when I delete data from the list when I do so the predicate becomes nil and I don't know why.
Here is the code
struct SectionList: View {
@FetchRequest(
entity: LifetimeInputs.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \LifetimeInputs.date, ascending: true)], predicate: nil
) var lifetimeInputsModel: FetchedResults<LifetimeInputs>
@FetchRequest(entity: Limit.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Limit.date, ascending: false)]) var limit: FetchedResults<Limit>
@Environment(\.dynamicTypeSize) var dynamicTypeSize
var size: CGFloat{
if UIDevice.current.userInterfaceIdiom == .phone {
switch dynamicTypeSize {
case .xSmall: return 11
case .small: return 13
case .medium: return 15
case .large: return 17
case .xLarge: return 19
case .xxLarge: return 21
case .xxxLarge: return 23
default: return 23
}
} else {
switch dynamicTypeSize {
case .xSmall: return 13
case .small: return 15
case .medium: return 17
case .large: return 19
case .xLarge: return 21
case .xxLarge: return 23
case .xxxLarge: return 25
case .accessibility1: return 27
case .accessibility2: return 29
default: return 29
}
}
}
@StateObject var lifeTimeInputsViewModel = LifeTimeInputsViewModel()
@Environment(\.managedObjectContext) private var viewContext
var conversion: Double {
if !limit.isEmpty{
switch limit.last?.unita {
case Unit.ml.rawValue: return 1
case Unit.oz.rawValue: return 29.574
default: return 1
}
}
return 1
}
@State private var wantsToFilter: Bool = false
@State private var dateSelected = Date()
var body: some View {
Section{
HStack{
Text("Filter")
Spacer()
Image(systemName: wantsToFilter ? "checkmark.circle" : "xmark")
.font(.system(size: size + 6))
.foregroundColor(wantsToFilter ? .green : .red)
.onTapGesture {
wantsToFilter.toggle()
if wantsToFilter{
lifetimeInputsModel.nsPredicate = NSPredicate(
format: "date >= %@ && date <= %@",
Calendar.current.dateInterval(of: .day, for: dateSelected)!.start as CVarArg,
Calendar.current.dateInterval(of: .day, for: dateSelected)!.end as CVarArg
)
} else{
lifetimeInputsModel.nsPredicate = nil
}
}
}
DatePicker("Date", selection: $dateSelected, displayedComponents: .date)
} header: {
Text("Filter")
.font(.system(size: size - 4))
}
.onChange(of: dateSelected, perform: { _ in
if wantsToFilter{
lifetimeInputsModel.nsPredicate = NSPredicate(
format: "date >= %@ && date <= %@",
Calendar.current.dateInterval(of: .day, for: dateSelected)!.start as CVarArg,
Calendar.current.dateInterval(of: .day, for: dateSelected)!.end as CVarArg
)
}
})
Section{
ForEach(lifetimeInputsModel){ lifetimeInputs in
HStack{
Text("\(lifetimeInputs.valori / conversion, specifier: format(unita: !limit.isEmpty ? limit[limit.count - 1].unita ?? ml : ml)) \(!limit.isEmpty ? limit[limit.count - 1].unita ?? ml: ml)")
.font(.system(size: size))
Spacer()
Text("\(dateFormatter.string(from: lifetimeInputs.date ?? Date()))")
.font(.system(size: size))
}
}
.onDelete{lifeTimeInputsViewModel.deleteItems(offsets: $0, lifetimeInputsModel: lifetimeInputsModel); }
} header: {
Text("History \(lifetimeInputsModel.count)".localized()).font(.system(size: size - 4))
}
}
}
Thank You!
I found a solution.
@FetchRequest(
entity: LifetimeInputs.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \LifetimeInputs.date, ascending: true)], predicate: nil
) var lifetimeInputsModel: FetchedResults<LifetimeInputs>
Becomes this (in my case)
@FetchRequest var lifetimeInputs: FetchedResults<LifetimeInputs>
init(nsPredicate: NSPredicate, sortDescriptors: [NSSortDescriptor]) {
let entity = LifetimeInputs.entity()
let fetchRequest = FetchRequest<LifetimeInputs>(entity: entity, sortDescriptors: sortDescriptors, predicate: nsPredicate, animation: .default)
self._lifetimeInputs = fetchRequest
}
The nsPredicate and the sortDescriptors must be passed to a parent view and you have to change/update (if you need to) them in that parent view.
Hope it works also for you