Update #4
- I've Reordered this post to read a little more easily. What you will read below will detail a bug I've experienced using SwiftUI. I recently requested code level support from apple who confirmed same and asked that I reach out to feedback for resolution (which was also done, no answer yet).
The bug is this: After displaying a List or ForEach in a SwiftUI View, if you alter that view by changing the number of items listed, the UI locks up while it attempts to calculate the number of rows that have changed / need to change..
I have seen others who have experienced this bug in the Apple dev forums. Their temporary solution was to "set the array to blank" thereby clearing the list completely for about 100 milliseconds before modifying the listed dataset. This will avoid the lock up sufficiently for users iterating a List or ForEach using an Array of data.
Problem is, with CoreData, being used as is described in this post, there does not seem any way to clear the list in between letters being pushed (fetch requests).
In Update #3, there is a GitHub project which shows a sample of this issue with sample data.
Any input on workarounds is appreciated.
Update #3
Not good.. As described in this post, I was able to change from using CoreData to a local SQLite database file. My results were that the search was just as slow as using CoreData. I do not know what is going on here. But maybe it is something with rendering results to the SwiftUI output? Either way, searching and displaying a large amount of data seems impossible..
Ive posted a sample project which demonstrates this problem on GitHub, per J. Doe's request. This project can be found here
I hope someone can see what I'm doing wrong. I find it hard to believe that this is just a limitation of iOS..
Original Post
Any ideas?
I feel like I am missing something fundamental. My fetch request (code below) is super slow. I have tried to add an index to the CoreData model with negative improvement (suggestion from J. Doe below). I am thinking maybe I need to somehow add a fetchBatchSize declaration to the fetch request (figured this out - see update #2 below - no help), but with the property wrapper @FetchRequest in SwiftUI, there does not seem to be a way to do this.
The code below is working on a test dataset of about 5,000 records. In the search, each time the input is changed (with each letter typed), the search gets run again which drags the system to a halt (100+% on CPU and growing memory usage).
In previous apps, I have completed similar tasks, but those apps used an SQLite data file and were written in ObjC. In those instances, things were really fast, with more than 3 times this test dataset.
If anyone can point me in the right direction to speed up my CoreData fetch, I would be very appreciative. I do not want to have to go back to an SQLite file if I don't have to..
Thank you very much!
Using SwiftUI, here is my code:
struct SearchView: View {
@Binding var searchTerm:String
var titleBar:String
var fetch: FetchRequest<MyData>
var records: FetchedResults<MyData>{fetch.wrappedValue}
init(searchTerm:Binding<String>, titleBar:String) {
self._searchTerm = searchTerm
self.titleBar = titleBar
self.fetch = FetchRequest(entity: MyData.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \ MyData.header, ascending: true)], predicate: NSCompoundPredicate(type: .and, subpredicates: [ NSCompoundPredicate(type: .or, subpredicates: [NSPredicate(format: "%K CONTAINS[cd] %@", #keyPath(MyData.title),searchTerm.wrappedValue), NSPredicate(format: "%K CONTAINS[cd] %@", #keyPath(MyData.details),searchTerm.wrappedValue)]), NSPredicate(format: "%K == %@", #keyPath(MyData.titleBar),titleBar)])) //fetch request contains logic for module and search data - need to fix sort order later
}
var body: some View {
List{
Section(header: SearchBar(text: $searchTerm)) {
ForEach(records, id: \.self) { fetchedData in
VStack {
NavigationLink(destination: DetailView(titleBar: fetchedData.title!, statuteData: fetchedData.details!, isFavorite: fetchedData.isFavorite)) {
HStack {
Text(fetchedData.header!)
.font(.subheadline)
VStack(alignment: .leading) {
Text(fetchedData.title!)
}
.scaledToFit()
Spacer()
if fetchedData.isFavorite {
Image(systemName: "star.fill")
.imageScale(.small)
.foregroundColor(.yellow)
}
}
}
}
}
}.navigationBarTitle(Text("Search"))
}
}
}
Thank you for your assistance.
Update:
Before edit, I had reported another issue with storing data, however, that issue was resolved with the post below:
Update #2:
My original question asked how to add the batch limit on my fetch to see if that helps. I was able to rewrite the fetch without using the FetchRequest wrapper, using NSFetchRequest and added the batch limit. It did nothing to help the situation..
Thanks again