Has anyone had any luck using the @FetchRequest modifier to set up a set of @FetchedResults using SwiftUI and Core Data and then dynamically modifying those results during runtime by changing the predicate and/or sort descriptors? Is there any way to do this using an elegent approach like the @FetchRequest combine pipeline? Thanks!
Replies
Code Block swift private var filteredFetchRequest: FetchRequest<Item> private var filteredItems: FetchedResults<Item> { filteredFetchRequest.wrappedValue } init(filter: String) { filteredFetchRequest = FetchRequest<Item>(entity: Item.entity(), sortDescriptors: [], predicate: NSPredicate(format: "title BEGINSWITH %@", filter)) }
Code Block @FetchRequest var filteredItems: FetchedResults<Item> init(filter: String) { _filteredItems = FetchRequest(sortDescriptors: [], predicate: NSPredicate(format: "title BEGINSWITH %@", filter)) }
Or you could pass a fetch request in from superview and it could even be @State there. Turn on SQL debugging and check how often you are hitting the database. I wish Apple would help us out here.
Extending what has already been suggested, you can modify the predicate of the wrappedValue to dynamically trigger an update to a SwiftUI list. In the example below, toggling the isFiltered @State causes an update of the List and it reloads the FetchedResults with the filtered or unfiltered predicate.
@FetchRequest private var items: FetchedResults<Item>
@State var isFiltered = false
let filteredPredicate: NSPredicate
let unfilteredPredicate: NSPredicate
init(element: Element) {
self.element = element
filteredPredicate = NSPredicate(format: "element == %@ && score > 0.85", element)
unfilteredPredicate = NSPredicate(format: "element == %@", element)
self._items = FetchRequest<Item>(entity: Item.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: true),
predicate: unfilteredPredicate,
animation: .default)
}
var listItems: FetchedResults<Moment> {
get {
_moments.wrappedValue.nsPredicate = isFiltered ? filteredPredicate : unfilteredPredicate
return moments
}
}
var body: some View {
List {
ForEach(Array(listItems.enumerated()), id: \.element) { index, item in
Text(item.name)
.toolbar {
ToolbarItem {
Button {
withAnimation {
isFiltered.toggle()
}
} label: {
Label("Filter Items", systemImage: isFiltered ? "star.circle.fill" : "star.circle")
}
}
}
}
}
}
After two years I have the solution you are looking for, but you probably don't need it anymore, but for others who may also be wondering the same thing here is an example of how to sort a FetchedResults type with a search bar
You'll ned to include this at the top of your struct. and you have to change "Item" to the name of the entity in your core data file.
@FetchRequest(sortDescriptors: []) var items: FetchedResults<Item>
@State private var searchingFor = ""
And link this to the end of your list or whatever contains your list. You'll have to change title to whatever the name of your attribute is in your core data file
.searchable(text: $searchingFor, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search Titles")
.onChange(of: searchingFor){ value in
if (searchingFor != ""){
items.nsPredicate=NSPredicate(format: "title CONTAINS[c] %@", searchingFor)
} else {
items.nsPredicate=nil
}
Let me know if you have any questions!
@WeagleWeagle I have a scenario in which the initialiser to the view passes a value and depending on the value I need to determine the predicate and sort descriptors.
Currently I have an if else statement and then I am setting self._items as suggested by @aharriscrowne
For that scenario, is it ok to set self._items in the initialiser?
-
I’m not sure. I don’t use @aharriscrowne ‘s method