A type-safe, fluent Swift library for working with Core Data

Last update: Jun 27, 2020

CoreDataQueryInterface

CocoaPods compatible Carthage compatible Language Platforms

Core Data Query Interface (CDQI) is a type-safe, fluent, intuitive library for working with Core Data in Swift. CDQI tremendously reduces the amount of code needed to do Core Data, and dramatically improves readability by allowing method chaining and by eliminating magic strings. CDQI is a bit like jQuery or LINQ, but for Core Data.

NOTE: The cdqi tool used to generate attribute proxies is deprecated. Bug fixes and changes in Swift make it very simple to hand-code attribute proxies, so the cdqi tool is no longer necessary.

Features

  • Fluent interface, i.e., chainable methods
  • Large number of useful overloads
  • Type-safety in filter comparisons.
  • Filtering, sorting, grouping, aggregate expressions, limits, etc.
  • Optionally eliminates the use of magic strings so common in Core Data
  • Query reuse, i.e., no side-effects from chaining
  • Support for iOS 9+, macOS 10.11+, tvOS 9+, and watchOS 2+.
  • Swift 5

Overview

In essence, CDQI is a tool that allows the creation (and execution) of fetch requests using a fluent syntax. In most cases, this can reduce many lines of code to a single (but still highly readable) line.

let swiftDevelopers = managedObjectContext.from(Developer.self).
                      filter(any(Developer.e.languages.name == "Swift"))
                      orderDesc(by: Developer.e.lastName)
                      .limit(5)
                      .all()

Integration

Carthage

In your Cartfile, add the following line:

github "prosumma/CoreDataQueryInterface" ~> 7.0

CocoaPods

Add the following to your Podfile. If it isn't already present, you will have to add use_frameworks! as well.

pod 'CoreDataQueryInterface', '~> 7.0'

Attribute And Model Proxies

CDQI works through the use of attribute and model proxies. In CDQI, a proxy is a type that stands in for a Core Data model or attribute. There are built-in proxies for all the Core Data attribute types, e.g., Int32Attribute, StringAttribute and so on. For your own Core Data models, you will need to create your own proxies, which is very simple to do. Imagine we have two Core Data models, Employee and Department. There is a many-to-one relationship in Core Data between these models. To keep things simple, each has a simple name attribute of type String:

class Employee: NSManagedObjectModel {
    @NSManaged var name: String
    @NSManaged var department: Department
}

class Department: NSManagedObjectModel {
    @NSManaged var name: String
    @NSManaged var employees: Set<Employee>
}

The proxy classes for these should look like this:

class EmployeeAttribute: EntityAttribute, Subqueryable {
    public private(set) lazy var name = StringAttribute(key: "name", parent: self)
    public private(set) lazy var department = DepartmentAttribute(key: "department", parent: self)
}

extension Employee: Entity {
    public typealias CDQIEntityAttribute = EmployeeAttribute
}

class DepartmentAttribute: EntityAttribute, Subqueryable {
    public private(set) lazy var name = StringAttribute(key: "name", parent: self)
    public private(set) lazy var employees: EmployeeAttribute(key: "employees", parent: self)    
}

extension Department: Entity {
    public typealias CDQIEntityAttribute = DepartmentAttribute
}

Once this is done, CDQI can do its magic. Read on.

Starting a Query

A CDQI query is a chain of methods that build an NSFetchRequest. Almost all of the NSFetchRequest's functionality is supported, such as choosing the result type, limiting the number of records fetched, filtering, sorting, etc.

A query is started by creating an instance of Query, which takes two generic type parameters. The first one tells us which NSManagedObject subclass is the target of our query. The second tells us what the result of the query should be: Either the same NSManagedObject subclass or an NSDictionary.

let developerQuery = Query<Developer, Developer>()
let developerDictionaryQuery = Query<Developer, NSDictionary>()

Most Query instances are of the form Query<M, M> where M is an NSManagedObject which implements the Entity protocol. A perhaps better way to start a query is…

