Slate 2 (2023)

A few years into using Slate on real projects, two things were clearly missing. First, the API was completion-based, and the rest of my code had moved to async/await. Second, I was constantly reinventing the “observe these query results and refresh the UI” pattern, and it really should have lived in the framework.

Slate 2 was a refactor to fix both. The primary read API became:

let books = try await slate.query { context in
    try context[Book.self]
        .where(\.pageCount, .greaterThan(100))
        .sort(\.title)
        .fetch()
}

Mutations got the same async treatment, and queries gained a typed predicate builder (.where(\.field, .equals(value))) so you don’t have to drop into NSPredicate format strings unless you want to.

The bigger addition was first-class streaming via Combine publishers:

let publisher = slate.stream { request in
    request.sort(\.title)
}

Subscribers get snapshots that update whenever the underlying data changes. That’s the bread and butter of any Core Data UI.

The one thing Slate 2 still inherited from v1 was the requirement to author your data model in an .xcdatamodeld file and run the code generator against its XML. Fixing that meant waiting on Swift macros, which is where Slate 3 eventually went.

Source on github.