Open-source project
JohnEstropia/CoreStore avatar
JohnEstropia/CoreStore

CoreStore: a Swift wrapper for Core Data with type-safe models and progressive migrations

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

4,048 stars258 forksSwiftMIT

At a glance

What is it?
CoreStore is an MIT-licensed Swift library that layers transactions, type-safe model objects and a migration chain over Apple's Core Data. It fits iOS, macOS, watchOS and tvOS apps that already store data locally and want the concurrency rules handled for them.
Who is it for?
CoreStore is worth adopting if your app already uses Core Data, you target the versions listed in the README (iOS 16.0+, macOS 13.0+, watchOS 9.0+, tvOS 16.0+ on Swift 5.9), and you want the transaction and migration machinery written for you. Do not adopt it if you need a cross-platform persistence layer, if you want to avoid Core Data's model file format, or if your deployment floor sits below those OS versions.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 63 days ago.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What CoreStore adds on top of Core Data

Core Data gives you an object graph, a persistent store and a concurrency model that is easy to get wrong. CoreStore's README frames the library as "unleashing the real power of Core Data with the elegance and safety of Swift", and the concrete offer behind that line is a set of abstractions: a DataStack that owns the coordinator, transactions that run on the correct queue, and model classes declared in pure Swift rather than in the Xcode model editor alone.

The audience is narrow and specific. This is a library for people already committed to Core Data on Apple platforms. It does not replace the persistence layer with something database-agnostic, and it does not try to hide the SQLite store underneath. If you have an app with an existing .xcdatamodeld, CoreStore is designed to sit on top of it; the README notes that classic NSManagedObject subclasses are also supported alongside its own CoreStoreObject base class.

The value proposition is mostly about the code you no longer write. Fetch requests, context merging and migration steps are the parts of Core Data that generate the most boilerplate, and CoreStore's tutorials map each of them to a shorter construct: a typed fetch with From, Where and OrderBy clauses, a transaction closure, a migrationChain array.

The DataStack, transactions and the migration chain

The architecture visible in the README centers on one object. You create a DataStack with a model name and an optional migration chain, then add one or more stores to it. The stack owns the persistent store coordinator and the contexts underneath, and you interact with it through perform calls rather than by touching NSManagedObjectContext directly.

Transactions come in three documented flavors: asynchronous, synchronous and unsafe. That naming matters. The asynchronous form is the default the README demonstrates, and it hands your closure a transaction object with create, update and delete operations. The unsafe variant is the escape hatch for work that cannot be expressed inside the transaction abstraction, and the README's own choice of the word "unsafe" is a fair warning that it trades the library's guarantees for flexibility.

Migrations are expressed declaratively. Instead of hand-writing mapping models, you pass the ordered list of model names to the DataStack initializer, and the README describes progressive migrations, forecasting migrations and custom migrations as the supported modes. The forecasting mode is the interesting one: it lets you check whether a migration will succeed before you run it, which addresses the failure mode where an app ships and then discovers mid-upgrade that a store cannot be transformed.

On top of that sit the type-safe CoreStoreObject models with @Field property wrappers for stored, virtual, coded and relationship properties, plus VersionLock for schema versioning. There is also a query layer with Select and GroupBy clauses, an observation layer for single properties, single objects and diffable lists, and reactive bindings for both RxSwift and Combine, with SwiftUI wrappers (ListReader, ObjectReader, ListState, ObjectState) listed in the README's contents.

Installing CoreStore and setting up a local store

The README lists three dependency managers: CocoaPods, Carthage and Swift Package Manager, and the repository carries the corresponding manifests (CoreStore.podspec, Package.swift, and the .swiftpm directory). The Swift Package Manager route is the one with a checked-in Package.swift, so it is the easiest to verify from the repository layout.

In Xcode, add the package by URL and select the version you want. The most recent release listed is 9.3.0, published on 2024-10-31, with 9.2.0 and 9.1.0 before it as Swift 5.9 and Swift 5.7 updates respectively.

bash
# In Xcode: File > Add Package Dependencies
# https://github.com/JohnEstropia/CoreStore

Once the package is linked, the first real use is constructing the stack and adding a SQLite store. The README gives this shape for setup with migration support:

swift
dataStack = DataStack(
    xcodeModelName: "MyStore",
    migrationChain: ["MyStore", "MyStoreV2", "MyStoreV3"]
)

The migrationChain array must list your model versions in order, from the oldest to the current one. Then you attach storage:

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

The completion closure receives a result, and the README leaves its body to the reader, so error handling here is your responsibility. The README also documents an in-memory store option for tests and throwaway data.

Writing happens inside a transaction. The README's example creates an object and sets properties on it:

swift
dataStack.perform(
    asynchronous: { (transaction) -> Void in
        let person = transaction.create(Into<Person>())
        person.name = "John Smith"
        person.age = 42
    }
)

The Person type in that snippet is a CoreStoreObject subclass, declared with @Field.Stored for the name property and @Field.Relationship for relationships, as shown in the README's TL;DR. Objects created inside a transaction should not be passed between threads directly; the README has a dedicated section on passing objects safely, which is the part most new users get wrong first.

Where CoreStore is the wrong choice

The clearest limitation is the platform floor. The README states Swift 5.9 with iOS 16.0+, macOS 13.0+, watchOS 9.0+ and tvOS 16.0+. If you support older OS versions, you are pinned to an older CoreStore branch: the README links Swift 5.4 users to 8.0.1 and Swift 5.3 users to 7.3.1. Running a two-year-old branch of any persistence library is a real cost, and it is not a hypothetical one here.

