Unleashing the real power of Core Data with the elegance and safety of Swift
Dependency managers
Contact

- Swift 5.0: iOS 10+ / macOS 10.12+ / watchOS 3.0+ / tvOS 10.0+
- Previously supported Swift versions: Swift 3.2, Swift 4.2

Upgrading from CoreStore 5.x (min iOS 9) to 6.x (min iOS 10)? Check out the new features and make sure to read the Change logs.

CoreStore is now part of the Swift Source Compatibility projects.

## Why use CoreStore?

CoreStore was (and is) heavily shaped by real-world needs of developing data-dependent apps. It enforces safe and convenient Core Data usage while letting you take advantage of the industry’s encouraged best practices.

### Features

- 💎Tight design around Swift’s code elegance and type safety. CoreStore fully utilizes Swift’s community-driven language features.
- 🚦Safer concurrency architecture. CoreStore makes it hard to fall into common concurrency mistakes. The main `NSManagedObjectContext` is strictly read-only, while all updates are done through serial transactions. (See Saving and processing transactions)
- 🔍Clean fetching and querying API. Fetching objects is easy, but querying for raw aggregates (`min`, `max`, etc.) and raw property values is now just as convenient. (See Fetching and querying)
- 🔭Type-safe, easy to configure observers. You don’t have to deal with the burden of setting up `NSFetchedResultsController`s and KVO. As an added bonus, `ListMonitor`s and `ObjectMonitor`s can have multiple observers. This means you can have multiple view controllers efficiently share a single resource! (See Observing changes and notifications)
- 📥Efficient importing utilities. Map your entities once with their corresponding import source (JSON for example), and importing from transactions becomes elegant. Uniquing is also done with an efficient find-and-replace algorithm. (See Importing data)
- 🗑Say goodbye to .xcdatamodeld files! The new `CoreStoreObject` is the replacement to `NSManagedObject`. `CoreStoreObject` subclasses can declare type-safe properties all in Swift code, no need to maintain separate resource files for the models. As bonus, these special properties support custom types, and can be used to create type-safe keypaths and queries. (See Type-safe `CoreStoreObject`s)
- 🔗Progressive migrations. No need to think how to migrate from all previous model versions to your latest model. Just tell the `DataStack` the sequence of version strings (`MigrationChain`s) and CoreStore will automatically use progressive migrations when needed. (See Migrations)
- Easier custom migrations. Say goodbye to .xcmappingmodel files; CoreStore can now infer entity mappings when possible, while still allowing an easy way to write custom mappings. (See Migrations)
- 📝Plug-in your own logging framework. Although a default logger is built-in, all logging, asserting, and error reporting can be funneled to `CoreStoreLogger` protocol implementations. (See Logging and error reporting)
- ⛓Heavy support for multiple persistent stores per data stack. CoreStore lets you manage separate stores in a single `DataStack`, just the way .xcdatamodeld configurations are designed to. CoreStore will also manage one stack by default, but you can create and manage as many as you need. (See Setting up)
- 🎯Free to name entities and their class names independently. CoreStore gets around a restriction with other Core Data wrappers where the entity name should be the same as the `NSManagedObject` subclass name. CoreStore loads entity-to-class mappings from the managed object model file, so you can assign different names for the entities and their class names.
- 📙Full Documentation. No magic here; all public classes, functions, properties, etc. have detailed Apple Docs. This README also introduces a lot of concepts and explains a lot of CoreStore’s behavior.
- ℹ️Informative (and pretty) logs. All CoreStore and Core Data-related types now have very informative and pretty print outputs! (See Logging and error reporting)
- 🎗Objective-C support! Is your project transitioning from Objective-C to Swift but still can’t quite fully convert some huge classes to Swift yet? CoreStore adjusts to the ever-increasing Swift adoption. While still written in pure Swift, all CoreStore types have their corresponding Objective-C-visible bridging classes. (See Objective-C support)
- 🛡More extensive Unit Tests. Extending CoreStore is safe without having to worry about breaking old behavior.

Have ideas that may benefit other Core Data users? Feature Requests are welcome!

## Contents

- TL;DR (a.k.a. sample codes)
- Architecture
- CoreStore Tutorials (All of these have demos in the CoreStoreDemo app project!)
  - Setting up
    - In-memory store
    - Local store
  - Migrations
    - Declaring model versions
    - Starting migrations
    - Progressive migrations
    - Forecasting migrations
    - Custom migrations
  - Saving and processing transactions
    - Transaction types
      - Asynchronous transactions
      - Synchronous transactions
      - Unsafe transactions
    - Creating objects
    - Updating objects
    - Deleting objects
    - Passing objects safely
  - Importing data
  - Fetching and querying
    - `From` clause
    - Fetching
      - `Where` clause
      - `OrderBy` clause
      - `Tweak` clause
    - Querying
      - `Select<T>` clause
      - `GroupBy` clause
  - Logging and error reporting
  - Observing changes and notifications
    - Observe a single property
    - Observe a single object
    - Observe a list of objects
  - Objective-C support
  - Type-safe `CoreStoreObject`s
    - `VersionLock`s
- Roadmap
- Installation
- Changesets
- Contact
- Who uses CoreStore?
- License

## TL;DR (a.k.a. sample codes)

Setting-up with progressive migration support:

    CoreStore.defaultStack = DataStack(
        xcodeModelName: "MyStore",
        migrationChain: ["MyStore", "MyStoreV2", "MyStoreV3"]
    )

Adding a store:

    CoreStore.addStorage(
        SQLiteStore(fileName: "MyStore.sqlite"),
        completion: { (result) -> Void in
            // ...
        }
    )

Starting transactions:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let person = transaction.create(Into<MyPersonEntity>())
            person.name = "John Smith"
            person.age = 42
        },
        completion: { (result) -> Void in
            switch result {
            case .success: print("success!")
            case .failure(let error): print(error)
            }
        }
    )

Fetching objects (simple):

    let people = try CoreStore.fetchAll(From<MyPersonEntity>())

Fetching objects (complex):

    let people = try CoreStore.fetchAll(
        From<MyPersonEntity>()
            .where(\.age > 30),
            .orderBy(.ascending(\.name), .descending(.\age)),
            .tweak({ $0.includesPendingChanges = false })
    )

Querying values:

    let maxAge = try CoreStore.queryValue(
        From<MyPersonEntity>()
            .select(Int.self, .maximum(\.age))
    )

But really, there’s a reason I wrote this huge README. Read up on the details!

Check out the CoreStoreDemo app project for sample codes as well!

## Architecture

For maximum safety and performance, CoreStore will enforce coding patterns and practices it was designed for. (Don’t worry, it’s not as scary as it sounds.) But it is advisable to understand the magic of CoreStore before you use it in your apps.

If you are already familiar with the inner workings of CoreData, here is a mapping of `CoreStore` abstractions:

Core Data | CoreStore
--- | ---
`NSManagedObjectModel` / `NSPersistentStoreCoordinator` (.xcdatamodeld file) | `DataStack`
`NSPersistentStore` (Configurations in the .xcdatamodeld file) | `StorageInterface` implementations (`InMemoryStore`, `SQLiteStore`, `ICloudStore`)
`NSManagedObjectContext` | `BaseDataTransaction` subclasses (`SynchronousDataTransaction`, `AsynchronousDataTransaction`, `UnsafeDataTransaction`)

A lot of Core Data wrapper libraries set up their `NSManagedObjectContext`s this way:

Nesting saves from child context to the root context ensures maximum data integrity between contexts without blocking the main queue. But in reality, merging contexts is still by far faster than saving contexts. CoreStore’s `DataStack` takes the best of both worlds by treating the main `NSManagedObjectContext` as a read-only context, and only allows changes to be made within transactions on the child context:

This allows for a butter-smooth main thread, while still taking advantage of safe nested contexts.

## Setting up

The simplest way to initialize CoreStore is to add a default store to the default stack:

    try CoreStore.addStorageAndWait()

This one-liner does the following:

- Triggers the lazy-initialization of `CoreStore.defaultStack` with a default `DataStack`
- Sets up the stack’s `NSPersistentStoreCoordinator`, the root saving `NSManagedObjectContext`, and the read-only main `NSManagedObjectContext`
- Adds an `SQLiteStore` in the Application Support/ directory (or the Caches/ directory on tvOS) with the file name [App bundle name].sqlite
- Creates and returns the `NSPersistentStore` instance on success, or an `NSError` on failure

For most cases, this configuration is enough as it is. But for more hardcore settings, refer to this extensive example:

    let dataStack = DataStack(
        xcodeModelName: "MyModel", // loads from the "MyModel.xcdatamodeld" file
        migrationChain: ["MyStore", "MyStoreV2", "MyStoreV3"] // model versions for progressive migrations
    )
    let migrationProgress = dataStack.addStorage(
        SQLiteStore(
            fileURL: sqliteFileURL, // set the target file URL for the sqlite file
            configuration: "Config2", // use entities from the "Config2" configuration in the .xcdatamodeld file
            localStorageOptions: .recreateStoreOnModelMismatch // if migration paths cannot be resolved, recreate the sqlite file
        ),
        completion: { (result) -> Void in
            switch result {
            case .success(let storage):
                print("Successfully added sqlite store: \(storage)")
            case .failure(let error):
                print("Failed adding sqlite store with error: \(error)")
            }
        }
    )

    CoreStore.defaultStack = dataStack // pass the dataStack to CoreStore for easier access later on