let developerQuery = Developer.cdqiQuery

Queries started with Query<Developer, Developer> or Developer.cdqiQuery have no implicit NSManagedObjectContext, so one must be passed when executing a query.

try Developer.cdqiQuery.order(by: Developer.e.lastName).all(managedObjectContext: moc)
try Developer.cdqiQuery,order(by: Developer.e.lastName).context(moc).all()

This pattern is so common that a convenience method exists on NSManagedObjectContext.

try moc.from(Developer.self).order(by: Developer.e.lastName).all()

Filtering

Filtering in Core Data requires an NSPredicate. CDQI has overloads of many of the built-in operators. These overloads generate Core Data friendly NSPredicates instead of Bools. They are carefully designed so as not to conflict with the ordinary operators.

Swift NSPredicate
Developer.e.lastName == "Li" "lastName == 'Li'"
Person.e.age >= 18 "age >= 18"
21...55 ~= Person.e.age "age BETWEEN 21 AND 55"
Person.e.firstName == "Friedrich" && Person.e.lastName == "Hayek" "firstName == 'Friedrich' AND lastName == 'Hayek'"

GitHub

https://github.com/prosumma/CoreDataQueryInterface
Comments
  • 1. Typesafe predicates

    Here are the features I'd like to see for type-safe predicates.

    • [ ] Support for any type conceivably supported by Core Data, especially the myriad of numeric types, both as literals and variables.
    • [ ] Type-safety should be optional (but the default). I.e., the old "loose" comparisons should still work, and should always be available as a fallback.
    • [ ] Transformables would be a nice bonus, but not required.
    Reviewed by Revolucent at 2016-03-29 15:52
  • 2. More unit test coverage

    While @patgoley and I were working on typesafe attributes for version 4, it became clear to me that my unit test coverage was not sufficient. For instance, there were no tests that covered scenarios like ANY employees.lastName == 'Goley', which led @patgoley to make some incorrect assumptions about how to-many attributes need to work. Had such a unit test failed, it would have led to less wasted effort.

    Reviewed by Revolucent at 2016-04-03 16:47
  • 3. cdqi script not included in cocoapods install

    I was attempting a Cocoapods install (only to figure out where the cdqi script would be located for reference) and realized it's not actually included in the podspec. Looking at the podspec documentation, I don't see anything for including files that aren't built and included in the Pods library. I'm not sure how we get around that, but it seems like a fatal flaw for cocoapods users.

    Reviewed by patgoley at 2016-04-30 23:53
  • 4. Complex boolean expressions

    Importing CDQI >= 4.0 into a compilation unit has an unfortunate and very annoying side effect. When complex boolean expressions are used with types supported by CDQI, the compiler thinks these are trying to create NSPredicates rather than Bools. For example:

    // This code works just fine:
    let x = 3
    if x > 0 { 
        /* blah blah */ 
    }
    
    // This code does not:
    if (x > 0) || (x < -44) {
       /* blah blah */
    }
    

    The compiler thinks we are trying to create an instance of NSPredicate in the second if statement. This is a pretty serious bug, although interestingly I have several large codebases that use CDQI and only encountered this in one place.

    The solution is to require that the LHS of a CDQI expression that generates an NSPredicate be an instance of an Attribute subclass.

    Reviewed by Revolucent at 2016-04-21 19:01
  • 5. Default -p option for CDQI tool

    If I run the tool without -p, I get compiler errors on my Entity conformance extensions. The errors state typealias CDQIAttribute must be public because it matches a requirement in protocol Entity. Works fine if I run with -p. Compiled using Xcode 8.3.2, Swift 3.1

    Reviewed by patgoley at 2017-05-16 01:04
  • 6. Fix Swift 4 Warnings

    No breaking changes for Swift 4, only a few warnings that certain generic constraints were redundant. For example, if you declare func query<M: NSManagedObject>() -> Query<M, M>, the M: NSManagedObject is redundant since it's already required by Query. I've fixed these warnings on my fork. Probably don't want to merge to master until Xcode 9 release, but perhaps just publish the branch on the main repository so it's available? For those of us who are OCD about project warnings hah.

    Reviewed by patgoley at 2017-06-13 23:07
  • 7. Eliminate, alter, or make caveats to references to type-safety.

    Strictly speaking, CDQI's filtering is not type safe. Using a proxy, we can make a comparison to any type, e.g.,

    let department = DepartmentAttribute()
    let predicate: NSPredicate = department.name == 7
    

    This produces the predicate "name == 7", which will probably fail at runtime. I have no plans to create a truly type-safe version of CDQI. CDQI is focused around flexibly building keypaths and predicates, not type-safety per se.

    Reviewed by Revolucent at 2016-01-02 23:27
  • 8. Don't include date in generated file comment header, maybe a hash instead?

    Including the date in the generated files causes unnecessary changes in git when run automatically. I'm not sure that the date of generation is important, really only the model version it was generated from. Perhaps a hash of the model itself would be more appropriate? Sorry, I know this is really picky, but I think CDQI is best used in automation (run script phase or CI), and I keep having to commit or reset model files that have changes only because it's a new day. Happy to implement it myself, just let me know what you think.

    Reviewed by patgoley at 2017-06-13 23:12
  • 9. Improve tests

    This pull request is all about test coverage. While the tests are very simple, it's mostly about just expressing every possible use case made possible by the framework's api to make sure they continue to compile forever or until we change our minds. The only tests that failed were using < or > against nil, so those operators and related functions have been removed.

    Code coverage gathering is now enabled by default on the scheme. The current coverage is at 93%, up from 70.

    Reviewed by patgoley at 2016-04-06 00:19
  • 10. Add debugging flag

    Add a launch flag along the lines of -com.apple.CoreData.SQLDebug, but instead of printing out the SQL (for which you can already use the aforementioned tag), this will print out any generated predicates, and possibly other useful information.

    -com.prosumma.CoreDataQueryInterface.Debug, perhaps?

    Reviewed by Revolucent at 2016-04-03 13:04
  • 11. Eliminate AttributeType protocol in favor of just using the plain Attribute class

    My original intention in including AttributeType was a form of future-proofing. But the more I think about it, the more I think it is unnecessary. Eliminating AttributeType will simplify a lot of code and will cause no breaking changes I can think of. The use of AttributeType anywhere other than the internal implementation of CDQI is probably quite rare.

    If anyone has any objections, please let me know.

    Reviewed by Revolucent at 2015-12-16 05:17
