So basically, you dream language is C# linq.
So I figured out how to implement this DSL with the currently available tools.
I've published a beta of the project here.
The documentation isn't written yet, but the DSL works likes this:
let orderings: [NSSortDescription] = [\Person.age|asc, \Person.name|desc]
let predicate: NSPredicate = Person.age > 30 && \friends.count < 2
The same syntax also yields function forms:
let orderings: [(Person,Person)->Bool] = [\Person.age|asc, \Person.name|desc]
let predicate: (Person)->Bool = Person.age > 30 && \friends.count < 2
Type inference usually makes the explicit types unnecessary. Thus some NSManagedContext library could implement the such a shape:
let people = Person.select(
where: \.age > 30 && \friends.count < 2 ,
orderBy: [\Person.age|asc, \Person.name|desc])
I've tested the library against CoreData and everything is woking well for me. The only caveat is that the KeyPath has to be reachable via @objc, but that's implicit with @NSManaged anyway.
Interested parties are encouraged to test against their code bases. Please provide and responses/feedback/feature requests via the project site so we don't clutter this forum. Thank you!
Something like C# linq would be nice but I would decouple the data source from it though. I'm interested in creating Query objects that could be used to fetch into custom classes such as Core Data's managed objects, structs or dictionaries. Examples:
// Person is a managed object subclass or maybe a struct
// Query<Person>
let query =
from Person
where age > 30
// This query could be used to fetch data as managed objects as
// specified by the Foo entity in the object model.
// Query<ManagedObject>
let query =
from "Foo"
where someAttribute == "something"
// Just the first and last name... We are doing something simple, no need for
// fetching as managed objects here.
// Query<[String:Any]>
let query =
from Person
select firstName, lastName
where age > 30
It would be nice to query across to-many relationships. Let's imagine Family has a pets to-many relationship.
// Query<Family>
let familiesWithoutPetsQuery =
from Family
where pets.isEmpty
orderBy lastName asc
// Query<Family>
let familiesWithPetsQuery =
from Family
where pets.isEmpty == false
orderBy lastName asc
And compose Query objects like this:
/ / Query<Pet>
let isPuppyOrKitty =
from Pet
where age < 1.0 && type in (.dog, .cat)
// Query<Family>
let familiesWithPuppiesOrKitties =
from Family
where pets containsAnySatisfying isPuppyOrKittyQuery
orderBy pets.count
Fetching:
// Assume query is Query<Family>
// Fetch all rows as an array of Family objects
// [Family]
let families = managedObjectContext.fetch(query)
// Fetch one at a time and do something with it
// QueryIterator<Family>
var iterator = QueryIterator(query)
while let family = iterator.next(managedObjectContext) {
// do something with family here
family.doSomething()
// now save changes
managedObjectContext.saveChanges()
}