Unleashing the real power of Core Data with the elegance and safety of Swift
Dependency managers
Contact
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 (or "viewContext"), 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:
```swift
try CoreStoreDefaults.dataStack.addStorageAndWait()
```
This one-liner does the following:
- Triggers the lazy-initialization of `CoreStoreDefaults.dataStack` 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/
In 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:
```swift
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
`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:
| | 🆕[*ObjectPublisher*](#observe-a-single-objects-updates) | [*ObjectMonitor*](#observe-a-single-objects-per-property-updates) | 🆕[*ListPublisher*](#observe-a-diffable-list) | [*ListMonitor*](#observe-detailed-list-changes) |
| --- | --- | --- | --- | --- |
| *Number of objects* | 1 | 1 | N | N |
| *Allows multiple observers* | ✅ | ✅ | ✅ | ✅ |
| *Emits fine-grained changes* | ❌ | ✅ | ❌ | ✅ |
| *Emits DiffableDataSource snapshots* | ✅ | ❌ | ✅ | ❌ |
| *Delegate methods* | ❌ | ✅ | ❌ | ✅ |
| *Closure callback* | ✅ | ❌ | ✅ | ❌ |
| *SwiftUI support* | ✅ | ❌ | ✅ | ❌ |
### 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:
```swift
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:
```swift
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's updates
Observers of an `ObjectPublisher` can receive notifications if any of the object's property changes. You can create an `ObjectPublisher` from the object directly:
```swift
let objectPublisher: ObjectPublisher| Swift | Objective-C |
|---|---|
try dataStack.addStorageAndWait(SQLiteStore.self)
|
NSError *error
[CSCoreStore addSQLiteStorageAndWait:[CSSQLiteStore new] error:&error]
|
dataStack.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);
}];
}];
|
| Before | `@Field.Stored` |
|---|---|
class Person: CoreStoreObject {
|
class Person: CoreStoreObject {
|
| Before | `@Field.Virtual` |
|---|---|
class Animal: CoreStoreObject {
|
class Animal: CoreStoreObject {
|
| Before | `@Field.Coded` |
|---|---|
class Vehicle: CoreStoreObject {
|
class Vehicle: CoreStoreObject {
|
| Before | `@Field.Stored` |
|---|---|
class Pet: CoreStoreObject {
|
class Pet: CoreStoreObject {
|
Copy this dictionary value and use it as the `versionLock:` argument of the `CoreStoreSchema` initializer:
```swift
CoreStoreSchema(
modelVersion: "V1",
entities: [
Entity
# Installation
- Requires:
- iOS 10 SDK and above
- Swift 5.2 (Xcode 11.4+)
- For previous Swift versions: [Swift 3.2](https://github.com/JohnEstropia/CoreStore/tree/4.2.3), [Swift 4.2](https://github.com/JohnEstropia/CoreStore/tree/6.2.1), [Swift 5.0](https://github.com/JohnEstropia/CoreStore/tree/6.3.2), [Swift 5.1](https://github.com/JohnEstropia/CoreStore/tree/7.0.4)
- 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', '~> 7.2'
```
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" >= 7.2.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:
```swift
dependencies: [
.package(url: "https://github.com/JohnEstropia/CoreStore.git", from: "7.2.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