Skip to content

Instantly share code, notes, and snippets.

@bigmountainstudio
Last active March 12, 2024 21:24
Show Gist options
  • Save bigmountainstudio/cc6beba0ef18284696e97e59d8eb8cc0 to your computer and use it in GitHub Desktop.
Save bigmountainstudio/cc6beba0ef18284696e97e59d8eb8cc0 to your computer and use it in GitHub Desktop.
SwiftData Aggregate Functions
// Copyright © 2024 Big Mountain Studio. All rights reserved. Twitter: @BigMtnStudio
import SwiftData
import SwiftUI
@Model
class EmployeeModel {
var employeeId: UUID
var birthDate: Date
var firstName: String
var lastName: String
var gender: String
var hireDate: Date
var salary: Decimal
init(employeeId: UUID, birthDate: Date, firstName: String, lastName: String, gender: String, hireDate: Date, salary: Decimal) {
self.employeeId = employeeId
self.birthDate = birthDate
self.firstName = firstName
self.lastName = lastName
self.gender = gender
self.hireDate = hireDate
self.salary = salary
}
var viewAge: Int {
let calendar = Calendar.current
let now = Date()
let ageComponents = calendar.dateComponents([.year], from: birthDate, to: now)
return ageComponents.year ?? 0
}
}
// Aggregate function examples
extension EmployeeModel {
static func getSalarySum(employees: [EmployeeModel]) -> Decimal {
// Sum of salaries (reduce combines values of current + next items in a sequence)
let totalSalary = employees.reduce(Decimal(0)) { $0 + $1.salary }
return totalSalary
}
static func getAverageAge(employees: [EmployeeModel]) -> Int {
// Calculate the average age of employees using their birthDate
let totalAge = employees.reduce(0) { $0 + Calendar.current.dateComponents([.year], from: $1.birthDate, to: Date()).year! }
return totalAge / employees.count
}
static func getOldestEmployee(modelContext: ModelContext) -> String {
var fetch = FetchDescriptor<EmployeeModel>()
fetch.sortBy = [SortDescriptor(\EmployeeModel.birthDate, order: .forward)]
fetch.fetchLimit = 1
return try! modelContext.fetch(fetch).first!.firstName
}
static func getYoungestEmployees(modelContext: ModelContext) -> [EmployeeModel] {
// Youngest Male
let maleFilter = #Predicate<EmployeeModel> { $0.gender == "M" }
var fetchDescriptorMale = FetchDescriptor<EmployeeModel>()
fetchDescriptorMale.predicate = maleFilter
fetchDescriptorMale.sortBy = [SortDescriptor(\EmployeeModel.birthDate, order: .reverse)]
fetchDescriptorMale.fetchLimit = 1
let youngestMale = try! modelContext.fetch(fetchDescriptorMale).first!
// Youngest Female
let femaleFilter = #Predicate<EmployeeModel> { $0.gender == "F" }
var fetchDescriptorFemale = FetchDescriptor<EmployeeModel>()
fetchDescriptorFemale.predicate = femaleFilter
fetchDescriptorFemale.sortBy = [SortDescriptor(\EmployeeModel.birthDate, order: .reverse)]
fetchDescriptorFemale.fetchLimit = 1
let youngestFemale = try! modelContext.fetch(fetchDescriptorFemale).first!
return [youngestMale, youngestFemale]
}
}
extension EmployeeModel {
@MainActor
static var preview: ModelContainer {
let container = try! ModelContainer(for: EmployeeModel.self,
configurations: ModelConfiguration(isStoredInMemoryOnly: true))
let employees = [
EmployeeModel(employeeId: UUID(), birthDate: Date(timeIntervalSince1970: -568025136), firstName: "John", lastName: "Doe", gender: "M", hireDate: Date(timeIntervalSince1970: 915148800), salary: 50000),
EmployeeModel(employeeId: UUID(), birthDate: Date(timeIntervalSince1970: -315360000), firstName: "Jane", lastName: "Smith", gender: "F", hireDate: Date(timeIntervalSince1970: 1009843200), salary: 55000),
EmployeeModel(employeeId: UUID(), birthDate: Date(timeIntervalSince1970: 0), firstName: "Emily", lastName: "Davis", gender: "F", hireDate: Date(timeIntervalSince1970: 1199145600), salary: 65000),
EmployeeModel(employeeId: UUID(), birthDate: Date(timeIntervalSince1970: 157680000), firstName: "Michael", lastName: "Miller", gender: "M", hireDate: Date(timeIntervalSince1970: 1293840000), salary: 70000),
]
for employee in employees {
container.mainContext.insert(employee)
}
return container
}
}
struct AggregateFunctions: View {
@Environment(\.modelContext) private var modelContext
@Query private var employees: [EmployeeModel]
var body: some View {
Form {
Section("Employees") {
ForEach(employees) { employee in
VStack {
LabeledContent(employee.firstName, value: employee.salary, format: .currency(code: "USD"))
LabeledContent("Age", value: employee.viewAge, format: .number)
.font(.footnote)
}
}
}
Section("Summary") {
LabeledContent("Total Budget", value: EmployeeModel.getSalarySum(employees: employees), format: .currency(code: "USD"))
LabeledContent("Average Age", value: EmployeeModel.getAverageAge(employees: employees), format: .number)
LabeledContent("Oldest Employee", value: EmployeeModel.getOldestEmployee(modelContext: modelContext))
}
Section("Youngest") {
ForEach(EmployeeModel.getYoungestEmployees(modelContext: modelContext)) { employee in
LabeledContent(employee.firstName, value: employee.viewAge, format: .number)
}
}
}
}
}
#Preview {
AggregateFunctions()
.modelContainer(EmployeeModel.preview)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment