SwiftData Aggregation

Given a simple class:

final class Person {
   name: String
   age: Int
}

How can I get aggregated data such as average age, or count of people within specific ranges without retrieving all the data and doing the aggregates myself?

Replies

import SwiftData
@Model public class Person {
   var name: String
   var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

You'll probably want to do the below differently. The below ViewController is just given to you for ideas.

import UIKit
import SwiftData

class ViewController: UIViewController {
    var container: ModelContainer?
    var context: ModelContext?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        do {
            let schema = Schema([Person.self])
            let modelConfig = ModelConfiguration(schema: schema, cloudKitDatabase: .private("iCloud.com.yourcompany.ClassStats"))
            
            container = try ModelContainer(for: schema, configurations: [modelConfig])
            if let container = container {
                context = ModelContext(container)
                context?.autosaveEnabled = true
            }
        }
        catch {
            print(error)
        }
    }
    
    func getStats() {
        let minAge = 20
        let maxAge = 40
        let predicate = #Predicate<Person> { person in
            person.age >= minAge && person.age <= maxAge
        }
        let sortBy = [SortDescriptor<Person>(\.name)]
        var descriptor = FetchDescriptor<Person>(predicate: predicate, sortBy: sortBy)
        if let persons = try? context?.fetch(descriptor), persons.count > 0 {
            let count = persons.count
        }
    }
}

hi,

  • if you only need to count people within a certain age range, provide a predicate and use .fetchCount, rather than using .fetch and counting what comes back. no records are pulled for this and the operation should be pretty speedy.

  • if you need to average out the ages of all people, you can specify the .propertiesToFetch in the FetchDescriptor to return only the age property values (not whole records) and then add and divide.

at least that's what the documentation suggests right now (i am looking at beta 7, and Core Data already supports these).

hope that helps,

DMG