> 💡If you have never heard of Configurations, you’ll find them in your .xcdatamodeld file

In our sample code above, note that you don’t need to do the `CoreStore.defaultStack = dataStack` line. You can just as well hold a reference to the `DataStack` like below and call all its instance methods directly:

    class MyViewController: UIViewController {
        let dataStack = DataStack(xcodeModelName: "MyModel") // keep reference to the stack
        override func viewDidLoad() {
            super.viewDidLoad()
            do {
                try self.dataStack.addStorageAndWait(SQLiteStore.self)
            }
            catch { // ...
            }
        }
        func methodToBeCalledLaterOn() {
            let objects = self.dataStack.fetchAll(From(MyEntity))
            print(objects)
        }
    }

The difference is when you set the stack as the `CoreStore.defaultStack`, you can call the stack’s methods directly from `CoreStore` itself:

    class MyViewController: UIViewController {
        // elsewhere: CoreStore.defaultStack = DataStack(modelName: "MyModel")
        override func viewDidLoad() {
            super.viewDidLoad()
            do {
                try CoreStore.addStorageAndWait(SQLiteStore.self)
            }
            catch { // ...
            }
        }
        func methodToBeCalledLaterOn() {
            let objects = CoreStore.fetchAll(From<MyEntity>())
            print(objects)
        }
    }

> 💡By default, CoreStore will initialize `NSManagedObject`s from .xcdatamodeld files, but you can create models completely from source code using `CoreStoreObject`s and `CoreStoreSchema`. To use this feature, refer to Type-safe `CoreStoreObject`s.

Notice that in our previous examples, `addStorageAndWait(_:)` and `addStorage(_:completion:)` both accept either `InMemoryStore`, `SQLiteStore`, or `ICloudStore`. These implement the `StorageInterface` protocol.

### In-memory store

The most basic `StorageInterface` concrete type is the `InMemoryStore`, which just stores objects in memory. Since `InMemoryStore`s always start with a fresh empty data, they do not need any migration information.

    try CoreStore.addStorageAndWait(
        InMemoryStore(
            configuration: "Config2" // optional. Use entities from the "Config2" configuration in the .xcdatamodeld file
        )
    )

### Local Store

The most common `StorageInterface` you will probably use is the `SQLiteStore`, which saves data in a local SQLite file.

    let migrationProgress = CoreStore.addStorage(
        SQLiteStore(
            fileName: "MyStore.sqlite",
            configuration: "Config2", // optional. Use entities from the "Config2" configuration in the .xcdatamodeld file
            migrationMappingProviders: [Bundle.main], // optional. The bundles that contain required .xcmappingmodel files
            localStorageOptions: .recreateStoreOnModelMismatch // optional. Provides settings that tells the DataStack how to setup the persistent store
        ),
        completion: { /* ... */ }
    )

Refer to the SQLiteStore.swift source documentation for detailed explanations for each of the default values.

CoreStore can decide the default values for these properties, so `SQLiteStore`s can be initialized with no arguments:

    try CoreStore.addStorageAndWait(SQLiteStore())

The file-related properties above are actually requirements of another protocol that `SQLiteStore` implements, the `LocalStorage` protocol:

    public protocol LocalStorage: StorageInterface {
        var fileURL: NSURL { get }
        var migrationMappingProviders: [SchemaMappingProvider] { get }
        var localStorageOptions: LocalStorageOptions { get }
        func dictionary(forOptions: LocalStorageOptions) -> [String: AnyObject]?
        func cs_eraseStorageAndWait(metadata: [String: Any], soureModelHint: NSManagedObjectModel?) throws
    }

If you have custom `NSIncrementalStore` or `NSAtomicStore` subclasses, you can implement this protocol and use it similarly to `SQLiteStore`.

## Migrations

### Declaring model versions

Until CoreStore 4.0, model versions were always assumed to be declared in .xcdatamodeld files. The `DataStack` loads these for us by accepting the .xcdatamodeld file name and the `Bundle` where the files can be found:

    CoreStore.defaultStack = DataStack(
        xcodeModelName: "MyModel",
        bundle: Bundle.main,
        migrationChain: ["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4"]
    )

Starting CoreStore 4.0, model versions are now expressed as a first-class protocol, `DynamicSchema`. CoreStore currently supports the following schema classes:

- `XcodeDataModelSchema`: a model version with entities loaded from a .xcdatamodeld file.
- `CoreStoreSchema`: a model version created with `CoreStoreObject` entities. (See Type-safe `CoreStoreObject`s)
- `UnsafeDataModelSchema`: a model version created with an existing `NSManagedObjectModel` instance.

All the `DynamicSchema` for all model versions are then collected within a single `SchemaHistory` instance, which is then handed to the `DataStack`. Here are some common use cases:

Multiple model versions grouped in a .xcdatamodeld file (Core Data standard method)

    CoreStore.defaultStack = DataStack(
        xcodeModelName: "MyModel",
        bundle: Bundle.main,
        migrationChain: ["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4"]
    )

`CoreStoreSchema`-based model version (No .xcdatamodeld file needed) (For more details, see also Type-safe `CoreStoreObject`s)

    class Animal: CoreStoreObject {
        // ...
    }
    class Dog: Animal {
        // ...
    }
    class Person: CoreStoreObject {
        // ...
    }

    CoreStore.defaultStack = DataStack(
        CoreStoreSchema(
            modelVersion: "V1",
            entities: [
                Entity<Animal>("Animal", isAbstract: true),
                Entity<Dog>("Dog"),
                Entity<Person>("Person")
            ]
        )
    )

Models in a .xcdatamodeld file during past app versions, but migrated to the new `CoreStoreSchema` method

    class Animal: CoreStoreObject {
        // ...
    }
    class Dog: Animal {
        // ...
    }
    class Person: CoreStoreObject {
        // ...
    }

    let legacySchema = XcodeDataModelSchema.from(
        modelName: "MyModel", // .xcdatamodeld name
        bundle: bundle,
        migrationChain: ["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4"]
    )
    let newSchema = CoreStoreSchema(
        modelVersion: "V1",
        entities: [
            Entity<Animal>("Animal", isAbstract: true),
            Entity<Dog>("Dog"),
            Entity<Person>("Person")
        ]
    )
    CoreStore.defaultStack = DataStack(
        schemaHistory: SchemaHistory(
            legacySchema + [newSchema],
            migrationChain: ["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4", "V1"]
        )
    )

`CoreStoreSchema`-based model versions with progressive migration

    typealias Animal = V2.Animal
    typealias Dog = V2.Dog
    typealias Person = V2.Person
    enum V2 {
        class Animal: CoreStoreObject {
            // ...
        }
        class Dog: Animal {
            // ...
        }
        class Person: CoreStoreObject {
            // ...
        }
    }
    enum V1 {
        class Animal: CoreStoreObject {
            // ...
        }
        class Dog: Animal {
            // ...
        }
        class Person: CoreStoreObject {
            // ...
        }
    }

    CoreStore.defaultStack = DataStack(
        CoreStoreSchema(
            modelVersion: "V1",
            entities: [
                Entity<V1.Animal>("Animal", isAbstract: true),
                Entity<V1.Dog>("Dog"),
                Entity<V1.Person>("Person")
            ]
        ),
        CoreStoreSchema(
            modelVersion: "V2",
            entities: [
                Entity<V2.Animal>("Animal", isAbstract: true),
                Entity<V2.Dog>("Dog"),
                Entity<V2.Person>("Person")
            ]
        ),
        migrationChain: ["V1", "V2"]
    )

### Starting migrations

We have seen `addStorageAndWait(...)` used to initialize our persistent store. As the method name’s ~AndWait suffix suggests though, this method blocks so it should not do long tasks such as store migrations. In fact CoreStore will only attempt a synchronous lightweight migration if you explicitly provide the `.allowSynchronousLightweightMigration` option:

    try dataStack.addStorageAndWait(
        SQLiteStore(
            fileURL: sqliteFileURL,
            localStorageOptions: .allowSynchronousLightweightMigration
        )
    }

if you do so, any model mismatch will be thrown as an error.

In general though, if migrations are expected the asynchronous variant `addStorage(_:completion:)` method is recommended instead:

    let migrationProgress: Progress? = try dataStack.addStorage(
        SQLiteStore(
            fileName: "MyStore.sqlite",
            configuration: "Config2"
        ),
        completion: { (result) -> Void in
            switch result {
            case .success(let storage):
                print("Successfully added sqlite store: \(storage)")
            case .failure(let error):
                print("Failed adding sqlite store with error: \(error)")
            }
        }
    )

The `completion` block reports a `SetupResult` that indicates success or failure.

