Latest 1.0.1
License MIT
Platforms ios 10.0, requires ARC



Twitter: @nashytitz

A lightweight convenience API for basic CoreData database tasks. Run the RecordsDemo Xcode scheme for a demonstration.

Scalar or transformable types not supported


Consider the following database schema.


A class for entity ‘Performer’, may look like the following.

import Foundation
import CoreData
import Records

public class Performer: NSManagedObject, Fetchable {

  @NSManaged public var dob: Date

  @NSManaged public var firstName: String

  @NSManaged public var lastName: String

  @NSManaged public var party: Party

  @NSManaged public var performances: Set<Performance>?


// sourcery:inline:Performer.ManagedObject.Query.stencil
// sourcery:end

By declaring conformance to Fetchable and adding annotation marks for sourcery, the following auto-completing syntax is at your disposal (after you compile).

let query = Performer.Query(dob: nil, firstName: "Maggie", lastName: nil, party: nil, performances: nil) // Fetch all with first name that BEGINSWITH[cd] `Maggie`
let performers: [Performer] = try! query.all(in: context)


But wait?…sourcery? 🤔


Sourcery is a boiler-plate generation tool that you can optionally use with this framework. A pre-written stencil file is provided here which will instruct soucery to write the ‘Query’ syntax for any NSManagedObject subclass that implements Fetchable.

Any change you make to your CoreData schema will trigger the regeneration of boiler-plate code. So for instance, if you removed the property lastName from Performer the new initialiser will be as follows.

let query = Performer.Query(dob: nil, firstName: "Maggie", party: nil, performances: nil)
let performers: [Performer] = try! query.all(in: context)

The Xcode compiler will highlight the changed initialiser at compile time. It’s useful to individually evaluate each call site effected by your schema changes.

Other Features

Take a look at the source in the RecordsDemo target for more details on these features.

To-Many relationship queries.

let restriction = RelationshipRestriction(operation: .allMatching, records: Set(arrayLiteral: performerA, performerB))
let query = Performance.Query(performers: restriction, event: nil, ability: nil, group: nil)
let performances: [Performance] = try! query.all(in: context)

Null predicate searching.

let performer: Performer? = try! Performer.fetchFirst(in: context)
let performer: Performer? = try! Performer.fetchFirst(in: context, sortedBy: NSSortDescriptor(key: "firstName", ascending: true))

Custom predicates are still available.

let performers: [Performer] = try! Performer.fetchAll(withPredicate: NSPredicate(format: "firstName CONTAINS[cd] %@", "Maggie"), in: context)

Small UIViewController subclasses.

import UIKit

class PerformancesViewController: UIViewController {

  var fetchedResultsController: PerformancesFetchedResultsController!

  @IBOutlet private weak var tableView: PerformancesTableView! {
    didSet {
      fetchedResultsController.delegate = tableView
      fetchedResultsController.dataSource = tableView
      tableView.dataSource = fetchedResultsController
      tableView.delegate = fetchedResultsController

  override func viewDidLoad() {
    title = "Performances"
    fetchedResultsController.selectPerformance = { performance in
      /// do something


String replacement with Enum.

let query = Party.Query(email: email, name: name, phone: phone, performers: nil, type: .school)

To use an enum set the string property on your Entity subclass to private. Then create a var for your enum.

import Foundation
import CoreData
import Records

public class Party: NSManagedObject, Fetchable {

  @NSManaged public var email: String

  @NSManaged public var name: String

  @NSManaged public var phone: String

  @NSManaged private var type: String

  @NSManaged public var performers: Set<Performer>?

  public enum PartyType: String {
    case school = "School"
    case independent = "Independent"

  public var type_: PartyType {
    get {
      return PartyType(rawValue: type)!
    set {
      type = newValue.rawValue


Make sure to set a default value on the property and write a unit test (see here).


For the latest release, select the release tab.


Add github "rob-nash/Records" to your Cartfile.


If you like this and you want to buy me a drink, use bitcoin.

Bitcoin Image

Bitcoin Address: 15Gj4DBTzSujnJrfRZ6ivrR9kDnWXNPvNQ

Latest podspec

    "name": "Records",
    "version": "1.0.1",
    "summary": "A lightweight convenience API for basic CoreData database tasks.",
    "description": "In just a few minutes, setup a fully functioning CoreData implementation that embraces the static, type-safe nature of Swift.",
    "homepage": "",
    "license": {
        "type": "MIT",
        "file": "LICENSE"
    "authors": {
        "Robert Nash": "[email protected]"
    "social_media_url": "",
    "platforms": {
        "ios": "10.0"
    "source": {
        "git": "",
        "tag": "1.0.1"
    "source_files": "Records/*.{swift}",
    "requires_arc": true,
    "pushed_with_swift_version": "4.0"

Pin It on Pinterest

Share This