I have a performance issue with List when I have a large amount of data that is replaced. Given the code below, a data set of about 3500 name items are loaded from a fetch request. Depending of the selected gender in the segmented picker these items are filtered and the displayed in the List. When the List first render I have no performance issue with the loading and rendering of items. It scrolls nicely and smoothly through 1700 items. But as soon as I switch gender through the segemented picker it takes about 30-45 seconds to render the List again.
I think this has to do with removing 1700 items and the inserting 1500 items again from the List. Is there a best practice how to reload a large amount of items in SwiftUI? Or can I reset the List before I load it again, since there is no issue initially.
Anyone else having issue same issue?
struct NameList: View {
@ObservedObject fileprivate var global = GlobalSettings()
@FetchRequest(
entity: Name.entity(),
sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)]
) var names: FetchedResults<Name>
@State var selectedGender = Defaults.gender
var body: some View {
let filtered = names.filter { $0.gender == self.selectedGender }
return NavigationView {
VStack {
Picker("Gender", selection: $global.gender) {
Text(Gender.female.rawValue.uppercased())
.tag(Gender.female)
Text(Gender.male.rawValue.uppercased())
.tag(Gender.male)
Text(Gender.unisex.rawValue.uppercased())
.tag(Gender.unisex)
}
.pickerStyle(SegmentedPickerStyle())
.padding()
List( filtered, id: \.self) { (item: Name) in
NameListRow(item: item)
}
}
}
.onReceive(Defaults.publisher(for: \.gender)) { (gender) in
self.selectedGender = gender
}
}
}
My guess is your problem is that you are trying to filter the list directly or the array you pass to the list. Which leads to to whole thing being a big performance hog. Maybe try what Apple themselves showed in their WWDC videos: do not pass your array to „List(array){}“, instead create a List without passing anything „List{}“ and then inside the brackets do a for each with your array, where yo decide via classic SwiftUI „if“ when to render certain elements. This gives you multiple benefits and seems the be the recommended way in case you want to modify list content based on certain States. Apple used this method in their tutorials, to filter favorite items in a list if I remember correctly.
Update: Ok, I made small test yesterday and this approach also didnt work. Will keep an eye on this in case soem solution comes into my mind
Update 3: A collegue found a simple solution:
Instead of a complicated workaround, just empty the List array and then set the new filters array. It may be necessary to introduce a delay so that emptying the listArray won't be omitted by the followed write.
List(listArray){item in ... }
self.listArray = []
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
self.listArray = newList
}