Notice that this method also returns an optional `Progress`. If `nil`, no migrations are needed, thus progress reporting is unnecessary as well. If not `nil`, you can use this to track migration progress by using standard KVO on the `"fractionCompleted"` key, or by using a closure-based utility exposed in Progress+Convenience.swift:

    migrationProgress?.setProgressHandler { [weak self] (progress) -> Void in
        self?.progressView?.setProgress(Float(progress.fractionCompleted), animated: true)
        self?.percentLabel?.text = progress.localizedDescription // "50% completed"
        self?.stepLabel?.text = progress.localizedAdditionalDescription // "0 of 2"
    }

This closure is executed on the main thread so UIKit and AppKit calls can be done safely.

### Progressive migrations

By default, CoreStore uses Core Data’s default automatic migration mechanism. In other words, CoreStore will try to migrate the existing persistent store until it matches the `SchemaHistory`‘s `currentModelVersion`. If no mapping model path is found from the store’s version to the data model’s version, CoreStore gives up and reports an error.

The `DataStack` lets you specify hints on how to break a migration into several sub-migrations using a `MigrationChain`. This is typically passed to the `DataStack` initializer and will be applied to all stores added to the `DataStack` with `addSQLiteStore(...)` and its variants:

    let dataStack = DataStack(migrationChain:
        ["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4"])

The most common usage is to pass in the model version (.xcdatamodeld version names for `NSManagedObject`s, or the `modelName` for `CoreStoreSchema`s) in increasing order as above.

For more complex, non-linear migration paths, you can also pass in a version tree that maps the key-values to the source-destination versions:

    let dataStack = DataStack(migrationChain: [
        "MyAppModel": "MyAppModelV3",
        "MyAppModelV2": "MyAppModelV4",
        "MyAppModelV3": "MyAppModelV4"
    ])

This allows for different migration paths depending on the starting version. The example above resolves to the following paths:

- MyAppModel-MyAppModelV3-MyAppModelV4
- MyAppModelV2-MyAppModelV4
- MyAppModelV3-MyAppModelV4

Initializing with empty values (either `nil`, `[]`, or `[:]`) instructs the `DataStack` to disable progressive migrations and revert to the default migration behavior (i.e. use the .xcdatamodeld’s current version as the final version):

    let dataStack = DataStack(migrationChain: nil)

The `MigrationChain` is validated when passed to the `DataStack` and unless it is empty, will raise an assertion if any of the following conditions are met:

- a version appears twice in an array
- a version appears twice as a key in a dictionary literal
- a loop is found in any of the paths

> ⚠️Important: If a `MigrationChain` is specified, the .xcdatamodeld’s Current Version will be bypassed and the `MigrationChain`’s leafmost version will be the `DataStack`’s base model version.

### Forecasting migrations

Sometimes migrations are huge and you may want prior information so your app could display a loading screen, or to display a confirmation dialog to the user. For this, CoreStore provides a `requiredMigrationsForStorage(_:)` method you can use to inspect a persistent store before you actually call `addStorageAndWait(_:)` or `addStorage(_:completion:)`:

    do {
        let storage = SQLiteStorage(fileName: "MyStore.sqlite")
        let migrationTypes: [MigrationType] = try CoreStore.requiredMigrationsForStorage(storage)
        if migrationTypes.count > 1
            || (migrationTypes.filter { $0.isHeavyweightMigration }.count) > 0 {
            // ... will migrate more than once. Show special waiting screen
        }
        else if migrationTypes.count > 0 {
            // ... will migrate just once. Show simple activity indicator
        }
        else {
            // ... Do nothing
        }
        CoreStore.addStorage(storage, completion: { /* ... */ })
    }
    catch {
        // ... either inspection of the store failed, or if no mapping model was found/inferred
    }

`requiredMigrationsForStorage(_:)` returns an array of `MigrationType`s, where each item in the array may be either of the following values:

    case lightweight(sourceVersion: String, destinationVersion: String)
    case heavyweight(sourceVersion: String, destinationVersion: String)

Each `MigrationType` indicates the migration type for each step in the `MigrationChain`. Use these information as fit for your app.

### Custom migrations

Before CoreStore 4.0, the only way to implement custom migrations is to use Core Data’s standard method: declaring entity mappings through .xcmappingmodel files. Starting CoreStore 4.0, new ways to declare migration mappings have been added:

- `InferredSchemaMappingProvider`: The default mapping provider which tries to infer model migration between two `DynamicSchema` versions either by searching all .xcmappingmodel files from `Bundle.allBundles`, or by relying on lightweight migration if possible.
- `XcodeSchemaMappingProvider`: A mapping provider which loads entity mappings from .xcmappingmodel files in a specified `Bundle`.
- `CustomSchemaMappingProvider`: A mapping provider that infers mapping initially, but also accepts custom mappings for specified entities. This was added to support custom migrations with `CoreStoreObject`s as well, but may also be used with `NSManagedObject`s.

These mapping providers conform to `SchemaMappingProvider` and can be passed to `SQLiteStore`’s initializer:

    let dataStack = DataStack(migrationChain: ["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4"])
    _ = try dataStack.addStorage(
        SQLiteStore(
            fileName: "MyStore.sqlite",
            migrationMappingProviders: [
                XcodeSchemaMappingProvider(from: "V1", to: "V2", mappingModelBundle: Bundle.main),
                CustomSchemaMappingProvider(from: "V2", to: "V3", entityMappings: [.deleteEntity("Person") ])
            ]
        ),
        completion: { (result) -> Void in
            // ...
        }
    )

For version migrations present in the `DataStack`’s `MigrationChain` but not handled by any of the `SQLiteStore`’s `migrationMappingProviders` array, CoreStore will automatically try to use `InferredSchemaMappingProvider` as fallback. Finally if the `InferredSchemaMappingProvider` could not resolve any mapping, the migration will fail and the `DataStack.addStorage(...)` method will report the failure.

For `CustomSchemaMappingProvider`, more granular updates are supported through the dynamic objects `UnsafeSourceObject` and `UnsafeDestinationObject`. The example below allows the migration to conditionally ignore some objects:

    let person_v2_to_v3_mapping = CustomSchemaMappingProvider(
        from: "V2",
        to: "V3",
        entityMappings: [
            .transformEntity(
                sourceEntity: "Person",
                destinationEntity: "Person",
                transformer: { (sourceObject: UnsafeSourceObject, createDestinationObject: () -> UnsafeDestinationObject) in

                    if (sourceObject["isVeryOldAccount"] as! Bool?) == true {
                        return // this account is too old, don't migrate
                    }
                    // migrate the rest
                    let destinationObject = createDestinationObject()
                    destinationObject.enumerateAttributes { (attribute, sourceAttribute) in

                    if let sourceAttribute = sourceAttribute {
                        destinationObject[attribute] = sourceObject[sourceAttribute]
                    }
                }
            )
        ]
    )
    SQLiteStore(
        fileName: "MyStore.sqlite",
        migrationMappingProviders: [person_v2_to_v3_mapping]
    )

The `UnsafeSourceObject` is a read-only proxy for an object existing in the source model version. The `UnsafeDestinationObject` is a read-write object that is inserted (optionally) to the destination model version. Both classes’ properties are accessed through key-value-coding.

## Saving and processing transactions

To ensure deterministic state for objects in the read-only `NSManagedObjectContext`, CoreStore does not expose API’s for updating and saving directly from the main context (or any other context for that matter.) Instead, you spawn transactions from `DataStack` instances:

    let dataStack = self.dataStack
    dataStack.perform(
        asynchronous: { (transaction) -> Void in
            // make changes
        },
        completion: { (result) -> Void in
            // ...
        }
    )

or for the default stack, directly from `CoreStore`:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            // make changes
        },
        completion: { (result) -> Void in
            // ...
        }
    )

Transaction blocks automatically save changes once the block completes. To cancel and rollback a transaction, throw a `CoreStoreError.userCancelled` from inside the closure by calling `try transaction.cancel()`:

    dataStack.perform(
        asynchronous: { (transaction) -> Void in
            // ...
            if shouldCancel {
                try transaction.cancel()
            }
            // ...
        },
        completion: { (result) -> Void in
            if case .failure(.userCancelled) = result {
                // ... cancelled
            }
        }
    )

> ⚠️Important: Never use `try?` or `try!` on a `transaction.cancel()` call. Always use `try`. Using `try?` will swallow the cancellation and the transaction will proceed to save as normal. Using `try!` will crash the app as `transaction.cancel()` will always throw an error.

The examples above use `perform(asynchronous:...)`, but there are actually 3 types of transactions at your disposal: asynchronous, synchronous, and unsafe.

### Transaction types

#### Asynchronous transactions

are spawned from `perform(asynchronous:...)`. This method returns immediately and executes its closure from a background serial queue. The return value for the closure is declared as a generic type, so any value returned from the closure can be passed to the completion result:

    CoreStore.perform(
        asynchronous: { (transaction) -> Bool in
            // make changes
            return transaction.hasChanges
        },
        completion: { (result) -> Void in
            switch result {
            case .success(let hasChanges): print("success! Has changes? \(hasChanges)")
            case .failure(let error): print(error)
            }
        }
    )

The success and failure can also be declared as separate handlers:

    CoreStore.perform(
        asynchronous: { (transaction) -> Int in
            // make changes
            return transaction.delete(objects)
        },
        success: { (numberOfDeletedObjects: Int) -> Void in
            print("success! Deleted \(numberOfDeletedObjects) objects")
        },
        failure: { (error) -> Void in
            print(error)
        }
    )

> ⚠️Be careful when returning `NSManagedObject`s or `CoreStoreObject`s from the transaction closure. Those instances are for the transaction’s use only. See Passing objects safely.

Transactions created from `perform(asynchronous:...)` are instances of `AsynchronousDataTransaction`.

#### Synchronous transactions

are created from `perform(synchronous:...)`. While the syntax is similar to its asynchronous counterpart, `perform(synchronous:...)` waits for its transaction block to complete before returning:

    let hasChanges = CoreStore.perform(
        synchronous: { (transaction) -> Bool in
            // make changes
            return transaction.hasChanges
        }
    )

`transaction` above is a `SynchronousDataTransaction` instance.

Since `perform(synchronous:...)` technically blocks two queues (the caller’s queue and the transaction’s background queue), it is considered less safe as it’s more prone to deadlock. Take special care that the closure does not block on any other external queues.

By default, `perform(synchronous:...)` will wait for observers such as `ListMonitor`s to be notified before the method returns. This may cause deadlocks, especially if you are calling this from the main thread. To reduce this risk, you may try to set the `waitForAllObservers:` parameter to `false`. Doing so tells the `SynchronousDataTransaction` to block only until it completes saving. It will not wait for other context’s to receive those changes. This reduces deadlock risk but may have surprising side-effects:

    CoreStore.perform(
        synchronous: { (transaction) in
            let person = transaction.create(Into<Person>())
            person.name = "John"
        },
        waitForAllObservers: false
    )
    let newPerson = CoreStore.fetchOne(From<Person>.where(\.name == "John"))
    // newPerson may be nil!
    // The DataStack may have not yet received the update notification.

Due to this complicated nature of synchronous transactions, if your app has very heavy transaction throughput it is highly recommended to use asynchronous transactions instead.

#### Unsafe transactions

are special in that they do not enclose updates within a closure:

    let transaction = CoreStore.beginUnsafe()
    // make changes
    downloadJSONWithCompletion({ (json) -> Void in

        // make other changes
        transaction.commit()
    })
    downloadAnotherJSONWithCompletion({ (json) -> Void in

        // make some other changes
        transaction.commit()
    })

This allows for non-contiguous updates. Do note that this flexibility comes with a price: you are now responsible for managing concurrency for the transaction. As uncle Ben said, with great power comes great race conditions.

As the above example also shows, with unsafe transactions `commit()` can be called multiple times.

You’ve seen how to create transactions, but we have yet to see how to make creates, updates, and deletes. The 3 types of transactions above are all subclasses of `BaseDataTransaction`, which implements the methods shown below.

### Creating objects

The `create(...)` method accepts an `Into` clause which specifies the entity for the object you want to create:

    let person = transaction.create(Into<MyPersonEntity>())

While the syntax is straightforward, CoreStore does not just naively insert a new object. This single line does the following:

- Checks that the entity type exists in any of the transaction’s parent persistent store
- If the entity belongs to only one persistent store, a new object is inserted into that store and returned from `create(...)`
- If the entity does not belong to any store, an assertion failure will be raised. This is a programmer error and should never occur in production code.
- If the entity belongs to multiple stores, an assertion failure will be raised. This is also a programmer error and should never occur in production code. Normally, with Core Data you can insert an object in this state but saving the `NSManagedObjectContext` will always fail. CoreStore checks this for you at creation time when it makes sense (not during save).

If the entity exists in multiple configurations, you need to provide the configuration name for the destination persistent store:

    let person = transaction.create(Into<MyPersonEntity>("Config1"))

or if the persistent store is the auto-generated Default configuration, specify `nil`:

    let person = transaction.create(Into<MyPersonEntity>(nil))

Note that if you do explicitly specify the configuration name, CoreStore will only try to insert the created object to that particular store and will fail if that store is not found; it will not fall back to any other configuration that the entity belongs to.

### Updating objects

After creating an object from the transaction, you can simply update its properties as normal:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let person = transaction.create(Into<MyPersonEntity>())
            person.name = "John Smith"
            person.age = 30
        },
        completion: { _ in }
    )

To update an existing object, fetch the object’s instance from the transaction:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let person = try transaction.fetchOne(
                From<MyPersonEntity>()
                    .where(\.name == "Jane Smith")
            )
            person.age = person.age + 1
        },
        completion: { _ in }
    )

(For more about fetching, see Fetching and querying)

Do not update an instance that was not created/fetched from the transaction. If you have a reference to the object already, use the transaction’s `edit(...)` method to get an editable proxy instance for that object:

    let jane: MyPersonEntity = // ...

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            // WRONG: jane.age = jane.age + 1
            // RIGHT:
            let jane = transaction.edit(jane)! // using the same variable name protects us from misusing the non-transaction instance
            jane.age = jane.age + 1
        },
        completion: { _ in }
    )

This is also true when updating an object’s relationships. Make sure that the object assigned to the relationship is also created/fetched from the transaction:

    let jane: MyPersonEntity = // ...
    let john: MyPersonEntity = // ...

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            // WRONG: jane.friends = [john]
            // RIGHT:
            let jane = transaction.edit(jane)!
            let john = transaction.edit(john)!
            jane.friends = NSSet(array: [john])
        },
        completion: { _ in }
    )

### Deleting objects

Deleting an object is simpler because you can tell a transaction to delete an object directly without fetching an editable proxy (CoreStore does that for you):

    let john: MyPersonEntity = // ...

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            transaction.delete(john)
        },
        completion: { _ in }
    )

or several objects at once:

    let john: MyPersonEntity = // ...
    let jane: MyPersonEntity = // ...

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            try transaction.delete(john, jane)
            // try transaction.delete([john, jane]) is also allowed
        },
        completion: { _ in }
    )

If you do not have references yet to the objects to be deleted, transactions have a `deleteAll(...)` method you can pass a query to:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            try transaction.deleteAll(
                From<MyPersonEntity>()
                    .where(\.age > 30)
            )
        },
        completion: { _ in }
    )

### Passing objects safely

Always remember that the `DataStack` and individual transactions manage different `NSManagedObjectContext`s so you cannot just use objects between them. That’s why transactions have an `edit(...)` method:

    let jane: MyPersonEntity = // ...

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let jane = transaction.edit(jane)!
            jane.age = jane.age + 1
        },
        completion: { _ in }
    )

But `CoreStore`, `DataStack` and `BaseDataTransaction` have a very flexible `fetchExisting(...)` method that you can pass instances back and forth with:

    let jane: MyPersonEntity = // ...

    CoreStore.perform(
        asynchronous: { (transaction) -> MyPersonEntity in
            let jane = transaction.fetchExisting(jane)! // instance for transaction
            jane.age = jane.age + 1
            return jane
        },
        success: { (transactionJane) in
            let jane = CoreStore.fetchExisting(transactionJane)! // instance for DataStack
            print(jane.age)
        },
        failure: { (error) in
            // ...
        }
    )

`fetchExisting(...)` also works with multiple `NSManagedObject`s, `CoreStoreObject`s, or with `NSManagedObjectID`s:

    var peopleIDs: [NSManagedObjectID] = // ...

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let jane = try transaction.fetchOne(
                From<MyPersonEntity>()
                    .where(\.name == "Jane Smith")
            )
            jane.friends = NSSet(array: transaction.fetchExisting(peopleIDs)!)
            // ...
        },
        completion: { _ in }
    )

## Importing data

Some times, if not most of the time, the data that we save to Core Data comes from external sources such as web servers or external files. If you have for example a JSON dictionary, you may be extracting values as such:

    let json: [String: Any] = // ...
    person.name = json["name"] as? NSString
    person.age = json["age"] as? NSNumber
    // ...

If you have many attributes, you don’t want to keep repeating this mapping everytime you want to import data. CoreStore lets you write the data mapping code just once, and all you have to do is call `importObject(...)` or `importUniqueObject(...)` through `BaseDataTransaction` subclasses:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let json: [String: Any] = // ...
            try! transaction.importObject(
                Into<MyPersonEntity>(),
                source: json
            )
        },
        completion: { _ in }
    )

To support data import for an entity, implement either `ImportableObject` or `ImportableUniqueObject` on the `NSManagedObject` or `CoreStoreObject` subclass:

- `ImportableObject`: Use this protocol if the object have no inherent uniqueness and new objects should always be added when calling `importObject(...)`.
- `ImportableUniqueObject`: Use this protocol to specify a unique ID for an object that will be used to distinguish whether a new object should be created or if an existing object should be updated when calling `importUniqueObject(...)`.

