Slate 3 is the version I always wanted to write but couldn’t until Swift gained macros, and AI was able to help do the heavy lifting to connect them to code generators and unit tests.
Slate 2 had a good API, but the data model still lived in an
.xcdatamodeld file that you edited in Xcode’s graphical editor, and a code
generator would slurp the XML and emit Swift. It worked, but the editor was finicky,
and you were always context-switching
between Swift and a GUI to make small changes.
SwiftData fixed this in its
own way by inventing @Model, but SwiftData also throws away the immutability
story that’s the entire point of Slate. Slate 3 uses macros to get SwiftData’s ergonomics without giving up the read-only, Sendable model surface:
@SlateEntity
public struct Patient {
public let patientId: String
public let firstName: String
public let lastName: String
public let age: Int?
}
slate-generator
reads the annotated structs and emits both the NSManagedObject subclasses
that Core Data needs internally and the bridge code that maps between them
and the immutable Patient value you’ll see in your app. No .xcdatamodeld
file. No GUI editor.
Access is async/await throughout, with the completion-based holdovers
from earlier versions stripped out:
let adults = try await slate.many(Patient.self, where: \.age >= 18, sort: [\.lastName])
Every public type is Sendable. Mutability is a private implementation
detail of the persistence layer, and the rest of the app never sees a
mutable model.