[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-6909":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":17,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":22,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":30,"readmeContent":31,"aiSummary":32,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":33,"discoverSource":34},6909,"CoreStore","JohnEstropia\u002FCoreStore","JohnEstropia","Unleashing the real power of Core Data with the elegance and safety of Swift","",null,"Swift",4056,259,67,89,0,2,29.24,"MIT License",false,"develop",true,[24,25,26,27,28,29],"carthage","cocoapods","core-data","coredata","swift","swift-package-manager","2026-06-12 02:01:31","\u003Cp align=\"center\">\n\u003Cimg alt=\"CoreStore\" src=\"https:\u002F\u002Fgithub.com\u002FJohnEstropia\u002FCoreStore\u002Fraw\u002Fdevelop\u002FCoreStore.png\" width=614 \u002F>\n\u003Cbr \u002F>\n\u003Cbr \u002F>\nUnleashing the real power of Core Data with the elegance and safety of Swift\n\u003Cbr \u002F>\n\u003Cbr \u002F>\n\u003Ca href=\"https:\u002F\u002Fapp.bitrise.io\u002Fapp\u002Fe736852157296019#\u002Fbuilds\">\u003Cimg alt=\"Build Status\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbitrise\u002Fe736852157296019\u002Fmaster.svg?label=build&token=vhgAmaiF3tWZoQyFLkKM7g&logo=bitrise\" \u002F>\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FJohnEstropia\u002FCoreStore\u002Fcommits\">\u003Cimg alt=\"Last Commit\" src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flast-commit\u002Fjohnestropia\u002Fcorestore.svg?style=flat\" \u002F>\u003C\u002Fa>\n\u003Ca href=\"http:\u002F\u002Fcocoadocs.org\u002Fdocsets\u002FCoreStore\">\u003Cimg alt=\"Platform\" src=\"https:\u002F\u002Fimg.shields.io\u002Fcocoapods\u002Fp\u002FCoreStore.svg?style=flat\" \u002F>\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Fraw.githubusercontent.com\u002FJohnEstropia\u002FCoreStore\u002Fmaster\u002FLICENSE\">\u003Cimg alt=\"License\" src=\"https:\u002F\u002Fimg.shields.io\u002Fcocoapods\u002Fl\u002FCoreStore.svg?style=flat\" \u002F>\u003C\u002Fa>\n\u003Cbr \u002F>\u003Cbr \u002F>Dependency managers\u003Cbr \u002F>\n\u003Ca href=\"https:\u002F\u002Fcocoapods.org\u002Fpods\u002FCoreStore\">\u003Cimg alt=\"Cocoapods compatible\" src=\"https:\u002F\u002Fimg.shields.io\u002Fcocoapods\u002Fv\u002FCoreStore.svg?style=flat&label=Cocoapods\" \u002F>\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FCarthage\u002FCarthage\">\u003Cimg alt=\"Carthage compatible\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FCarthage-compatible-16a085.svg?style=flat\" \u002F>\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Fswift.org\u002Fsource-compatibility\u002F#current-list-of-projects\">\u003Cimg alt=\"Swift Package Manager compatible\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSwift_Package_Manager-compatible-orange.svg?style=flat\" \u002F>\u003C\u002Fa>\n\u003Cbr \u002F>\u003Cbr \u002F>Contact\u003Cbr \u002F>\n\u003Ca href=\"http:\u002F\u002Fswift-corestore-slack.herokuapp.com\u002F\">\u003Cimg alt=\"Join us on Slack!\" src=\"http:\u002F\u002Fswift-corestore-slack.herokuapp.com\u002Fbadge.svg?logo=slack\" \u002F>\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Ftwitter.com\u002FJohnEstropia\">\u003Cimg alt=\"Reach me on Twitter!\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Ftwitter-%40JohnEstropia-3498db.svg?logo=twitter\" \u002F>\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fsponsors\u002FJohnEstropia\">\u003Cimg alt=\"Sponsor\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002F%E2%9D%A4-Sponsor-ff69bf\">\u003C\u002Fa>\n\u003Cbr \u002F>\n\u003C\u002Fp>\n\n* **Swift 5.9:** iOS 16.0+ \u002F macOS 13.0+ \u002F watchOS 9.0+ \u002F tvOS 16.0+\n* Previously supported Swift versions: [Swift 5.4](https:\u002F\u002Fgithub.com\u002FJohnEstropia\u002FCoreStore\u002Ftree\u002F8.0.1), [Swift 5.3](https:\u002F\u002Fgithub.com\u002FJohnEstropia\u002FCoreStore\u002Ftree\u002F7.3.1), [Swift 5.7](https:\u002F\u002Fgithub.com\u002FJohnEstropia\u002FCoreStore\u002Ftree\u002F9.1.0)\n\nUpgrading from previous CoreStore versions? Check out the [🆕 features](#features) and make sure to read the [Change logs](https:\u002F\u002Fgithub.com\u002FJohnEstropia\u002FCoreStore\u002Freleases).\n\nCoreStore is part of the [Swift Source Compatibility projects](https:\u002F\u002Fswift.org\u002Fsource-compatibility\u002F#current-list-of-projects).\n\n## Contents\n\n- [TL;DR (a.k.a. sample codes)](#tldr-aka-sample-codes)\n- [Why use CoreStore?](#why-use-corestore)\n- [Architecture](#architecture)\n- CoreStore Tutorials (All of these have demos in the **Demo** app project!)\n    - [Setting up](#setting-up)\n        - [In-memory store](#in-memory-store)\n        - [Local store](#local-store)\n    - [Migrations](#migrations)\n        - [Declaring model versions](#declaring-model-versions)\n        - [Starting migrations](#starting-migrations)\n        - [Progressive migrations](#progressive-migrations)\n        - [Forecasting migrations](#forecasting-migrations)\n        - [Custom migrations](#custom-migrations)\n    - [Saving and processing transactions](#saving-and-processing-transactions)\n        - [Transaction types](#transaction-types)\n            - [Asynchronous transactions](#asynchronous-transactions)\n            - [Synchronous transactions](#synchronous-transactions)\n            - [Unsafe transactions](#unsafe-transactions)\n        - [Creating objects](#creating-objects)\n        - [Updating objects](#updating-objects)\n        - [Deleting objects](#deleting-objects)\n        - [Passing objects safely](#passing-objects-safely)\n    - [Importing data](#importing-data)\n    - [Fetching and querying](#fetching-and-querying)\n        - [`From` clause](#from-clause)\n        - [Fetching](#fetching)\n            - [`Where` clause](#where-clause)\n            - [`OrderBy` clause](#orderby-clause)\n            - [`Tweak` clause](#tweak-clause)\n        - [Querying](#querying)\n            - [`Select\u003CT>` clause](#selectt-clause)\n            - [`GroupBy` clause](#groupby-clause)\n    - [Logging and error reporting](#logging-and-error-reporting)\n    - [Observing changes and notifications](#observing-changes-and-notifications)\n        - [Observe a single property](#observe-a-single-property)\n        - [Observe a single object's updates](#observe-a-single-objects-updates)\n        - [Observe a single object's per-property updates](#observe-a-single-objects-per-property-updates)\n        - [Observe a diffable list](#observe-a-diffable-list)\n        - [Observe detailed list changes](#observe-detailed-list-changes)\n    - [Type-safe `CoreStoreObject`s](#type-safe-corestoreobjects)\n        - [New `@Field` Property Wrapper syntax](#new-field-property-wrapper-syntax)\n            - [`@Field.Stored` ](#fieldstored)\n            - [`@Field.Virtual` ](#fieldvirtual)\n            - [`@Field.Coded` ](#fieldcoded)\n            - [`@Field.Relationship` ](#fieldrelationship)\n            - [`@Field` usage notes](#field-usage-notes)\n        - [`VersionLock`s](#versionlocks)\n    - [Reactive Programming](#reactive-programming)\n        - [RxSwift](#rxswift)\n        - 🆕[Combine](#combine)\n            - 🆕[`DataStack.reactive`](#datastackreactive)\n            - 🆕[`ListPublisher.reactive`](#listpublisherreactive)\n            - 🆕[`ObjectPublisher.reactive`](#objectpublisherreactive)\n    - 🆕[SwiftUI Utilities](#swiftui-utilities)\n        - 🆕[SwiftUI Views`](#swiftui-views)\n            - 🆕[`ListReader`](#listreader)\n            - 🆕[`ObjectReader`](#objectreader)\n        - 🆕[SwiftUI Property Wrappers](#swiftui-property-wrappers)\n            - 🆕[`ListState`](#liststate)\n            - 🆕[`ObjectState`](#objectstate)\n        - 🆕[SwiftUI Extensions](#swiftui-extensions)\n            - 🆕[`ForEach`](#foreach)\n- [Roadmap](#roadmap)\n- [Installation](#installation)\n- [Changesets](#changesets)\n- [Contact](#contact)\n- [Who uses CoreStore?](#who-uses-corestore)\n- [License](#license)\n\n\n\n## TL;DR (a.k.a. sample codes)\n\nPure-Swift models:\n```swift\nclass Person: CoreStoreObject {\n    @Field.Stored(\"name\")\n    var name: String = \"\"\n    \n    @Field.Relationship(\"pets\", inverse: \\Dog.$master)\n    var pets: Set\u003CDog>\n}\n```\n(Classic `NSManagedObject`s also supported)\n\nSetting-up with progressive migration support:\n```swift\ndataStack = DataStack(\n    xcodeModelName: \"MyStore\",\n    migrationChain: [\"MyStore\", \"MyStoreV2\", \"MyStoreV3\"]\n)\n```\n\nAdding a store:\n```swift\ndataStack.addStorage(\n    SQLiteStore(fileName: \"MyStore.sqlite\"),\n    completion: { (result) -> Void in\n        \u002F\u002F ...\n    }\n)\n```\n\nStarting transactions:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let person = transaction.create(Into\u003CPerson>())\n        person.name = \"John Smith\"\n        person.age = 42\n    },\n    completion: { (result) -> Void in\n        switch result {\n        case .success: print(\"success!\")\n        case .failure(let error): print(error)\n        }\n    }\n)\n```\n\nFetching objects (simple):\n```swift\nlet people = try dataStack.fetchAll(From\u003CPerson>())\n```\n\nFetching objects (complex):\n```swift\nlet people = try dataStack.fetchAll(\n    From\u003CPerson>()\n        .where(\\.age > 30),\n        .orderBy(.ascending(\\.name), .descending(.\\age)),\n        .tweak({ $0.includesPendingChanges = false })\n)\n```\n\nQuerying values:\n```swift\nlet maxAge = try dataStack.queryValue(\n    From\u003CPerson>()\n        .select(Int.self, .maximum(\\.age))\n)\n```\n\nBut really, there's a reason I wrote this huge *README*. Read up on the details!\n\nCheck out the **Demo** app project for sample codes as well!\n\n\n## Why use CoreStore?\n\nCoreStore 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.\n\n### Features\n\n- **🆕[SwiftUI](#swiftui-utilities) and [Combine](#combine) API utilities.** `ListPublisher`s and `ObjectPublisher`s now have their `@ListState` and `@ObjectState` SwiftUI property wrappers. Combine `Publisher` s are also available through the `ListPublisher.reactive`, `ObjectPublisher.reactive`, and `DataStack.reactive` namespaces.\n- **Backwards-portable [DiffableDataSources implementation](#observe-a-diffable-list)!** `UITableViews` and `UICollectionViews` now have a new ally: `ListPublisher`s provide diffable snapshots that make reloading animations very easy and very safe. Say goodbye to `UITableViews` and `UICollectionViews` reload errors!\n- **💎Tight design around Swift’s code elegance and type safety.** CoreStore fully utilizes Swift's community-driven language features.\n- **🚦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](#saving-and-processing-transactions))*\n- **🔍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](#fetching-and-querying))*\n- **🔭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, list and object observable types all support multiple observers. This means you can have multiple view controllers efficiently share a single resource! *(See [Observing changes and notifications](#observing-changes-and-notifications))*\n- **📥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](#importing-data))*\n- **🗑Say goodbye to *.xcdatamodeld* files!** While CoreStore supports `NSManagedObject`s, it offers `CoreStoreObject` whose subclasses can declare type-safe properties all in Swift code without the 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](#type-safe-corestoreobjects))*\n- **🔗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](#migrations))*\n- **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](#migrations))*\n- **📝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](#logging-and-error-reporting))*\n- **⛓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](#setting-up))*\n- **🎯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 independent names for the entities and their class names.\n- **📙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.\n- **ℹ️Informative (and pretty) logs.** All CoreStore and Core Data-related types now have very informative and pretty print outputs! *(See [Logging and error reporting](#logging-and-error-reporting))*\n- **🛡More extensive Unit Tests.** Extending CoreStore is safe without having to worry about breaking old behavior.\n\n*Have ideas that may benefit other Core Data users? [Feature Request](https:\u002F\u002Fgithub.com\u002FJohnEstropia\u002FCoreStore\u002Fissues)s are welcome!*\n\n\n\n## Architecture\nFor 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.\n\nIf you are already familiar with the inner workings of CoreData, here is a mapping of `CoreStore` abstractions:\n\n| *Core Data* | *CoreStore* |\n| --- | --- |\n| `NSPersistentContainer`\u003Cbr \u002F>(.xcdatamodeld file) | `DataStack` |\n| `NSPersistentStoreDescription`\u003Cbr \u002F>(\"Configuration\"s in the .xcdatamodeld file) | `StorageInterface` implementations\u003Cbr \u002F>(`InMemoryStore`, `SQLiteStore`) |\n| `NSManagedObjectContext` | `BaseDataTransaction` subclasses\u003Cbr \u002F>(`SynchronousDataTransaction`, `AsynchronousDataTransaction`, `UnsafeDataTransaction`) |\n\nA lot of Core Data wrapper libraries set up their `NSManagedObjectContext`s this way:\n\n\u003Cimg src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F3029684\u002F16707160\u002F984ef25c-4600-11e6-869f-8db7d2c63668.png\" alt=\"nested contexts\" height=380 \u002F>\n\nNesting saves from child context to the root context ensures maximum data integrity between contexts without blocking the main queue. But \u003Ca href=\"http:\u002F\u002Ffloriankugler.com\u002F2013\u002F04\u002F29\u002Fconcurrent-core-data-stack-performance-shootout\u002F\">in reality\u003C\u002Fa>, 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 (or \"viewContext\"), and only allows changes to be made within *transactions* on the child context:\n\n\u003Cimg src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F3029684\u002F16707161\u002F9adeb962-4600-11e6-8bc8-4ec85764dba4.png\" alt=\"nested contexts and merge hybrid\" height=292 \u002F>\n\nThis allows for a butter-smooth main thread, while still taking advantage of safe nested contexts.\n\n\n\n## Setting up\nThe simplest way to initialize CoreStore is to add a default store to the default stack:\n```swift\ntry CoreStoreDefaults.dataStack.addStorageAndWait()\n```\nThis one-liner does the following:\n- Triggers the lazy-initialization of `CoreStoreDefaults.dataStack` with a default `DataStack`\n- Sets up the stack's `NSPersistentStoreCoordinator`, the root saving `NSManagedObjectContext`, and the read-only main `NSManagedObjectContext`\n- Adds an `SQLiteStore` in the *\"Application Support\u002F\u003Cbundle id>\"* directory (or the *\"Caches\u002F\u003Cbundle id>\"* directory on tvOS) with the file name *\"[App bundle name].sqlite\"*\n- Creates and returns the `NSPersistentStore` instance on success, or an `NSError` on failure\n\nFor most cases, this configuration is enough as it is. But for more hardcore settings, refer to this extensive example:\n```swift\nlet dataStack = DataStack(\n    xcodeModelName: \"MyModel\", \u002F\u002F loads from the \"MyModel.xcdatamodeld\" file\n    migrationChain: [\"MyStore\", \"MyStoreV2\", \"MyStoreV3\"] \u002F\u002F model versions for progressive migrations\n)\nlet migrationProgress = dataStack.addStorage(\n    SQLiteStore(\n        fileURL: sqliteFileURL, \u002F\u002F set the target file URL for the sqlite file\n        configuration: \"Config2\", \u002F\u002F use entities from the \"Config2\" configuration in the .xcdatamodeld file\n        localStorageOptions: .recreateStoreOnModelMismatch \u002F\u002F if migration paths cannot be resolved, recreate the sqlite file\n    ),\n    completion: { (result) -> Void in\n        switch result {\n        case .success(let storage):\n            print(\"Successfully added sqlite store: \\(storage)\")\n        case .failure(let error):\n            print(\"Failed adding sqlite store with error: \\(error)\")\n        }\n    }\n)\n\nCoreStoreDefaults.dataStack = dataStack \u002F\u002F pass the dataStack to CoreStore for easier access later on\n```\n\n> 💡If you have never heard of \"Configurations\", you'll find them in your *.xcdatamodeld* file\n> \u003Cimg src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F3029684\u002F8333192\u002Fe52cfaac-1acc-11e5-9902-08724f9f1324.png\" alt=\"xcode configurations screenshot\" height=212 \u002F>\n\nIn our sample code above, note that you don't need to do the `CoreStoreDefaults.dataStack = dataStack` line. You can just as well hold a reference to the `DataStack` like below and call all its instance methods directly:\n```swift\nclass MyViewController: UIViewController {\n    let dataStack = DataStack(xcodeModelName: \"MyModel\") \u002F\u002F keep reference to the stack\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        do {\n            try self.dataStack.addStorageAndWait(SQLiteStore.self)\n        }\n        catch { \u002F\u002F ...\n        }\n    }\n    func methodToBeCalledLaterOn() {\n        let objects = self.dataStack.fetchAll(From\u003CMyEntity>())\n        print(objects)\n    }\n}\n```\n\n> 💡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](#type-safe-corestoreobjects).\n\nNotice that in our previous examples, `addStorageAndWait(_:)` and `addStorage(_:completion:)` both accept either `InMemoryStore`, or `SQLiteStore`. These implement the `StorageInterface` protocol.\n\n### In-memory store\nThe 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.\n```swift\ntry dataStack.addStorageAndWait(\n    InMemoryStore(\n        configuration: \"Config2\" \u002F\u002F optional. Use entities from the \"Config2\" configuration in the .xcdatamodeld file\n    )\n)\n```\nAsynchronous variant:\n```swift\ntry dataStack.addStorage(\n    InMemoryStore(\n        configuration: \"Config2\n    ),\n    completion: { storage in\n        \u002F\u002F ...\n    }\n)\n```\n\n(A reactive-programming variant of this method is explained in detail in the section on [`DataStack` Combine publishers](#datastackreactive))\n\n### Local Store\nThe most common `StorageInterface` you will probably use is the `SQLiteStore`, which saves data in a local SQLite file.\n```swift\nlet migrationProgress = dataStack.addStorage(\n    SQLiteStore(\n        fileName: \"MyStore.sqlite\",\n        configuration: \"Config2\", \u002F\u002F optional. Use entities from the \"Config2\" configuration in the .xcdatamodeld file\n        migrationMappingProviders: [Bundle.main], \u002F\u002F optional. The bundles that contain required .xcmappingmodel files\n        localStorageOptions: .recreateStoreOnModelMismatch \u002F\u002F optional. Provides settings that tells the DataStack how to setup the persistent store\n    ),\n    completion: { \u002F* ... *\u002F }\n)\n```\nRefer to the *SQLiteStore.swift* source documentation for detailed explanations for each of the default values.\n\nCoreStore can decide the default values for these properties, so `SQLiteStore`s can be initialized with no arguments:\n```swift\ntry dataStack.addStorageAndWait(SQLiteStore())\n```\n\n(The asynchronous variant of this method is explained further in the next section on [Migrations](#starting-migrations), and a reactive-programming variant in the section on [`DataStack` Combine publishers](#datastackreactive))\n\nThe file-related properties of `SQLiteStore` are actually requirements of another protocol that it implements, the `LocalStorage` protocol:\n```swift\npublic protocol LocalStorage: StorageInterface {\n    var fileURL: NSURL { get }\n    var migrationMappingProviders: [SchemaMappingProvider] { get }\n    var localStorageOptions: LocalStorageOptions { get }\n    func dictionary(forOptions: LocalStorageOptions) -> [String: AnyObject]?\n    func cs_eraseStorageAndWait(metadata: [String: Any], soureModelHint: NSManagedObjectModel?) throws\n}\n```\nIf you have custom `NSIncrementalStore` or `NSAtomicStore` subclasses, you can implement this protocol and use it similarly to `SQLiteStore`.\n\n\n## Migrations\n\n### Declaring model versions\nModel versions are now expressed as a first-class protocol, `DynamicSchema`. CoreStore currently supports the following schema classes:\n- **`XcodeDataModelSchema`**: a model version with entities loaded from a *.xcdatamodeld* file.\n- **`CoreStoreSchema`**: a model version created with `CoreStoreObject` entities. *(See [Type-safe `CoreStoreObject`s](#type-safe-corestore-objects))*\n- **`UnsafeDataModelSchema`**: a model version created with an existing `NSManagedObjectModel` instance.\n\nAll 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:\n\n**Multiple model versions grouped in a *.xcdatamodeld* file (Core Data standard method)**\n```swift\nCoreStoreDefaults.dataStack = DataStack(\n    xcodeModelName: \"MyModel\",\n    bundle: Bundle.main,\n    migrationChain: [\"MyAppModel\", \"MyAppModelV2\", \"MyAppModelV3\", \"MyAppModelV4\"]\n)\n```\n\n**`CoreStoreSchema`-based model version (No *.xcdatamodeld* file needed)**\n*(For more details, see also [Type-safe `CoreStoreObject`s](#type-safe-corestore-objects))*\n```swift\nclass Animal: CoreStoreObject {\n    \u002F\u002F ...\n}\nclass Dog: Animal {\n    \u002F\u002F ...\n}\nclass Person: CoreStoreObject {\n    \u002F\u002F ...\n}\n\nCoreStoreDefaults.dataStack = DataStack(\n    CoreStoreSchema(\n        modelVersion: \"V1\",\n        entities: [\n            Entity\u003CAnimal>(\"Animal\", isAbstract: true),\n            Entity\u003CDog>(\"Dog\"),\n            Entity\u003CPerson>(\"Person\")\n        ]\n    )\n)\n```\n\n**Models in a *.xcdatamodeld* file during past app versions, but migrated to the new `CoreStoreSchema` method**\n```swift\nclass Animal: CoreStoreObject {\n    \u002F\u002F ...\n}\nclass Dog: Animal {\n    \u002F\u002F ...\n}\nclass Person: CoreStoreObject {\n    \u002F\u002F ...\n}\n\nlet legacySchema = XcodeDataModelSchema.from(\n    modelName: \"MyModel\", \u002F\u002F .xcdatamodeld name\n    bundle: bundle,\n    migrationChain: [\"MyAppModel\", \"MyAppModelV2\", \"MyAppModelV3\", \"MyAppModelV4\"]\n)\nlet newSchema = CoreStoreSchema(\n    modelVersion: \"V1\",\n    entities: [\n        Entity\u003CAnimal>(\"Animal\", isAbstract: true),\n        Entity\u003CDog>(\"Dog\"),\n        Entity\u003CPerson>(\"Person\")\n    ]\n)\nCoreStoreDefaults.dataStack = DataStack(\n    schemaHistory: SchemaHistory(\n        legacySchema + [newSchema],\n        migrationChain: [\"MyAppModel\", \"MyAppModelV2\", \"MyAppModelV3\", \"MyAppModelV4\", \"V1\"] \n    )\n)   \n```\n\n**`CoreStoreSchema`-based model versions with progressive migration**\n```swift\ntypealias Animal = V2.Animal\ntypealias Dog = V2.Dog\ntypealias Person = V2.Person\nenum V2 {\n    class Animal: CoreStoreObject {\n        \u002F\u002F ...\n    }\n    class Dog: Animal {\n        \u002F\u002F ...\n    }\n    class Person: CoreStoreObject {\n        \u002F\u002F ...\n    }\n}\nenum V1 {\n    class Animal: CoreStoreObject {\n        \u002F\u002F ...\n    }\n    class Dog: Animal {\n        \u002F\u002F ...\n    }\n    class Person: CoreStoreObject {\n        \u002F\u002F ...\n    }\n}\n\nCoreStoreDefaults.dataStack = DataStack(\n    CoreStoreSchema(\n        modelVersion: \"V1\",\n        entities: [\n            Entity\u003CV1.Animal>(\"Animal\", isAbstract: true),\n            Entity\u003CV1.Dog>(\"Dog\"),\n            Entity\u003CV1.Person>(\"Person\")\n        ]\n    ),\n    CoreStoreSchema(\n        modelVersion: \"V2\",\n        entities: [\n            Entity\u003CV2.Animal>(\"Animal\", isAbstract: true),\n            Entity\u003CV2.Dog>(\"Dog\"),\n            Entity\u003CV2.Person>(\"Person\")\n        ]\n    ),\n    migrationChain: [\"V1\", \"V2\"]\n)\n```\n\n\n### Starting migrations\nWe 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 data migrations. In fact CoreStore will only attempt a synchronous **lightweight** migration if you explicitly provide the `.allowSynchronousLightweightMigration` option:\n```swift\ntry dataStack.addStorageAndWait(\n    SQLiteStore(\n        fileURL: sqliteFileURL,\n        localStorageOptions: .allowSynchronousLightweightMigration\n    )\n}\n```\nif you do so, any model mismatch will be thrown as an error. \n\nIn general though, if migrations are expected the asynchronous variant `addStorage(_:completion:)` method is recommended instead:\n```swift\nlet migrationProgress: Progress? = try dataStack.addStorage(\n    SQLiteStore(\n        fileName: \"MyStore.sqlite\",\n        configuration: \"Config2\"\n    ),\n    completion: { (result) -> Void in\n        switch result {\n        case .success(let storage):\n            print(\"Successfully added sqlite store: \\(storage)\")\n        case .failure(let error):\n            print(\"Failed adding sqlite store with error: \\(error)\")\n        }\n    }\n)\n```\nThe `completion` block reports a `SetupResult` that indicates success or failure.\n\n(A reactive-programming variant of this method is explained further in the section on [`DataStack` Combine publishers](#datastackreactive))\n\nNotice 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*:\n```swift\nmigrationProgress?.setProgressHandler { [weak self] (progress) -> Void in\n    self?.progressView?.setProgress(Float(progress.fractionCompleted), animated: true)\n    self?.percentLabel?.text = progress.localizedDescription \u002F\u002F \"50% completed\"\n    self?.stepLabel?.text = progress.localizedAdditionalDescription \u002F\u002F \"0 of 2\"\n}\n```\nThis closure is executed on the main thread so UIKit and AppKit calls can be done safely.\n\n\n### Progressive migrations\nBy 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.\n\nThe `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:\n```swift\nlet dataStack = DataStack(migrationChain: \n    [\"MyAppModel\", \"MyAppModelV2\", \"MyAppModelV3\", \"MyAppModelV4\"])\n```\nThe 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.\n\nFor more complex, non-linear migration paths, you can also pass in a version tree that maps the key-values to the source-destination versions:\n```swift\nlet dataStack = DataStack(migrationChain: [\n    \"MyAppModel\": \"MyAppModelV3\",\n    \"MyAppModelV2\": \"MyAppModelV4\",\n    \"MyAppModelV3\": \"MyAppModelV4\"\n])\n```\nThis allows for different migration paths depending on the starting version. The example above resolves to the following paths:\n- MyAppModel-MyAppModelV3-MyAppModelV4\n- MyAppModelV2-MyAppModelV4\n- MyAppModelV3-MyAppModelV4\n\nInitializing 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):\n```swift\nlet dataStack = DataStack(migrationChain: nil)\n```\n\nThe `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:\n- a version appears twice in an array\n- a version appears twice as a key in a dictionary literal\n- a loop is found in any of the paths\n\n> ⚠️**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.\n\n\n### Forecasting migrations\n\nSometimes 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:)`:\n```swift\ndo {\n    let storage = SQLiteStorage(fileName: \"MyStore.sqlite\")\n    let migrationTypes: [MigrationType] = try dataStack.requiredMigrationsForStorage(storage)\n    if migrationTypes.count > 1\n        || (migrationTypes.filter { $0.isHeavyweightMigration }.count) > 0 {\n        \u002F\u002F ... will migrate more than once. Show special waiting screen\n    }\n    else if migrationTypes.count > 0 {\n        \u002F\u002F ... will migrate just once. Show simple activity indicator\n    }\n    else {\n        \u002F\u002F ... Do nothing\n    }\n    dataStack.addStorage(storage, completion: { \u002F* ... *\u002F })\n}\ncatch {\n    \u002F\u002F ... either inspection of the store failed, or if no mapping model was found\u002Finferred\n}\n```\n`requiredMigrationsForStorage(_:)` returns an array of `MigrationType`s, where each item in the array may be either of the following values:\n```swift\ncase lightweight(sourceVersion: String, destinationVersion: String)\ncase heavyweight(sourceVersion: String, destinationVersion: String)\n```\nEach `MigrationType` indicates the migration type for each step in the `MigrationChain`. Use these information as fit for your app.\n\n\n### Custom migrations\n\nCoreStore offers several ways to declare migration mappings:\n\n- `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.\n- `XcodeSchemaMappingProvider`: A mapping provider which loads entity mappings from *.xcmappingmodel* files in a specified `Bundle`.\n- `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.\n\nThese mapping providers conform to `SchemaMappingProvider` and can be passed to `SQLiteStore`'s initializer:\n```swift\nlet dataStack = DataStack(migrationChain: [\"MyAppModel\", \"MyAppModelV2\", \"MyAppModelV3\", \"MyAppModelV4\"])\n_ = try dataStack.addStorage(\n    SQLiteStore(\n        fileName: \"MyStore.sqlite\",\n        migrationMappingProviders: [\n            XcodeSchemaMappingProvider(from: \"V1\", to: \"V2\", mappingModelBundle: Bundle.main),\n            CustomSchemaMappingProvider(from: \"V2\", to: \"V3\", entityMappings: [.deleteEntity(\"Person\") ])\n        ]\n    ),\n    completion: { (result) -> Void in\n        \u002F\u002F ...\n    }\n)\n```\n\nFor 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.\n\nFor `CustomSchemaMappingProvider`, more granular updates are supported through the dynamic objects `UnsafeSourceObject` and `UnsafeDestinationObject`. The example below allows the migration to conditionally ignore some objects:\n```swift\nlet person_v2_to_v3_mapping = CustomSchemaMappingProvider(\n    from: \"V2\",\n    to: \"V3\",\n    entityMappings: [\n        .transformEntity(\n            sourceEntity: \"Person\",\n            destinationEntity: \"Person\",\n            transformer: { (sourceObject: UnsafeSourceObject, createDestinationObject: () -> UnsafeDestinationObject) in\n                \n                if (sourceObject[\"isVeryOldAccount\"] as! Bool?) == true {\n                    return \u002F\u002F this account is too old, don't migrate \n                }\n                \u002F\u002F migrate the rest\n                let destinationObject = createDestinationObject()\n                destinationObject.enumerateAttributes { (attribute, sourceAttribute) in\n                \n                if let sourceAttribute = sourceAttribute {\n                    destinationObject[attribute] = sourceObject[sourceAttribute]\n                }\n            }\n        ) \n    ]\n)\nSQLiteStore(\n    fileName: \"MyStore.sqlite\",\n    migrationMappingProviders: [person_v2_to_v3_mapping]\n)\n```\nThe `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.\n\n\n## Saving and processing transactions\nTo 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:\n```swift\nlet dataStack = self.dataStack\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        \u002F\u002F make changes\n    },\n    completion: { (result) -> Void in\n        \u002F\u002F ...\n    }\n)\n```\nTransaction closures automatically save changes once the closures completes. To cancel and rollback a transaction, throw a `CoreStoreError.userCancelled` from inside the closure by calling `try transaction.cancel()`:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        \u002F\u002F ...\n        if shouldCancel {\n            try transaction.cancel()\n        }\n        \u002F\u002F ...\n    },\n    completion: { (result) -> Void in\n        if case .failure(.userCancelled) = result {\n            \u002F\u002F ... cancelled\n        }\n    }\n)\n```\n> ⚠️**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.\n\nThe examples above use `perform(asynchronous:...)`, but there are actually 3 types of transactions at your disposal: *asynchronous*, *synchronous*, and *unsafe*.\n\n### Transaction types\n\n#### Asynchronous transactions\nare 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:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Bool in\n        \u002F\u002F make changes\n        return transaction.hasChanges\n    },\n    completion: { (result) -> Void in\n        switch result {\n        case .success(let hasChanges): print(\"success! Has changes? \\(hasChanges)\")\n        case .failure(let error): print(error)\n        }\n    }\n)\n```\nThe success and failure can also be declared as separate handlers:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Int in\n        \u002F\u002F make changes\n        return transaction.delete(objects)\n    },\n    success: { (numberOfDeletedObjects: Int) -> Void in\n        print(\"success! Deleted \\(numberOfDeletedObjects) objects\")\n    },\n    failure: { (error) -> Void in\n        print(error)\n    }\n)\n```\n\n> ⚠️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](#passing-objects-safely).\n\nTransactions created from `perform(asynchronous:...)` are instances of `AsynchronousDataTransaction`.\n\n#### Synchronous transactions\nare created from `perform(synchronous:...)`. While the syntax is similar to its asynchronous counterpart, `perform(synchronous:...)` waits for its transaction block to complete before returning:\n```swift\nlet hasChanges = dataStack.perform(\n    synchronous: { (transaction) -> Bool in\n        \u002F\u002F make changes\n        return transaction.hasChanges\n    }\n)\n```\n`transaction` above is a `SynchronousDataTransaction` instance.\n\nSince `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.\n\nBy 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:\n```swift\ndataStack.perform(\n    synchronous: { (transaction) in\n        let person = transaction.create(Into\u003CPerson>())\n        person.name = \"John\"\n    },\n    waitForAllObservers: false\n)\nlet newPerson = dataStack.fetchOne(From\u003CPerson>.where(\\.name == \"John\"))\n\u002F\u002F newPerson may be nil!\n\u002F\u002F The DataStack may have not yet received the update notification.\n```\nDue to this complicated nature of synchronous transactions, if your app has very heavy transaction throughput it is highly recommended to use [asynchronous transactions](#asynchronous-transactions) instead.\n\n#### Unsafe transactions\nare special in that they do not enclose updates within a closure:\n```swift\nlet transaction = dataStack.beginUnsafe()\n\u002F\u002F make changes\ndownloadJSONWithCompletion({ (json) -> Void in\n\n    \u002F\u002F make other changes\n    transaction.commit()\n})\ndownloadAnotherJSONWithCompletion({ (json) -> Void in\n\n    \u002F\u002F make some other changes\n    transaction.commit()\n})\n```\nThis 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.\"\n\nAs the above example also shows, with unsafe transactions `commit()` can be called multiple times.\n\n\nYou'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.\n\n### Creating objects\n\nThe `create(...)` method accepts an `Into` clause which specifies the entity for the object you want to create:\n```swift\nlet person = transaction.create(Into\u003CMyPersonEntity>())\n```\nWhile the syntax is straightforward, CoreStore does not just naively insert a new object. This single line does the following:\n- Checks that the entity type exists in any of the transaction's parent persistent store\n- If the entity belongs to only one persistent store, a new object is inserted into that store and returned from `create(...)`\n- 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.**\n- 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).\n\nIf the entity exists in multiple configurations, you need to provide the configuration name for the destination persistent store:\n```swift\nlet person = transaction.create(Into\u003CMyPersonEntity>(\"Config1\"))\n```\nor if the persistent store is the auto-generated \"Default\" configuration, specify `nil`:\n```swift\nlet person = transaction.create(Into\u003CMyPersonEntity>(nil))\n```\nNote 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. \n\n### Updating objects\n\nAfter creating an object from the transaction, you can simply update its properties as normal:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let person = transaction.create(Into\u003CMyPersonEntity>())\n        person.name = \"John Smith\"\n        person.age = 30\n    },\n    completion: { _ in }\n)\n```\nTo update an existing object, fetch the object's instance from the transaction:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let person = try transaction.fetchOne(\n            From\u003CMyPersonEntity>()\n                .where(\\.name == \"Jane Smith\")\n        )\n        person.age = person.age + 1\n    },\n    completion: { _ in }\n)\n```\n*(For more about fetching, see [Fetching and querying](#fetching-and-querying))*\n\n**Do not update an instance that was not created\u002Ffetched 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:\n```swift\nlet jane: MyPersonEntity = \u002F\u002F ...\n\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        \u002F\u002F WRONG: jane.age = jane.age + 1\n        \u002F\u002F RIGHT:\n        let jane = transaction.edit(jane)! \u002F\u002F using the same variable name protects us from misusing the non-transaction instance\n        jane.age = jane.age + 1\n    },\n    completion: { _ in }\n)\n```\nThis is also true when updating an object's relationships. Make sure that the object assigned to the relationship is also created\u002Ffetched from the transaction:\n```swift\nlet jane: MyPersonEntity = \u002F\u002F ...\nlet john: MyPersonEntity = \u002F\u002F ...\n\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        \u002F\u002F WRONG: jane.friends = [john]\n        \u002F\u002F RIGHT:\n        let jane = transaction.edit(jane)!\n        let john = transaction.edit(john)!\n        jane.friends = NSSet(array: [john])\n    },\n    completion: { _ in }\n)\n```\n\n### Deleting objects\n\nDeleting 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):\n```swift\nlet john: MyPersonEntity = \u002F\u002F ...\n\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        transaction.delete(john)\n    },\n    completion: { _ in }\n)\n```\nor several objects at once:\n```swift\nlet john: MyPersonEntity = \u002F\u002F ...\nlet jane: MyPersonEntity = \u002F\u002F ...\n\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        try transaction.delete(john, jane)\n        \u002F\u002F try transaction.delete([john, jane]) is also allowed\n    },\n    completion: { _ in }\n)\n```\nIf you do not have references yet to the objects to be deleted, transactions have a `deleteAll(...)` method you can pass a query to:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        try transaction.deleteAll(\n            From\u003CMyPersonEntity>()\n                .where(\\.age > 30)\n        )\n    },\n    completion: { _ in }\n)\n```\n\n### Passing objects safely\n\nAlways 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:\n```swift\nlet jane: MyPersonEntity = \u002F\u002F ...\n\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let jane = transaction.edit(jane)!\n        jane.age = jane.age + 1\n    },\n    completion: { _ in }\n)\n```\nBut `CoreStore`, `DataStack` and `BaseDataTransaction` have a very flexible `fetchExisting(...)` method that you can pass instances back and forth with:\n```swift\nlet jane: MyPersonEntity = \u002F\u002F ...\n\ndataStack.perform(\n    asynchronous: { (transaction) -> MyPersonEntity in\n        let jane = transaction.fetchExisting(jane)! \u002F\u002F instance for transaction\n        jane.age = jane.age + 1\n        return jane\n    },\n    success: { (transactionJane) in\n        let jane = dataStack.fetchExisting(transactionJane)! \u002F\u002F instance for DataStack\n        print(jane.age)\n    },\n    failure: { (error) in\n        \u002F\u002F ...\n    }\n)\n```\n`fetchExisting(...)` also works with multiple `NSManagedObject`s, `CoreStoreObject`s, or with `NSManagedObjectID`s:\n```swift\nvar peopleIDs: [NSManagedObjectID] = \u002F\u002F ...\n\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let jane = try transaction.fetchOne(\n            From\u003CMyPersonEntity>()\n                .where(\\.name == \"Jane Smith\")\n        )\n        jane.friends = NSSet(array: transaction.fetchExisting(peopleIDs)!)\n        \u002F\u002F ...\n    },\n    completion: { _ in }\n)\n```\n\n\n## Importing data\nSome 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 a JSON dictionary for example, you may be extracting values as such:\n```swift\nlet json: [String: Any] = \u002F\u002F ...\nperson.name = json[\"name\"] as? NSString\nperson.age = json[\"age\"] as? NSNumber\n\u002F\u002F ...\n```\nIf 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:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let json: [String: Any] = \u002F\u002F ...\n        try! transaction.importObject(\n            Into\u003CMyPersonEntity>(),\n            source: json\n        )\n    },\n    completion: { _ in }\n)\n```\nTo support data import for an entity, implement either `ImportableObject` or `ImportableUniqueObject` on the `NSManagedObject` or `CoreStoreObject` subclass:\n- `ImportableObject`: Use this protocol if the object have no inherent uniqueness and new objects should always be added when calling `importObject(...)`.\n- `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(...)`.\n\nBoth protocols require implementers to specify an `ImportSource` which can be set to any type that the object can extract data from:\n```swift\ntypealias ImportSource = NSDictionary\n```\n```swift\ntypealias ImportSource = [String: Any]\n```\n```swift\ntypealias ImportSource = NSData\n```\nYou can even use external types from popular 3rd-party JSON libraries, or just simple tuples or primitives.\n\n#### `ImportableObject`\n`ImportableObject` is a very simple protocol:\n```swift\npublic protocol ImportableObject: AnyObject {\n    typealias ImportSource\n    static func shouldInsert(from source: ImportSource, in transaction: BaseDataTransaction) -> Bool\n    func didInsert(from source: ImportSource, in transaction: BaseDataTransaction) throws\n}\n```\nFirst, set `ImportSource` to the expected type of the data source:\n```swift\ntypealias ImportSource = [String: Any]\n```\nThis lets us call `importObject(_:source:)` with any `[String: Any]` type as the argument to `source`:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let json: [String: Any] = \u002F\u002F ...\n        try! transaction.importObject(\n            Into\u003CMyPersonEntity>(),\n            source: json\n        )\n        \u002F\u002F ...\n    },\n    completion: { _ in }\n)\n```\nThe actual extraction and assignment of values should be implemented in the `didInsert(from:in:)` method of the `ImportableObject` protocol:\n```swift\nfunc didInsert(from source: ImportSource, in transaction: BaseDataTransaction) throws {\n    self.name = source[\"name\"] as? NSString\n    self.age = source[\"age\"] as? NSNumber\n    \u002F\u002F ...\n}\n```\nTransactions also let you import multiple objects at once using the `importObjects(_:sourceArray:)` method:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let jsonArray: [[String: Any]] = \u002F\u002F ...\n        try! transaction.importObjects(\n            Into\u003CMyPersonEntity>(),\n            sourceArray: jsonArray \u002F\u002F make sure this is of type Array\u003CMyPersonEntity.ImportSource>\n        )\n        \u002F\u002F ...\n    },\n    completion: { _ in }\n)\n```\nDoing 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.\n\nIf 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:)`:\n```swift\nfunc didInsert(from source: ImportSource, in transaction: BaseDataTransaction) throws {\n    self.name = source[\"name\"] as? NSString\n    self.age = source[\"age\"] as? NSNumber\n    \u002F\u002F ...\n    if self.name == nil {\n        throw Errors.InvalidNameError\n    }\n}\n```\nDoing so can let you abandon an invalid transaction immediately:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let jsonArray: [[String: Any]] = \u002F\u002F ...\n\n        try transaction.importObjects(\n            Into\u003CMyPersonEntity>(),\n            sourceArray: jsonArray\n        )\n    },\n    success: {\n        \u002F\u002F ...\n    },\n    failure: { (error) in\n        switch error {\n        case Errors.InvalidNameError: print(\"Invalid name\")\n        \u002F\u002F ...\n        }\n    }\n)\n```\n\n#### `ImportableUniqueObject`\nTypically, 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:\n```swift\npublic protocol ImportableUniqueObject: ImportableObject {\n    typealias ImportSource\n    typealias UniqueIDType: ImportableAttributeType\n\n    static var uniqueIDKeyPath: String { get }\n    var uniqueIDValue: UniqueIDType { get set }\n\n    static func shouldInsert(from source: ImportSource, in transaction: BaseDataTransaction) -> Bool\n    static func shouldUpdate(from source: ImportSource, in transaction: BaseDataTransaction) -> Bool\n    static func uniqueID(from source: ImportSource, in transaction: BaseDataTransaction) throws -> UniqueIDType?\n    func didInsert(from source: ImportSource, in transaction: BaseDataTransaction) throws\n    func update(from source: ImportSource, in transaction: BaseDataTransaction) throws\n}\n```\nNotice that it has the same insert methods as `ImportableObject`, with additional methods for updates and for specifying the unique ID:\n```swift\nclass var uniqueIDKeyPath: String {\n    return #keyPath(MyPersonEntity.personID) \n}\nvar uniqueIDValue: Int { \n    get { return self.personID }\n    set { self.personID = newValue }\n}\nclass func uniqueID(from source: ImportSource, in transaction: BaseDataTransaction) throws -> Int? {\n    return source[\"id\"] as? Int\n}\n```\nFor `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.\n\nYou can then create\u002Fupdate an object by calling a transaction's `importUniqueObject(...)` method:\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let json: [String: Any] = \u002F\u002F ...\n        try! transaction.importUniqueObject(\n            Into\u003CMyPersonEntity>(),\n            source: json\n        )\n        \u002F\u002F ...\n    },\n    completion: { _ in }\n)\n```\nor multiple objects at once with the `importUniqueObjects(...)` method:\n\n```swift\ndataStack.perform(\n    asynchronous: { (transaction) -> Void in\n        let jsonArray: [[String: AnyObject]] = \u002F\u002F ...\n        try! transaction.importUniqueObjects(\n            Into\u003CMyPersonEntity>(),\n            sourceArray: jsonArray\n        )\n        \u002F\u002F ...\n    },\n    completion: { _ in }\n)\n```\nAs with `ImportableObject`, you can control whether to skip importing an object by implementing \n`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.\n\n\n## Fetching and Querying\nBefore we dive in, be aware that CoreStore distinguishes between *fetching* and *querying*:\n- 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:\n    - results need to be `NSManagedObject` or `CoreStoreObject` instances\n    - unsaved objects should be included in the search (though fetches can be configured to exclude unsaved ones)\n- 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:\n    - you need to compute aggregate functions (see below for a list of supported functions)\n    - 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)\n    - only values for specified attribute keys need to be included in the results\n    - unsaved objects should be ignored\n\n#### `From` clause\nThe search conditions for fetches and queries are specified using *clauses*. All fetches and queries require a `From` clause that indicates the target entity type:\n```swift\nlet people = try dataStack.fetchAll(From\u003CMyPersonEntity>())\n```\n`people` in the example above will be of type `[MyPersonEntity]`. The `From\u003CMyPersonEntity>()` clause indicates a fetch to all persistent stores that `MyPersonEntity` belong to.\n\nIf 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:\n```swift\nlet people = try dataStack.fetchAll(From\u003CMyPersonEntity>(\"Config1\")) \u002F\u002F ignore objects in persistent stores other than the \"Config1\" configuration\n```\nor if the persistent store is the auto-generated \"Default\" configuration, specify `nil`:\n```swift\nlet person = try dataStack.fetchAll(From\u003CMyPersonEntity>(nil))\n```\nNow we know how to use a `From` clause, let's move on to fetching and querying.\n\n### Fetching\n\nThere 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\u002For `Tweak` clauses.\n\n- `fetchAll(...)` - returns an array of all objects that match the criteria.\n- `fetchOne(...)` - returns the first object that match the criteria.\n- `fetchCount(...)` - returns the number of objects that match the criteria.\n- `fetchObjectIDs(...)` - returns an array of `NSManagedObjectID`s for all objects that match the criteria.\n- `fetchObjectID(...)` - returns the `NSManagedObjectID`s for the first objects that match the criteria.\n\nEach method's purpose is straightforward, but we need to understand how to set the clauses for the fetch.\n\n#### `Where` clause\n\nThe `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):\n```swift\nvar people = try dataStack.fetchAll(\n    From\u003CMyPersonEntity>(),\n    Where\u003CMyPersonEntity>(\"%K > %d\", \"age\", 30) \u002F\u002F string format initializer\n)\npeople = try dataStack.fetchAll(\n    From\u003CMyPersonEntity>(),\n    Where\u003CMyPersonEntity>(true) \u002F\u002F boolean initializer\n)\n```\nIf you do have an existing `NSPredicate` instance already, you can pass that to `Where` as well:\n```swift\nlet predicate = NSPredicate(...)\nvar people = dataStack.fetchAll(\n    From\u003CMyPersonEntity>(),\n    Where\u003CMyPersonEntity>(predicate) \u002F\u002F predicate initializer\n)\n```\n `Where` clauses are generic types. To avoid verbose repetition of the generic object type, fetch methods support **Fetch Chain builders**. We can also use Swift's Smart KeyPaths as the `Where` clause expression:\n```swift\nvar people = try dataStack.fetchAll(\n    From\u003CMyPersonEntity>()\n        .where(\\.age > 30) \u002F\u002F Type-safe!\n)\n```\n`Where` clauses also implement the `&&`, `||`, and `!` logic operators, so you can provide logical conditions without writing too much `AND`, `OR`, and `NOT` strings:\n```swift\nvar people = try dataStack.fetchAll(\n    From\u003CMyPersonEntity>()\n        .where(\\.age > 30 && \\.gender == \"M\")\n)\n```\nIf you do not provide a `Where` clause, all objects that belong to the specified `From` will be returned.\n\n#### `OrderBy` clause\n\nThe `OrderBy` clause is CoreStore's `NSSortDescriptor` wrapper. Use it to specify attribute keys in which to sort the fetch (or query) results with.\n```swift\nvar mostValuablePeople = try dataStack.fetchAll(\n    From\u003CMyPersonEntity>(),\n    OrderBy\u003CMyPersonEntity>(.descending(\"rating\"), .ascending(\"surname\"))\n)\n```\nAs seen above, `OrderBy` accepts a list of `SortKey` enumeration values, which can be either `.ascending` or `.descending`.\nAs with `Where` clauses, `OrderBy` clauses are also generic types. To avoid verbose repetition of the generic object type, fetch methods support **Fetch Chain builders**. We can also use Swift's Smart KeyPaths as the `OrderBy` clause expression:\n```swift\nvar people = try dataStack.fetchAll(\n    From\u003CMyPersonEntity>()\n        .orderBy(.descending(\\.rating), .ascending(\\.surname)) \u002F\u002F Type-safe!\n)\n```\n\nYou can use the `+` and `+=` operator to append `OrderBy`s together. This is useful when sorting conditionally:\n```swift\nvar orderBy = OrderBy\u003CMyPersonEntity>(.descending(\\.rating))\nif sortFromYoungest {\n    orderBy += OrderBy(.ascending(\\.age))\n}\nvar mostValuablePeople = try dataStack.fetchAll(\n    From\u003CMyPersonEntity>(),\n    orderBy\n)\n```\n\n#### `Tweak` clause\n\nThe `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:\n```swift\nvar people = try dataStack.fetchAll(\n    From\u003CMyPersonEntity>(),\n    Where\u003CMyPersonEntity>(\"age > %d\", 30),\n    OrderBy\u003CMyPersonEntity>(.ascending(\"surname\")),\n    Tweak { (fetchRequest) -> Void in\n        fetchRequest.includesPendingChanges = false\n        fetchRequest.returnsObjectsAsFaults = false\n        fetchRequest.includesSubentities = false\n    }\n)\n```\n`Tweak` also supports **Fetch Chain builders**:\n```swift\nvar people = try dataStack.fetchAll(\n    From\u003CMyPersonEntity>(),\n        .where(\\.age > 30)\n        .orderBy(.ascending(\\.surname))\n        .tweak {\n            $0.includesPendingChanges = false\n            $0.returnsObjectsAsFaults = false\n            $0.includesSubentities = false\n        }\n)\n```\nThe clauses are evaluated the order they appear in the fetch\u002Fquery, so you typically need to set `Tweak` as the last clause.\n`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.\n\nWhile `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!\n\n### Querying\n\nOne 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:\n\n- `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.\n- `queryAttributes(...)` - returns an array of dictionaries containing attribute keys with their corresponding values.\n\nBoth methods above accept the same parameters: a required `From` clause, a required `Select\u003CT>` clause, and an optional series of `Where`, `OrderBy`, `GroupBy`, and\u002For `Tweak` clauses.\n\nSetting 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\u003CT>` and `GroupBy` clauses.\n\n#### `Select\u003CT>` clause\n\nThe `Select\u003CT>` clause specifies the target attribute\u002Faggregate key, as well as the expected return type: \n```swift\nlet johnsAge = try dataStack.queryValue(\n    From\u003CMyPersonEntity>(),\n    Select\u003CInt>(\"age\"),\n    Where\u003CMyPersonEntity>(\"name == %@\", \"John Smith\")\n)\n```\nThe 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\u003CInt>` generic type. For `queryValue(...)`, types that conform to `QueryableAttributeType` are allowed as the return type (and therefore as the generic type for `Select\u003CT>`).\n\nFor `queryAttributes(...)`, only `NSDictionary` is valid for `Select`, thus you are allowed to omit the generic type:\n```swift\nlet allAges = try dataStack.queryAttributes(\n    From\u003CMyPersonEntity>(),\n    Select(\"age\")\n)\n```\nquery methods also support **Query Chain builders**. We can also use Swift's Smart KeyPaths to use in the expressions:\n```swift\nlet johnsAge = try dataStack.queryValue(\n    From\u003CMyPersonEntity>()\n        .select(\\.age) \u002F\u002F binds the result to Int\n        .where(\\.name == \"John Smith\")\n)\n```\n\nIf you only need a value for a particular attribute, you can just specify the key name (like we did with `Select\u003CInt>(\"age\")`), but several aggregate functions can also be used as parameter to `Select`:\n- `.average(...)`\n- `.count(...)`\n- `.maximum(...)`\n- `.minimum(...)`\n- `.sum(...)`\n\n```swift\nlet oldestAge = try dataStack.queryValue(\n    From\u003CMyPersonEntity>(),\n    Select\u003CInt>(.maximum(\"age\"))\n)\n```\n\nFor `queryAttributes(...)` which returns an array of dictionaries, you can specify multiple attributes\u002Faggregates to `Select`:\n```swift\nlet personJSON = try dataStack.queryAttributes(\n    From\u003CMyPersonEntity>(),\n    Select(\"name\", \"age\")\n)\n```\n`personJSON` will then have the value:\n```swift\n[\n    [\n        \"name\": \"John Smith\",\n        \"age\": 30\n    ],\n    [\n        \"name\": \"Jane Doe\",\n        \"age\": 22\n    ]\n]\n```\nYou can also include an aggregate as well:\n```swift\nlet personJSON = try dataStack.queryAttributes(\n    From\u003CMyPersonEntity>(),\n    Select(\"name\", .count(\"friends\"))\n)\n```\nwhich returns:\n```swift\n[\n    [\n        \"name\": \"John Smith\",\n        \"count(friends)\": 42\n    ],\n    [\n        \"name\": \"Jane Doe\",\n        \"count(friends)\": 231\n    ]\n]\n```\nThe `\"count(friends)\"` key name was automatically used by CoreStore, but you can specify your own key alias if you need:\n```swift\nlet personJSON = try dataStack.queryAttributes(\n    From\u003CMyPersonEntity>(),\n    Select(\"name\", .count(\"friends\", as: \"friendsCount\"))\n)\n```\nwhich now returns:\n```swift\n[\n    [\n        \"name\": \"John Smith\",\n        \"friendsCount\": 42\n    ],\n    [\n        \"name\": \"Jane Doe\",\n        \"friendsCount\": 231\n    ]\n]\n```\n\n#### `GroupBy` clause\n\nThe `GroupBy` clause lets you group results by a specified attribute\u002Faggregate. This is useful only for `queryAttributes(...)` since `queryValue(...)` just returns the first value.\n```swift\nlet personJSON = try dataStack.queryAttributes(\n    From\u003CMyPersonEntity>(),\n    Select(\"age\", .count(\"age\", as: \"count\")),\n    GroupBy(\"age\")\n)\n```\n`GroupBy` clauses are also generic types and support **Query Chain builders**. We can also use Swift's Smart KeyPaths to use in the expressions:\n```swift\nlet personJSON = try dataStack.queryAttributes(\n    From\u003CMyPersonEntity>()\n        .select(.attribute(\\.age), .count(\\.age, as: \"count\"))\n        .groupBy(\\.age)\n)\n```\nthis returns dictionaries that shows the count for each `\"age\"`:\n```swift\n[\n    [\n        \"age\": 42,\n        \"count\": 1\n    ],\n    [\n        \"age\": 22,\n        \"count\": 1\n    ]\n]\n```\n\n## Logging and error reporting\nOne 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.\n```swift\npublic protocol CoreStoreLogger {\n    func log(level level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString)\n    func log(error error: CoreStoreError, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString)\n    func assert(@autoclosure condition: () -> Bool, @autoclosure message: () -> String, fileName: StaticString, lineNumber: Int, functionName: StaticString)\n    func abort(message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString)\n}\n```\nImplement this protocol with your custom class then pass the instance to `CoreStoreDefaults.logger`:\n```swift\nCoreStoreDefaults.logger = MyLogger()\n```\nDoing so channels all logging calls to your logger.\n\nNote that ","CoreStore 是一个旨在通过 Swift 的优雅和安全性来释放 Core Data 真正潜力的库。它提供了简化 Core Data 操作的核心功能，包括设置内存或本地存储、数据迁移等，并且支持多种依赖管理工具如 CocoaPods、Carthage 和 Swift Package Manager。该库特别适合需要高效管理和操作持久化数据的 iOS、macOS、watchOS 和 tvOS 应用开发场景。其设计注重代码的安全性和可维护性，使得开发者能够更加专注于业务逻辑而非底层数据处理细节。","2026-06-11 03:09:31","top_language"]