Both protocols require implementers to specify an `ImportSource` which can be set to any type that the object can extract data from:

    typealias ImportSource = NSDictionary

    typealias ImportSource = [String: Any]

    typealias ImportSource = NSData

You can even use external types from popular 3rd-party JSON libraries, or just simple tuples or primitives.

#### `ImportableObject`

`ImportableObject` is a very simple protocol:

    public protocol ImportableObject: class {
        typealias ImportSource
        static func shouldInsert(from source: ImportSource, in transaction: BaseDataTransaction) -> Bool
        func didInsert(from source: ImportSource, in transaction: BaseDataTransaction) throws
    }

First, set `ImportSource` to the expected type of the data source:

    typealias ImportSource = [String: Any]

This lets us call `importObject(_:source:)` with any `[String: Any]` type as the argument to `source`:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let json: [String: Any] = // ...
            try! transaction.importObject(
                Into<MyPersonEntity>(),
                source: json
            )
            // ...
        },
        completion: { _ in }
    )

The actual extraction and assignment of values should be implemented in the `didInsert(from:in:)` method of the `ImportableObject` protocol:

    func didInsert(from source: ImportSource, in transaction: BaseDataTransaction) throws {
        self.name = source["name"] as? NSString
        self.age = source["age"] as? NSNumber
        // ...
    }

Transactions also let you import multiple objects at once using the `importObjects(_:sourceArray:)` method:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let jsonArray: [[String: Any]] = // ...
            try! transaction.importObjects(
                Into<MyPersonEntity>(),
                sourceArray: jsonArray // make sure this is of type Array<MyPersonEntity.ImportSource>
            )
            // ...
        },
        completion: { _ in }
    )

Doing so tells the transaction to iterate through the array of import sources and calls `shouldInsert(from:in:)` on the `ImportableObject` to determine which instances should be created. You can do validations and return `false` from `shouldInsert(from:in:)` if you want to skip importing from a source and continue on with the other sources in the array.

If on the other hand, your validation in one of the sources failed in such a manner that all other sources should also be rolled back and cancelled, you can `throw` from within `didInsert(from:in:)`:

    func didInsert(from source: ImportSource, in transaction: BaseDataTransaction) throws {
        self.name = source["name"] as? NSString
        self.age = source["age"] as? NSNumber
        // ...
        if self.name == nil {
            throw Errors.InvalidNameError
        }
    }

Doing so can let you abandon an invalid transaction immediately:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let jsonArray: [[String: Any]] = // ...

            try transaction.importObjects(
                Into<MyPersonEntity>(),
                sourceArray: jsonArray
            )
        },
        success: {
            // ...
        },
        failure: { (error) in
            switch error {
            case Errors.InvalidNameError: print("Invalid name")
            // ...
            }
        }
    )

#### `ImportableUniqueObject`

Typically, we don’t just keep creating objects every time we import data. Usually we also need to update already existing objects. Implementing the `ImportableUniqueObject` protocol lets you specify a unique ID that transactions can use to search existing objects before creating new ones:

    public protocol ImportableUniqueObject: ImportableObject {
        typealias ImportSource
        typealias UniqueIDType: ImportableAttributeType

        static var uniqueIDKeyPath: String { get }
        var uniqueIDValue: UniqueIDType { get set }

        static func shouldInsert(from source: ImportSource, in transaction: BaseDataTransaction) -> Bool
        static func shouldUpdate(from source: ImportSource, in transaction: BaseDataTransaction) -> Bool
        static func uniqueID(from source: ImportSource, in transaction: BaseDataTransaction) throws -> UniqueIDType?
        func didInsert(from source: ImportSource, in transaction: BaseDataTransaction) throws
        func update(from source: ImportSource, in transaction: BaseDataTransaction) throws
    }

Notice that it has the same insert methods as `ImportableObject`, with additional methods for updates and for specifying the unique ID:

    class var uniqueIDKeyPath: String {
        return #keyPath(MyPersonEntity.personID)
    }
    var uniqueIDValue: Int {
        get { return self.personID }
        set { self.personID = newValue }
    }
    class func uniqueID(from source: ImportSource, in transaction: BaseDataTransaction) throws -> Int? {
        return source["id"] as? Int
    }

For `ImportableUniqueObject`, the extraction and assignment of values should be implemented from the `update(from:in:)` method. The `didInsert(from:in:)` by default calls `update(from:in:)`, but you can separate the implementation for inserts and updates if needed.

You can then create/update an object by calling a transaction’s `importUniqueObject(...)` method:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let json: [String: Any] = // ...
            try! transaction.importUniqueObject(
                Into<MyPersonEntity>(),
                source: json
            )
            // ...
        },
        completion: { _ in }
    )

or multiple objects at once with the `importUniqueObjects(...)` method:

    CoreStore.perform(
        asynchronous: { (transaction) -> Void in
            let jsonArray: [[String: AnyObject]] = // ...
            try! transaction.importUniqueObjects(
                Into<MyPersonEntity>(),
                sourceArray: jsonArray
            )
            // ...
        },
        completion: { _ in }
    )

As with `ImportableObject`, you can control wether to skip importing an object by implementing `shouldInsert(from:in:)` and `shouldUpdate(from:in:)`, or to cancel all objects by `throw`ing an error from the `uniqueID(from:in:)`, `didInsert(from:in:)` or `update(from:in:)` methods.

## Fetching and Querying

Before we dive in, be aware that CoreStore distinguishes between fetching and querying:

- A fetch executes searches from a specific transaction or data stack. This means fetches can include pending objects (i.e. before a transaction calls on `commit()`.) Use fetches when:
  - results need to be `NSManagedObject` or `CoreStoreObject` instances
  - unsaved objects should be included in the search (though fetches can be configured to exclude unsaved ones)
- A query pulls data straight from the persistent store. This means faster searches when computing aggregates such as count, min, max, etc. Use queries when:
  - you need to compute aggregate functions (see below for a list of supported functions)
  - results can be raw values like `NSString`s, `NSNumber`s, `Int`s, `NSDate`s, an `NSDictionary` of key-values, or any type that conform to `QueryableAttributeType`. (See QueryableAttributeType.swift for a list of built-in types)
  - only values for specified attribute keys need to be included in the results
  - unsaved objects should be ignored

#### `From` clause

The search conditions for fetches and queries are specified using clauses. All fetches and queries require a `From` clause that indicates the target entity type:

    let people = try CoreStore.fetchAll(From<MyPersonEntity>())

`people` in the example above will be of type `[MyPersonEntity]`. The `From<MyPersonEntity>()` clause indicates a fetch to all persistent stores that `MyPersonEntity` belong to.

If the entity exists in multiple configurations and you need to only search from a particular configuration, indicate in the `From` clause the configuration name for the destination persistent store:

    let people = try CoreStore.fetchAll(From<MyPersonEntity>("Config1")) // ignore objects in persistent stores other than the "Config1" configuration

or if the persistent store is the auto-generated Default configuration, specify `nil`:

    let person = try CoreStore.fetchAll(From<MyPersonEntity>(nil))

Now we know how to use a `From` clause, let’s move on to fetching and querying.

### Fetching

There are currently 5 fetch methods you can call from `CoreStore`, from a `DataStack` instance, or from a `BaseDataTransaction` instance. All of the methods below accept the same parameters: a required `From` clause, and an optional series of `Where`, `OrderBy`, and/or `Tweak` clauses.

- `fetchAll(...)` - returns an array of all objects that match the criteria.
- `fetchOne(...)` - returns the first object that match the criteria.
- `fetchCount(...)` - returns the number of objects that match the criteria.
- `fetchObjectIDs(...)` - returns an array of `NSManagedObjectID`s for all objects that match the criteria.
- `fetchObjectID(...)` - returns the `NSManagedObjectID`s for the first objects that match the criteria.

Each method’s purpose is straightforward, but we need to understand how to set the clauses for the fetch.

#### `Where` clause

The `Where` clause is CoreStore’s `NSPredicate` wrapper. It specifies the search filter to use when fetching (or querying). It implements all initializers that `NSPredicate` does (except for `-predicateWithBlock:`, which Core Data does not support):

    var people = try CoreStore.fetchAll(
        From<MyPersonEntity>(),
        Where<MyPersonEntity>("%K > %d", "age", 30) // string format initializer
    )
    people = try CoreStore.fetchAll(
        From<MyPersonEntity>(),
        Where<MyPersonEntity>(true) // boolean initializer
    )

If you do have an existing `NSPredicate` instance already, you can pass that to `Where` as well:

    let predicate = NSPredicate(...)
    var people = CoreStore.fetchAll(
        From<MyPersonEntity>(),
        Where<MyPersonEntity>(predicate) // predicate initializer
    )

⭐️Starting CoreStore 5.0, `Where` clauses became more type-safe and are now generic types. To avoid verbose repetition of the generic object type, fetch methods now support Fetch Chain builders. We can also use Swift’s Smart KeyPaths as the `Where` clause expression:

    var people = try CoreStore.fetchAll(
        From<MyPersonEntity>()
            .where(\.age > 30) // Type-safe!
    )

`Where` clauses also implement the `&&`, `||`, and `!` logic operators, so you can provide logical conditions without writing too much `AND`, `OR`, and `NOT` strings:

    var people = try CoreStore.fetchAll(
        From<MyPersonEntity>()
            .where(\.age > 30 && \.gender == "M")
    )

If you do not provide a `Where` clause, all objects that belong to the specified `From` will be returned.

#### `OrderBy` clause

The `OrderBy` clause is CoreStore’s `NSSortDescriptor` wrapper. Use it to specify attribute keys in which to sort the fetch (or query) results with.

    var mostValuablePeople = try CoreStore.fetchAll(
        From<MyPersonEntity>(),
        OrderBy<MyPersonEntity>(.descending("rating"), .ascending("surname"))
    )

As seen above, `OrderBy` accepts a list of `SortKey` enumeration values, which can be either `.ascending` or `.descending`. ⭐️As with `Where` clauses, CoreStore 5.0 turned `OrderBy` clauses into generic types. To avoid verbose repetition of the generic object type, fetch methods now support Fetch Chain builders. We can also use Swift’s Smart KeyPaths as the `OrderBy` clause expression:

    var people = try CoreStore.fetchAll(
        From<MyPersonEntity>()
            .orderBy(.descending(\.rating), .ascending(\.surname)) // Type-safe!
    )

You can use the `+` and `+=` operator to append `OrderBy`s together. This is useful when sorting conditionally:

    var orderBy = OrderBy<MyPersonEntity>(.descending(\.rating))
    if sortFromYoungest {
        orderBy += OrderBy(.ascending(\.age))
    }
    var mostValuablePeople = try CoreStore.fetchAll(
        From<MyPersonEntity>(),
        orderBy
    )

#### `Tweak` clause

The `Tweak` clause lets you, uh, tweak the fetch (or query). `Tweak` exposes the `NSFetchRequest` in a closure where you can make changes to its properties:

    var people = try CoreStore.fetchAll(
        From<MyPersonEntity>(),
        Where<MyPersonEntity>("age > %d", 30),
        OrderBy<MyPersonEntity>(.ascending("surname")),
        Tweak { (fetchRequest) -> Void in
            fetchRequest.includesPendingChanges = false
            fetchRequest.returnsObjectsAsFaults = false
            fetchRequest.includesSubentities = false
        }
    )

`Tweak` also supports Fetch Chain builders:

    var people = try CoreStore.fetchAll(
        From<MyPersonEntity>(),
            .where(\.age > 30)
            .orderBy(.ascending(\.surname))
            .tweak {
                $0.includesPendingChanges = false
                $0.returnsObjectsAsFaults = false
                $0.includesSubentities = false
            }
    )

The clauses are evaluated the order they appear in the fetch/query, so you typically need to set `Tweak` as the last clause. `Tweak`‘s closure is executed only just before the fetch occurs, so make sure that any values captured by the closure is not prone to race conditions.

While `Tweak` lets you micro-configure the `NSFetchRequest`, note that CoreStore already preconfigured that `NSFetchRequest` to suitable defaults. Only use `Tweak` when you know what you are doing!

### Querying

One of the functionalities overlooked by other Core Data wrapper libraries is raw properties fetching. If you are familiar with `NSDictionaryResultType` and `-[NSFetchedRequest propertiesToFetch]`, you probably know how painful it is to setup a query for raw values and aggregate values. CoreStore makes this easy by exposing the 2 methods below:

- `queryValue(...)` - returns a single raw value for an attribute or for an aggregate value. If there are multiple results, `queryValue(...)` only returns the first item.
- `queryAttributes(...)` - returns an array of dictionaries containing attribute keys with their corresponding values.

Both methods above accept the same parameters: a required `From` clause, a required `Select<T>` clause, and an optional series of `Where`, `OrderBy`, `GroupBy`, and/or `Tweak` clauses.

Setting up the `From`, `Where`, `OrderBy`, and `Tweak` clauses is similar to how you would when fetching. For querying, you also need to know how to use the `Select<T>` and `GroupBy` clauses.

#### `Select<T>` clause

The `Select<T>` clause specifies the target attribute/aggregate key, as well as the expected return type:

    let johnsAge = try CoreStore.queryValue(
        From<MyPersonEntity>(),
        Select<Int>("age"),
        Where("name == %@", "John Smith")
    )

The example above queries the age property for the first object that matches the `Where` condition. `johnsAge` will be bound to type `Int?`, as indicated by the `Select<Int>` generic type. For `queryValue(...)`, types that conform to `QueryableAttributeType` are allowed as the return type (and therefore as the generic type for `Select<T>`).

For `queryAttributes(...)`, only `NSDictionary` is valid for `Select`, thus you are allowed to omit the generic type:

    let allAges = try CoreStore.queryAttributes(
        From<MyPersonEntity>(),
        Select("age")
    )

⭐️Starting CoreStore 5.0, query methods now support Query Chain builders. We can also use Swift’s Smart KeyPaths to use in the expressions:

    let johnsAge = try CoreStore.queryValue(
        From<MyPersonEntity>()
            .select(\.age) // binds the result to Int
            .where(\.name == "John Smith")
    )

If you only need a value for a particular attribute, you can just specify the key name (like we did with `Select<Int>("age")`), but several aggregate functions can also be used as parameter to `Select`:

- `.average(...)`
- `.count(...)`
- `.maximum(...)`
- `.minimum(...)`
- `.sum(...)`

    let oldestAge = try CoreStore.queryValue(
        From<MyPersonEntity>(),
        Select<Int>(.maximum("age"))
    )

For `queryAttributes(...)` which returns an array of dictionaries, you can specify multiple attributes/aggregates to `Select`:

    let personJSON = try CoreStore.queryAttributes(
        From<MyPersonEntity>(),
        Select("name", "age")
    )

`personJSON` will then have the value:

    [
        [
            "name": "John Smith",
            "age": 30
        ],
        [
            "name": "Jane Doe",
            "age": 22
        ]
    ]

You can also include an aggregate as well:

    let personJSON = try CoreStore.queryAttributes(
        From<MyPersonEntity>(),
        Select("name", .count("friends"))
    )

which returns:

    [
        [
            "name": "John Smith",
            "count(friends)": 42
        ],
        [
            "name": "Jane Doe",
            "count(friends)": 231
        ]
    ]

The `"count(friends)"` key name was automatically used by CoreStore, but you can specify your own key alias if you need:

    let personJSON = try CoreStore.queryAttributes(
        From<MyPersonEntity>(),
        Select("name", .count("friends", as: "friendsCount"))
    )

which now returns:

    [
        [
            "name": "John Smith",
            "friendsCount": 42
        ],
        [
            "name": "Jane Doe",
            "friendsCount": 231
        ]
    ]

#### `GroupBy` clause

The `GroupBy` clause lets you group results by a specified attribute/aggregate. This is useful only for `queryAttributes(...)` since `queryValue(...)` just returns the first value.

    let personJSON = try CoreStore.queryAttributes(
        From<MyPersonEntity>(),
        Select("age", .count("age", as: "count")),
        GroupBy("age")
    )

⭐️Starting CoreStore 5.0, `GroupBy` clauses are now also generic types and now support Query Chain builders. We can also use Swift’s Smart KeyPaths to use in the expressions:

    let personJSON = try CoreStore.queryAttributes(
        From<MyPersonEntity>()
            .select(.attribute(\.age), .count(\.age, as: "count"))
            .groupBy(\.age)
    )

this returns dictionaries that shows the count for each `"age"`:

    [
        [
            "age": 42,
            "count": 1
        ],
        [
            "age": 22,
            "count": 1
        ]
    ]

## Logging and error reporting

One unfortunate thing when using some third-party libraries is that they usually pollute the console with their own logging mechanisms. CoreStore provides its own default logging class, but you can plug-in your own favorite logger by implementing the `CoreStoreLogger` protocol.

    public protocol CoreStoreLogger {
        func log(level level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString)
        func log(error error: CoreStoreError, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString)
        func assert(@autoclosure condition: () -> Bool, @autoclosure message: () -> String, fileName: StaticString, lineNumber: Int, functionName: StaticString)
        func abort(message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString)
    }

Implement this protocol with your custom class then pass the instance to `CoreStore.logger`:

    CoreStore.logger = MyLogger()

Doing so channels all logging calls to your logger.

Note that to keep the call stack information intact, all calls to these methods are NOT thread-managed. Therefore you have to make sure that your logger is thread-safe or you may otherwise have to dispatch your logging implementation to a serial queue.

Take special care when implementing `CoreStoreLogger`’s `assert(...)` and `abort(...)` functions:

- `assert(...)`: The behavior between `DEBUG` and release builds, or `-O` and `-Onone`, are all left to the implementers’ responsibility. CoreStore calls `CoreStoreLogger.assert(...)` only for invalid but usually recoverable errors (for example, early validation failures that may cause an error thrown and handled somewhere else)
- `abort(...)`: This method is the last-chance for your app to synchronously log a fatal error within CoreStore. The app will be terminated right after this function is called (CoreStore calls `fatalError()` internally)