QueryKit, a simple type-safe Core Data query language.
QueryKit, a simple type-safe Core Data query language.

QueryKit QueryKit, a simple type-safe Core Data query language. Usage QuerySet<Person>(context, "Person")

Jul 29, 2022
JSON to Core Data and back. Swift Core Data Sync.
JSON to Core Data and back. Swift Core Data Sync.

Notice: Sync was supported from it's creation back in 2014 until March 2021 Moving forward I won't be able to support this project since I'm no longer

Aug 9, 2022
Core Data Generator (CDG for short) is a framework for generation (using Sourcery) of Core Data entities from plain structs/classes/enums.
Core Data Generator (CDG for short) is a framework for generation (using Sourcery) of Core Data entities from plain structs/classes/enums.

Core Data Generator Introduction Features Supported platforms Installation CDG Setup RepositoryType ModelType DataStoreVersion MigrationPolicy Basic U

Aug 3, 2022
Example repo of working with Core Data in a SwiftUI application

CoreData in SwiftUI This repository serves many purpose. But mostly so I can experiment with Core Data in SwiftUI, and also so I can use it show my ap

Jan 30, 2022
A minimalistic, thread safe, non-boilerplate and super easy to use version of Active Record on Core Data.
A minimalistic, thread safe, non-boilerplate and super easy to use version of Active Record on Core Data.

Skopelos A minimalistic, thread-safe, non-boilerplate and super easy to use version of Active Record on Core Data. Simply all you need for doing Core