The second limitation is that CoreStore does not remove Core Data, it wraps it. Every constraint of the underlying framework still applies: the model file, the SQLite store format, the merge behavior, the fact that a failed lightweight migration can leave a store that needs to be rebuilt. If your complaint with Core Data is the framework itself, this library will not settle it. If your complaint is the boilerplate around it, it will.

The third is the transaction model. The README exposes an "unsafe" transaction type precisely because some work does not fit the abstraction. Reaching for it repeatedly means you are paying the dependency cost without getting the safety the library exists to provide.

Finally, consider the maintenance picture honestly. The last push to the develop branch was on 2026-07-29, and the newest tagged release is 9.3.0 from 2024-10-31. That is a gap of roughly nine months between the latest release and the latest commit, which suggests active work has continued on the branch without being cut into a release. Nothing in the repository is archived, but if your team needs a tagged release for every upstream change, the release cadence is slower than the commit cadence.

CoreStore compared with using Core Data directly

The honest alternative is not another library. It is Apple's Core Data used directly, with your own helper layer.

The difference in approach is where the abstractions live. With raw Core Data you write the NSPersistentContainer setup, choose the context for each operation yourself, and manage merge policies by hand. CoreStore puts a DataStack in front of the coordinator and routes writes through transaction closures, so the queue discipline is the library's problem rather than yours. The README's claim to "safety" rests on that: the transaction API makes it harder to touch a managed object on the wrong queue.

Migrations show the same split. Raw Core Data gives you NSEntityMigrationPolicy, mapping models and a versioned model file you maintain manually. CoreStore asks for an ordered array of model names and runs the chain, with forecasting available before you commit to the upgrade. That is less code, and it is also less control over the edge cases where a custom mapping policy is genuinely needed, which is why the README keeps a custom migrations section.

The type-safe model layer is the other axis. CoreStoreObject with @Field property wrappers lets you declare attributes in Swift and get compiler checking on property types and relationship inverses. With plain NSManagedObject you can generate subclasses from the model editor, but the generated code is a starting point, not a contract.

If your app is small, one context, one model version and no migration history, the wrapper earns less than it costs. The library pays off in proportion to how much concurrency and how many schema versions you have.

Licence and the cost of staying current

CoreStore is MIT licensed, per the repository's LICENSE file and the licence badge in the README. MIT is permissive: it allows use in closed-source applications, and it carries no copyleft obligation on your own code. The usual caveat applies and is not legal advice: keep the copyright notice and licence text with any distribution of the library itself, and check with your own counsel if your distribution model is unusual.

Upgrade cost is the more practical concern. The README points upgraders at the change logs on the releases page and warns to read the features section before moving between major versions. The release history shows why: 9.1.0 and 9.2.0 were both Swift-version updates published on the same day (2024-06-24), and 9.3.0 followed in October 2024. A Swift version bump is not a cosmetic change in a library that relies on property wrappers and result builders; it can shift what compiles.

Budget for the model layer too. If you adopt CoreStoreObject, your models are written against the library's property wrappers. Migrating away later means rewriting those declarations back to NSManagedObject or to another framework. The README's support for classic NSManagedObject subclasses softens this, since you can adopt the DataStack and transaction API first and move models over gradually.

Editorial conclusion

CoreStore is worth adopting if your app already uses Core Data, you target the versions listed in the README (iOS 16.0+, macOS 13.0+, watchOS 9.0+, tvOS 16.0+ on Swift 5.9), and you want the transaction and migration machinery written for you. Do not adopt it if you need a cross-platform persistence layer, if you want to avoid Core Data's model file format, or if your deployment floor sits below those OS versions. Before committing, verify three things in your own project: that your existing NSManagedObject models still compile under the CoreStoreObject bridging described in the README, that your migration chain is expressible as the ordered list of model names the DataStack initializer expects, and that the 9.3.0 release notes cover the Swift and OS versions you ship against.

Frequently asked questions

What is CoreStore?

CoreStore is an MIT-licensed Swift library that wraps Apple's Core Data, adding a DataStack, transaction-based writes, type-safe CoreStoreObject models and a declarative migration chain. It targets iOS 16.0+, macOS 13.0+, watchOS 9.0+ and tvOS 16.0+ on Swift 5.9.

Is CoreStore actively maintained?

The repository is not archived, and the last push to the develop branch was on 2026-07-29. The most recent tagged release is 9.3.0 from 2024-10-31, so commits and releases are not moving at the same pace.

How do I install CoreStore in an iOS project?

The README lists CocoaPods, Carthage and Swift Package Manager, and the repository includes a Package.swift and a CoreStore.podspec. With Swift Package Manager you add the GitHub URL as a package dependency in Xcode.

Does CoreStore replace Core Data?

No. It sits on top of Core Data: the DataStack owns the persistent store coordinator and routes writes through transaction closures, and the README states that classic NSManagedObject subclasses are supported alongside CoreStoreObject models.

Which Swift and OS versions does CoreStore 9.x require?

The README states Swift 5.9 with iOS 16.0+, macOS 13.0+, watchOS 9.0+ and tvOS 16.0+. Older Swift versions are served by older branches, with 9.1.0 for Swift 5.7 and 8.0.1 for Swift 5.4.

Official sources

  1. Issues
  2. JohnEstropia/CoreStore on GitHub
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/johnestropia-corestore.svg)](https://hysenlabs.com/projects/johnestropia-corestore)