Starting CoreStore 2.0, all CoreStore types now have very useful (and pretty formatted!) `print(...)` outputs.

A couple of examples, `ListMonitor`:

`CoreStoreError.mappingModelNotFoundError`:

These are all implemented with `CustomDebugStringConvertible.debugDescription`, so they work with lldb’s `po` command as well.

## Observing changes and notifications

CoreStore provides type-safe wrappers for observing managed objects:

- `ObjectMonitor`: use to monitor changes to a single `NSManagedObject` or `CoreStoreObject` instance (instead of Key-Value Observing)
- `ListMonitor`: use to monitor changes to a list of `NSManagedObject` or `CoreStoreObject` instances (instead of `NSFetchedResultsController`)

### Observe a single property

To get notifications for single property changes in an object, there are two methods depending on the object’s base class.

- For `NSManagedObject` subclasses: Use the standard KVO method:

      let observer = person.observe(\.age, options: [.new]) { (person, change)
      print("Happy \(change.newValue)th birthday!")
      }

- For `CoreStoreObject` subclasses: Call the `observe(...)` method directly on the property. You’ll notice that the API itself is a bit similar to the KVO method:

      let observer = person.age.observe(options: [.new]) { (person, change)
      print("Happy \(change.newValue)th birthday!")
      }

For both methods, you will need to keep a reference to the returned `observer` for the duration of the observation.

### Observe a single object

To observe an object itself as a whole, implement the `ObjectObserver` protocol and specify the `EntityType`:

    class MyViewController: UIViewController, ObjectObserver {
        func objectMonitor(monitor: ObjectMonitor<MyPersonEntity>, willUpdateObject object: MyPersonEntity) {
            // ...
        }

        func objectMonitor(monitor: ObjectMonitor<MyPersonEntity>, didUpdateObject object: MyPersonEntity, changedPersistentKeys: Set<KeyPathString>) {
            // ...
        }

        func objectMonitor(monitor: ObjectMonitor<MyPersonEntity>, didDeleteObject object: MyPersonEntity) {
            // ...
        }
    }

We then need to keep a `ObjectMonitor` instance and register our `ObjectObserver` as an observer:

    let person: MyPersonEntity = // ...
    self.monitor = CoreStore.monitorObject(person)
    self.monitor.addObserver(self)

The controller will then notify our observer whenever the object’s attributes change. You can add multiple `ObjectObserver`s to a single `ObjectMonitor` without any problem. This means you can just share around the `ObjectMonitor` instance to different screens without problem.

You can get `ObjectMonitor`‘s object through its `object` property. If the object is deleted, the `object` property will become `nil` to prevent further access.

While `ObjectMonitor` exposes `removeObserver(...)` as well, it only stores `weak` references of the observers and will safely unregister deallocated observers.

### Observe a list of objects

To observe a list of objects, implement one of the `ListObserver` protocols and specify the `EntityType`:

    class MyViewController: UIViewController, ListObserver {
        func listMonitorDidChange(monitor: ListMonitor<MyPersonEntity>) {
            // ...
        }

        func listMonitorDidRefetch(monitor: ListMonitor<MyPersonEntity>) {
            // ...
        }
    }

Including `ListObserver`, there are 3 observer protocols you can implement depending on how detailed you need to handle a change notification:

- `ListObserver`: lets you handle these callback methods:

      func listMonitorWillChange(_ monitor: ListMonitor<MyPersonEntity>)
      func listMonitorDidChange(_ monitor: ListMonitor<MyPersonEntity>)
      func listMonitorWillRefetch(_ monitor: ListMonitor<MyPersonEntity>)
      func listMonitorDidRefetch(_ monitor: ListMonitor<MyPersonEntity>)

  `listMonitorDidChange(_:)` and `listMonitorDidRefetch(_:)` implementations are both required. `listMonitorDidChange(_:)` is called whenever the `ListMonitor`’s count, order, or filtered objects change. `listMonitorDidRefetch(_:)` is called when the `ListMonitor.refetch()` was executed or if the internal persistent store was changed.

- `ListObjectObserver`: in addition to `ListObserver` methods, also lets you handle object inserts, updates, and deletes:

      func listMonitor(_ monitor: ListMonitor<MyPersonEntity>, didInsertObject object: MyPersonEntity, toIndexPath indexPath: NSIndexPath)
      func listMonitor(_ monitor: ListMonitor<MyPersonEntity>, didDeleteObject object: MyPersonEntity, fromIndexPath indexPath: NSIndexPath)
      func listMonitor(_ monitor: ListMonitor<MyPersonEntity>, didUpdateObject object: MyPersonEntity, atIndexPath indexPath: NSIndexPath)
      func listMonitor(_ monitor: ListMonitor<MyPersonEntity>, didMoveObject object: MyPersonEntity, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath)

- `ListSectionObserver`: in addition to `ListObjectObserver` methods, also lets you handle section inserts and deletes:

      func listMonitor(_ monitor: ListMonitor<MyPersonEntity>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)
      func listMonitor(_ monitor: ListMonitor<MyPersonEntity>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int)

We then need to create a `ListMonitor` instance and register our `ListObserver` as an observer:

    self.monitor = CoreStore.monitorList(
        From<MyPersonEntity>()
            .where(\.age > 30)
            .orderBy(.ascending(\.name))
            .tweak { $0.fetchBatchSize = 20 }
    )
    self.monitor.addObserver(self)

Similar to `ObjectMonitor`, a `ListMonitor` can also have multiple `ListObserver`s registered to a single `ListMonitor`.

If you have noticed, the `monitorList(...)` method accepts `Where`, `OrderBy`, and `Tweak` clauses exactly like a fetch. As the list maintained by `ListMonitor` needs to have a deterministic order, at least the `From` and `OrderBy` clauses are required.

A `ListMonitor` created from `monitorList(...)` will maintain a single-section list. You can therefore access its contents with just an index:

    let firstPerson = self.monitor[0]

If the list needs to be grouped into sections, create the `ListMonitor` instance with the `monitorSectionedList(...)` method and a `SectionBy` clause:

    self.monitor = CoreStore.monitorSectionedList(
        From<MyPersonEntity>()
            .sectionBy(\.age)
            .where(\.gender == "M")
            .orderBy(.ascending(\.age), .ascending(\.name))
            .tweak { $0.fetchBatchSize = 20 }
    )

A list controller created this way will group the objects by the attribute key indicated by the `SectionBy` clause. One more thing to remember is that the `OrderBy` clause should sort the list in such a way that the `SectionBy` attribute would be sorted together (a requirement shared by `NSFetchedResultsController`.)

The `SectionBy` clause can also be passed a closure to transform the section name into a displayable string:

    self.monitor = CoreStore.monitorSectionedList(
        From<MyPersonEntity>()
            .sectionBy(\.age) { (sectionName) -> String? in
                "\(sectionName) years old"
            }
            .orderBy(.ascending(\.age), .ascending(\.name))
    )

This is useful when implementing a `UITableViewDelegate`’s section header:

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        let sectionInfo = self.monitor.sectionInfoAtIndex(section)
        return sectionInfo.name
    }

To access the objects of a sectioned list, use an `NSIndexPath` or a tuple:

    let indexPath = NSIndexPath(forRow: 2, inSection: 1)
    let person1 = self.monitor[indexPath]
    let person2 = self.monitor[1, 2]
    // person1 and person2 are the same object

## Objective-C support

> ⚠️Objective-C support is planned to be deprecated in a future CoreStore version.

All CoreStore types are still written in pure Swift, but most core types have Objective-C bridging classes that are visible to Objective-C code. To show a couple of usage examples:

Swift | Objective-C
--- | ---
    try CoreStore.addStorageAndWait(SQLiteStore.self)
    NSError *error
    [CSCoreStore addSQLiteStorageAndWait:[CSSQLiteStore new] error:&error]
    CoreStore.perform(
        asynchronous: { (transaction) in
            // ...
        },
        completion: { (result) in
            switch result {
            case .success: print("Done")
            case .failure(let error): print(error)
            }
        }
    )
    [CSCoreStore beginAsynchronous:^(CSAsynchronousDataTransaction *transaction) {
        // ...
        [transaction
         commitWithSuccess:^{
            NSLog(@"Done");
         }
         failure: ^(CSError *error) {
            NSLog(@"error: %@", result.error);
         }];
    }];

All of these `CS`-prefixed bridging classes have very similar usage to the existing CoreStore APIs, and ironically none of them are written in Objective-C. This is very different to the common approach where apps and libraries write Objective-C APIs just to support both Objective-C and Swift. The advantage with CoreStore’s approach is that your Swift codebase can already use the purely-Swift API without further changes in the future, but your hybrid codebase can still bridge instances back and forth from Objective-C to Swift.

For example, you may have a new, modern Swift class that holds a `ListMonitor`:

    class MyViewController: UIViewController {
        let monitor = CoreStore.monitorList(From<MyEntity>(), ...)
        // ...
    }