Jun 30, 2022
🎯 PredicateKit allows Swift developers to write expressive and type-safe predicates for CoreData using key-paths, comparisons and logical operators, literal values, and functions.
🎯 PredicateKit allows Swift developers to write expressive and type-safe predicates for CoreData using key-paths, comparisons and logical operators, literal values, and functions.

?? PredicateKit PredicateKit is an alternative to NSPredicate allowing you to write expressive and type-safe predicates for CoreData using key-paths,

Aug 6, 2022
Super awesome Swift minion for Core Data (iOS, macOS, tvOS)

⚠️ Since this repository is going to be archived soon, I suggest migrating to NSPersistentContainer instead (available since iOS 10). For other conven

Aug 4, 2022
A powerful and elegant Core Data framework for Swift.
A powerful and elegant Core Data framework for Swift.

A powerful and elegant Core Data framework for Swift. Usage Beta version. New docs soon... Simple do that: let query = persistentContainer.viewContext

Jul 19, 2022
CloudCore is a framework that manages syncing between iCloud (CloudKit) and Core Data written on native Swift.
CloudCore is a framework that manages syncing between iCloud (CloudKit) and Core Data written on native Swift.

CloudCore CloudCore is a framework that manages syncing between iCloud (CloudKit) and Core Data written on native Swift. Features Leveraging NSPersist

Aug 3, 2022
Unleashing the real power of Core Data with the elegance and safety of Swift
 Unleashing the real power of Core Data with the elegance and safety of Swift

Unleashing the real power of Core Data with the elegance and safety of Swift Dependency managers Contact Swift 5.4: iOS 11+ / macOS 10.13+ / watchOS 4

Aug 4, 2022
HitList is a Swift App shows the implementation of Core Data.
HitList is a Swift App shows the implementation of Core Data.

HitList HitList is a Swift App shows the implementation of Core Data. It is the demo app of Ray Wenderlich's tech blog. For details please reference G

Dec 17, 2021
100% Swift Simple Boilerplate Free Core Data Stack. NSPersistentContainer
100% Swift Simple Boilerplate Free Core Data Stack. NSPersistentContainer

DATAStack helps you to alleviate the Core Data boilerplate. Now you can go to your AppDelegate remove all the Core Data related code and replace it wi

Jun 29, 2022
This project server as a demo for anyone who wishes to learn Core Data in Swift.

CoreDataDemo This project server as a demo for anyone who wishes to learn Core Data in Swift. The purpose of this project is to help someone new to Co

May 3, 2022
JSQCoreDataKit - A swifter Core Data stack

JSQCoreDataKit A swifter Core Data stack About This library aims to do the following: Encode Core Data best practices, so you don't have to think "is

Jul 29, 2022
JustPersist is the easiest and safest way to do persistence on iOS with Core Data support out of the box.
JustPersist is the easiest and safest way to do persistence on iOS with Core Data support out of the box.

JustPersist JustPersist is the easiest and safest way to do persistence on iOS with Core Data support out of the box. It also allows you to migrate to

Mar 13, 2022
A synchronization framework for Core Data.

Core Data Ensembles Author: Drew McCormack Created: 29th September, 2013 Last Updated: 15th February, 2017 Ensembles 2 is now available for purchase a

Aug 6, 2022
Core Data code generation

mogenerator Visit the project's pretty homepage. Here's mogenerator's elevator pitch: mogenerator is a command-line tool that, given an .xcdatamodel f

Jul 22, 2022
Super Awesome Easy Fetching for Core Data!

MagicalRecord In software engineering, the active record pattern is a design pattern found in software that stores its data in relational databases. I

Jul 27, 2022
A feature-light wrapper around Core Data that simplifies common database operations.
A feature-light wrapper around Core Data that simplifies common database operations.

Introduction Core Data Dandy is a feature-light wrapper around Core Data that simplifies common database operations. Feature summary Initializes and m

May 11, 2022