2

My list filtering is slow with the current setup:

struct TechList: View {

@FetchRequest
var devices: FetchedResults<HearingDevice>


@State private var selectedDevice: String?
@ObservedObject var model = Model()

init(predicate: NSPredicate?) {
    let request: NSFetchRequest<HearingDevice> = HearingDevice.fetchRequest()
    request.sortDescriptors = []
    if let predicate = predicate {
        request.predicate = predicate
    }
    _devices = FetchRequest<HearingDevice>(fetchRequest: request)
}

var body: some View {
    List{
        ForEach(devices, id: \.self) { device in
            VStack(alignment: .center) {
                HStack{
                    Text(device.model ?? "Unknown" + " ")
                        .font(.system(size: 17))
                        .fontWeight(.medium)
                        .foregroundColor(self.selectedDevice == device.model ? Color.white:Color.init(hex: "47535B"))
                        .multilineTextAlignment(.leading)
                        .padding(.leading)
                    Spacer()
                }
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 60)
            .background(self.selectedDevice == device.model ? Color.init(hex: "666699"):Color.init(hex: "F6F6F6"))
            .cornerRadius(7.0)
            .onTapGesture {
                self.model.deviceModel = device.model!
                withAnimation(.spring()){
                    self.selectedDevice = device.model!
                }
            }
        }
    }.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
        .padding(.top, 170.0)
        .padding(.bottom, 23.0)
        .padding(.horizontal, 10.0)
     }
 }

I am using an NSPredicate to filter my fetch request. When the user inputs text into a field the list then updates to reflect the input text. Filtering the list however is extremely slow, I have less than 300 records in my data set so why would this be taking so long to filter? I will also post my textfield code below to see if anyone can recognise where this slow filtering may be occurring. I would like to mention I have also tried using an @Published variable for the input predicate to track when the user input changes instead of using onEditingChanged, this did not improve performance.

TextField("Search for Device by name", text: $searchInput, onEditingChanged: {_ in
        self.predicate = NSPredicate(format: "model contains %@", "\(self.searchInput)")

        print("THE PREDICATE: \(String(describing: self.predicate))")

        if self.searchInput == ""{
            self.predicate = nil
        }

    })
Luke Ireton
  • 479
  • 1
  • 3
  • 18
  • 1
    Fetch limit of course is a good thing, but here is the pitfall in refetching on every input char, which is not intended I assume. I would recommend to combine it with `Combine` (ups) and use `.debounce` operator so re-fetching happens only when user stops typing. – Asperi Dec 20 '19 at 17:32
  • [This topic](https://stackoverflow.com/questions/57922766/how-to-use-combine-on-a-swiftui-view) should be helpful. – Asperi Dec 20 '19 at 17:39

2 Answers2

2

The problem isn't your NSFetchRequest. The problem is SwiftUI related.

You just need to add:

.id(UUID()) to your List.

Ex:

List(items, id: \.self) {
    Text("Item \($0)")
}.id(UUID())

Please see https://www.hackingwithswift.com/articles/210/how-to-fix-slow-list-updates-in-swiftui

so you can get an idea of why this is happening

grantespo
  • 2,233
  • 2
  • 24
  • 62
1

Answering my own question I found one way you can achieve optimisation by using fetchLimit like so

 init(predicate: NSPredicate?) {
    let request: NSFetchRequest<HearingDevice> = HearingDevice.fetchRequest()
    request.sortDescriptors = []
    if let predicate = predicate {
        request.predicate = predicate
    }else{
        request.fetchLimit = 50 <------
    }
    _devices = FetchRequest<HearingDevice>(fetchRequest: request)
}

This way when loading the default unfiltered data set you can prevent the lag created by loading a large amount of items.

Luke Ireton
  • 479
  • 1
  • 3
  • 18