Now let’s say you have a legacy Objective-C class that previously uses `NSFetchedResultsController`. It’s easy to switch from `NSFetchedResultsController` to `CSListMonitor`, but converting the rest of this huge class is impractical. You end up with

    @interface MYOldViewController: UIViewController
    @property (nonatomic, readonly, strong) CSListMonitor* monitor;
    - (instancetype)initWithMonitor:(CSListMonitor *)monitor;
    @end

When you need to instantiate this class from Swift, you just call `bridgeToObjectiveC`:

    class MyViewController: UIViewController {
        let monitor = CoreStore.monitorList(From<MyEntity>(), ...)
        func showOldController() {
            let controller = MYOldViewController(monitor: self.monitor.bridgeToObjectiveC)
            self.presentViewController(controller, animated: true, completion: nil)
        }
    }

Note that the `CSListMonitor` holds the exact same `ListMonitor` instance, which means that no copies and no extra fetching occur.

### Objective-C syntax sugars

Objective-C tends to be verbose, so some method calls are long and unreadable. For example, fetching looks like this:

    NSArray<MYPerson *> *objects =
    [CSCoreStore
     fetchAllFrom:[[CSFrom alloc] initWithEntityClass:[MYPerson class]]
     fetchClauses:@[[[CSWhere alloc] initWithFormat:@"%K == %@", @"isHidden", @NO],
                    [[CSOrderBy alloc] initWithSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES],
                                                                 [NSSortDescriptor sortDescriptorWithKey:@"firstName" ascending:YES]]]]];

Although it works, it looks terrible. For this, CoreStore provides CoreStoreBridge.h where these Objective-C calls are wrapped in readable, convenient macros and global functions. The call above becomes

    NSArray<MYPerson *> *objects =
    [CSCoreStore
     fetchAllFrom:CSFromClass([MYPerson class])
     fetchClauses:@[CSWhereFormat(@"%K == %@", @"isHidden", @NO),
                    CSOrderByKeys(CSSortAscending(@"lastName"),
                                  CSSortAscending(@"firstName"), nil)]];

That’s much shorter now. But we can still do better. Notice that we have strings being used as key paths. The `CSKeyPath(...)` macro gives us compile-time checking so keys that don’t exist in a class will generate errors. Our key-safe code now looks like this:

    NSArray<MYPerson *> *objects =
    [CSCoreStore
     fetchAllFrom:CSFromClass([MYPerson class])
     fetchClauses:@[CSWhereFormat(@"%K == %@", CSKeyPath(MYPerson, isHidden), @NO),
                    CSOrderByKeys(CSSortAscending(CSKeyPath(MYPerson, lastName)),
                                  CSSortAscending(CSKeyPath(MYPerson, firstName)), nil)]];

To use these syntax sugars, include CoreStoreBridge.h in your Objective-C source files.

## Type-safe `CoreStoreObject`s

Starting CoreStore 4.0, we can now create persisted objects without depending on .xcdatamodeld Core Data files. The new `CoreStoreObject` subclass replaces `NSManagedObject`, and specially-typed properties declared on these classes will be synthesized as Core Data attributes.

    class Animal: CoreStoreObject {
        let species = Value.Required<String>("species", initial: "")
    }

    class Dog: Animal {
        let nickname = Value.Optional<String>("nickname")
        let master = Relationship.ToOne<Person>("master")
    }

    class Person: CoreStoreObject {
        let name = Value.Required<String>("name", initial: "")
        let pets = Relationship.ToManyUnordered<Dog>("pets", inverse: { $0.master })
    }

The property names to be saved to Core Data is specified as the `keyPath` argument. This lets us refactor our Swift code without affecting the underlying database. For example:

    class Person: CoreStoreObject {
        private let _name = Value.Required<String>("name", initial: "")
        // note property name is independent of the storage key name
    }

Here we added an underscore to the property name and made it `private`, but the underlying key-path `"name"` was unchanged so our model will not trigger a data migration.

> ⚠️Important: As a rule, CoreStore can only process stored properties. Computed, `static`, `weak`, or `lazy` properties will be ignored and will not be added to the store. It is also strictly advised use `let` instead of `var` to declare these properties, as any changes to the property value will break the schema.

Also note how `Relationship`s are linked statically with the `inverse:` argument. All relationships are required to have an inverse relationship. Unfortunately, due to Swift compiler limitation we can only declare the `inverse:` on one end of the relationship-pair.

To tell the `DataStack` about these types, add all `CoreStoreObject`s’ entities to a `CoreStoreSchema`:

    CoreStore.defaultStack = DataStack(
        CoreStoreSchema(
            modelVersion: "V1",
            entities: [
                Entity<Animal>("Animal", isAbstract: true),
                Entity<Dog>("Dog"),
                Entity<Person>("Person")
            ]
        )
    )
    CoreStore.addStorage(/* ... */)

And that’s all CoreStore needs to build the model; we don’t need .xcdatamodeld files anymore.

These special properties’ values can be accessed or mutated using `.value`:

    CoreStore.perform(
        asynchronous: { (transaction) in
            let dog: Dog = transaction.fetchOne(From<Dog>())!
            // ...
            let nickname = dog.nickname.value // String?
            let species = dog.species.value // String
            let age = dog.age.value // Int
            // ...
            dog.age.value = age + 1
        },
        completion: { /* ... */ }
    )

In addition, `Value` and `Relationship` properties can be used to create type-safe key-paths

    let keyPath: String = Dog.keyPath { $0.nickname }

as well as `Where` and `OrderBy` clauses

    let puppies = try CoreStore.fetchAll(
        From<Dog>()
            .where(\.age < 1)
            .orderBy(.ascending(\.age))
    )

All CoreStore APIs that are usable with `NSManagedObject`s are also available for `CoreStoreObject`s. These include `ListMonitor`s, `ImportableObject`s, fetching, etc.

### `VersionLock`s

While it is convenient to be able to declare entities only in code, it is worrying that we might accidentally change the `CoreStoreObject`‘s properties and break our users’ model version history. For this, the `CoreStoreSchema` allows us to lock our properties to a particular configuration. Any changes to that `VersionLock` will raise an assertion failure during the `CoreStoreSchema` initialization, so you can then look for the commit which changed the `VersionLock` hash.

To use `VersionLock`s, create the `CoreStoreSchema`, run the app, and look for a similar log message that is automatically printed to the console:

Copy this dictionary value and use it as the `versionLock:` argument of the `CoreStoreSchema` initializer:

    CoreStoreSchema(
        modelVersion: "V1",
        entities: [
            Entity<Animal>("Animal", isAbstract: true),
            Entity<Dog>("Dog"),
            Entity<Person>("Person"),
        ],
        versionLock: [
            "Animal": [0x1b59d511019695cf, 0xdeb97e86c5eff179, 0x1cfd80745646cb3, 0x4ff99416175b5b9a],
            "Dog": [0xe3f0afeb109b283a, 0x29998d292938eb61, 0x6aab788333cfc2a3, 0x492ff1d295910ea7],
            "Person": [0x66d8bbfd8b21561f, 0xcecec69ecae3570f, 0xc4b73d71256214ef, 0x89b99bfe3e013e8b]
        ]
    )

You can also get this hash after the `DataStack` has been fully set up by printing to the console:

    print(CoreStore.defaultStack.modelSchema.printCoreStoreSchema())

Once the version lock is set, any changes in the properties or to the model will trigger an assertion failure similar to this:

# Installation

Requires

- iOS 10 SDK and above
- Swift 5.0 (Xcode 10+)
- For previous Swift versions: Swift 3.2, Swift 4.2

- Dependencies:
  - None
- Other notes:
  - The `com.apple.CoreData.ConcurrencyDebug` debug argument should be turned off for the app. CoreStore already guarantees safety for you by making the main context read-only, and by only executing transactions serially.

### Install with CocoaPods

In your `Podfile`, add

    pod 'CoreStore', '~> 6.3'

and run

    pod update

This installs CoreStore as a framework. Declare `import CoreStore` in your swift file to use the library.

### Install with Carthage

In your `Cartfile`, add

    github "JohnEstropia/CoreStore" >= 6.3.0

and run

    carthage update

This installs CoreStore as a framework. Declare `import CoreStore` in your swift file to use the library.

#### Install with Swift Package Manager:

    dependencies: [
        .package(url: "https://github.com/JohnEstropia/CoreStore.git", from: "6.3.0"))
    ]

Declare `import CoreStore` in your swift file to use the library.

### Install as Git Submodule

    git submodule add https://github.com/JohnEstropia/CoreStore.git <destination directory>

Drag and drop CoreStore.xcodeproj to your project.

### Objective-C support

To use the Objective-C syntax sugars, import CoreStoreBridge.h in your .m source files.

# Changesets

For the full Changelog, refer to the Releases page.

# Contact

You can reach me on Twitter @JohnEstropia

or join our Slack team at swift-corestore.slack.com

日本語の対応も可能なので是非！

# Who uses CoreStore?

I’d love to hear about apps using CoreStore. Send me a message and I’ll welcome any feedback!

# License

CoreStore is released under an MIT license. See the LICENSE file for more information