Luuk D. Jansen
Luuk D. Jansen

Reputation: 4508

SwfitData and Filtering and View Updates

I have an App and I am having serious performance issues.

There are events (currently approx 1000) which needs to be filtered.

Filter directly with Query doesn't work for all params (the distance to the event needs calculation) and it needs to be sorted by day to be displayed in sections.

I have a boolean which is set to false when the view is not presented, preventing updates when the view is not shown.

So I implemented the following @Query and then a data variable which is used by ForEach:

@Query(sort: \Event.startTime) private var events: [Event]
var data: [Date:[Event]] {
        return filterManager.sortedEventsByDay(events: filterManager.filteredEvents(events: events))
    } else {
        return [:]
    }
}

The sorting procedure works as follows:

func sortedEventsByDay(events: [Event]) -> [Date:[Event]] {
    var dict: [Date:[Event]] = [:]
    if(events.count == 0) { return dict }
    
    var currentDay: Date?
    var eventsOfTheDay = [Event]()
    for event in events {
        if currentDay == nil { currentDay = Calendar.current.startOfDay(for: events.first!.startTime) }
        if(Calendar.current.startOfDay(for: event.startTime) != currentDay){
            if(!eventsOfTheDay.isEmpty) {
                dict[currentDay!] = eventsOfTheDay
                eventsOfTheDay.removeAll()
            }
            currentDay = Calendar.current.startOfDay(for: event.startTime)
            eventsOfTheDay.append(event)
        } else {
            eventsOfTheDay.append(event)
        }
        
        if(!eventsOfTheDay.isEmpty){
            dict[currentDay!] = eventsOfTheDay
        }
    }
    return dict
}

and the filter

func filteredEvents(events: [Event])-> [Event] {
        var result = [Event]()
        if(events.count == 0) { return result }
        let locationManager = AppDelegate.instance.locationManager
        for event in events {
            if distance < 1000 && event.distanceFromCurrentLocation(locationManager) > distance { continue }
            if !selectedCategories.isEmpty && event.category != nil && !selectedCategories.contains(event.category!) { continue }
            
            let published = StatusType.PUBLISHED
            if event.statusId != published.id { continue }
            if maxPrice < 1000 && event.price > maxPrice { continue }
            if event.startTime < startDate { continue }
            if event.endTime > endDate { continue }
            if(!searchText.isEmpty && !(event.name.localizedStandardContains(searchText) || event.desc.localizedStandardContains(searchText))){ continue }
            
            result.append(event)            
        }
        return result
    }

While it does exactly what I want, updates when the events update and filters, but it is called hundred of times in the course of loading the view, and on a major update like showing the filter sheet, and I get:

<decode: bad range for [%{public}s] got [offs:398 len:984 within:0]>

in the process. It suggests data is updated while processing and it stalls (or crashes) on older phones and clearly is very inefficient.

So how can I do the update to the dataset once and then have it rendered. There might be a very obvious answer, but I don't see it at the moment.

Upvotes: 0

Views: 65

Answers (0)

Related Questions