Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performance Issue with SwiftUI List

I have a List with displays the result of a dynamic FetchRequest.

The code works fine, but having a bigger result set (e.g. 3000), the list is built quite slowly when the dynamic predicate changed.

struct ItemList: View {
    @State var startsWith: String = "A"

    var body: some View {
        NavigationView {
            VStack {
                TextField("Startswith", text:$startsWith)
                FilterRequestList(filter: startsWith)
            }
            .navigationBarTitle("Tasks CD")
        } 
    }
} 


struct FilterRequestList: View {
    var fetchRequest: FetchRequest<Item>

    init(filter: String) {
        if filter == "" {
            fetchRequest = FetchRequest<Item>(entity: Item.entity(),
                                              sortDescriptors: [],
                                              predicate: nil)
        } else {
            fetchRequest = FetchRequest<Item>(entity: Item.entity(),
                                              sortDescriptors: [],
                                              predicate: NSPredicate(format: "title BEGINSWITH %@", filter))
        }
    }

    var body: some View {
        VStack {
            Text("Count: \(fetchRequest.wrappedValue.count)")
            List(fetchRequest.wrappedValue, id: \.self) { item in
                Text("\(item.title) ")
            }
        }
    }
}

Any Idea, how to improve that?

Update: What I discovered: The first List is quite fast, but if the startsWith State changes, the reload is very slow. I added

FilterRequestList(filter: startsWith)
    .onAppear(perform: { print("appear F") })
    .onDisappear(perform: { print("disappear F") })

and discovered, the the FilterRequestList is not disappearing and reappearing, when the filter changed.

Could that be the problem? How can a recreation be forced?

like image 494
mica Avatar asked May 20 '26 03:05

mica


1 Answers

Thanks to Paul Hudson I found the solution for the problem. In "https://www.youtube.com/watch?v=h0SgafWwoh8" he explains it in detail.

You simply have to add the modifier

.id(UUID())

to the list.

The problem was, that swiftUI tries to detect any changes form the old list to the new one to animate the changes. With the modifier the old and the new list for swiftUI are not the same lists (because of the always changing id), so there is no need to detect the changes. Swift can simply create the new list very fast. The only downcast is, that therefore there is no animation.

struct ItemList: View {
    @State var startsWith: String = "A"

    var body: some View {
        NavigationView {
            VStack {
                TextField("Startswith", text:$startsWith)
                FilterRequestList(filter: startsWith)
            }
            .navigationBarTitle("Tasks CD")
        } 
    }
} 


struct FilterRequestList: View {
    var fetchRequest: FetchRequest<Item>

    init(filter: String) {
        if filter == "" {
            fetchRequest = FetchRequest<Item>(entity: Item.entity(),
                                              sortDescriptors: [],
                                              predicate: nil)
        } else {
            fetchRequest = FetchRequest<Item>(entity: Item.entity(),
                                              sortDescriptors: [],
                                              predicate: NSPredicate(format: "title BEGINSWITH %@", filter))
        }
    }

    var body: some View {
        VStack {
            Text("Count: \(fetchRequest.wrappedValue.count)")
            List(fetchRequest.wrappedValue, id: \.self) { item in
                Text("\(item.title) ")
            }
            .id(UUID())
        }
    }
}
like image 144
mica Avatar answered May 23 '26 03:05

mica



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!