I’m trying to get aggregate data for individual Realm records so that it can be graphed on a chart.
I have a ‘WorkDay’ Realm Object which stores values like ‘startTime’, ‘endTime’, ‘singleTimeIncome’, ‘penaltyIncome’, ‘totalIncome’, etc.
A charting example is that I want to collect the ‘totalIncome’ of all Work Days for an entire year and graph that against the ‘totalIncome’ for all Work Days of previous years.
My current approach is to cycle all WorkDay Realm Objects for a given year with a ‘for loop’ and store the values from WorkDay in a custom data struct. I move the values from the Realm WorkDay Object to an array in the data struct. The reason I want to add them into an array as it will then allow me to calculate things like ‘averageTotalIncome’ for that year as well as getting the ‘totalIncome’ for the entire year by running ‘reduce(0, +)’ on the array.
The custom data struct looks similar to this:
import Foundation
import SwiftDate
struct TimePeriodData {
//MARK: - Time Period
var timePeriod: TimePeriod = .week
var date: Date = Date()
var localDate: Date = Date()
//MARK: - Work Day Times
var startTimeArray: [Date] = []
var endTimeArray: [Date] = []
//MARK: - Work Day Hours
var hoursSingleTimeArray: [Double] = []
var hoursPenaltyArray: [Double] = []
var hoursTotalArray: [Double] = []
//MARK: - Work Day Income
var singleTimeIncomeArray: [Double] = []
var penaltyIncomeArray: [Double] = []
var totalIncomeArray: [Double] = []
}
I then collect all Work Days from the Realm, the function looks like this:
private func updateWorkDays() {
// Get Time Period Dates
let start = timePeriodStart()
let end = timePeriodEnd()
// Get Current User Work Days for Time Period
let allUserWorkDays = realm.objects(WorkDay.self).where { workDay in
workDay.date.contains(start ... end)
}
workDays = allUserWorkDays
}
Once I have the Work Days I then move the data into ‘TimePeriodData’ which will be used by SwiftUI to generate charts, etc. The code look something like this:
private func getData(for inputDate: Date, from inputWorkDays: Results<WorkDay>) -> TimePeriodData {
var data = TimePeriodData()
for workDay in inputWorkDays {
// Time Period
data.timePeriod = self.timePeriod
data.date = inputDate
data.localDate = workDayListBrain.getLocalDate(from: inputDate)
// Work Day Times
data.startTimeArray.append(workDay.startTime)
data.endTimeArray.append(workDay.endTime)
// Work Day Hours
data.hoursSingleTimeArray.append(workDay.hoursSingleTime)
data.hoursPenaltyArray.append(workDay.hoursPenalty)
data.hoursTotalArray.append(workDay.hoursTotal)
// Work Day Income
data.singleTimeIncomeArray.append(workDay.singleTimeIncome)
data.penaltyIncomeArray.append(workDay.penaltyIncome)
}
return data
}
When ‘getData’ returns the UI is updated. However what I am finding is that when I use ‘getData’ for an entire year I need to cycle 100 Work Day Objects and this takes around 2 Seconds. I know I will need to create ‘TimePeriodData’ for previous years as well so there is other data to graph against, assuming each of which will take 2 Seconds if they have 100 Work Days. This adds up quickly.
I’ve read other posts where people cycle 50,000 Realm Objects and it takes 0.1 Seconds. Given I am only cycling 100 and it takes two seconds, am I doing something fundamentally wrong here? Is there a better way to approach this?
What I have tried to date is using a map function instead of a ‘for loop’, this looks a bit like this:
data.startTimeArray = inputWorkDays.compactMap { workDay in workDay.startTime } as [Date]
data.endTimeArray = inputWorkDays.compactMap { workDay in workDay.endTime } as [Date]
But the ‘compactMap’ made no notable gains in speed. I also tried doing it all on a BG Thread, this made things ‘feel’ speedier as the UI was never stalled while processing data but it still took 2 Seconds to update.
Any help or advice you could offer would be very much appreciated. Thanks!