# CoreStore **Repository Path**: godcoder1/CoreStore ## Basic Information - **Project Name**: CoreStore - **Description**: corestore............ - **Primary Language**: Swift - **License**: MIT - **Default Branch**: develop - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-03-12 - **Last Updated**: 2023-03-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
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| 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
## Reactive Programming
### RxSwift
RxSwift utilities are available through the [RxCoreStore](https://github.com/JohnEstropia/RxCoreStore) external module.
### Combine
Combine publishers are available from the `DataStack`, `ListPublisher`, and `ObjectPublisher`'s `.reactive` namespace property.
#### `DataStack.reactive`
Adding a storage through `DataStack.reactive.addStorage(_:)` returns a publisher that reports a `MigrationProgress` `enum` value. The `.migrating` value is only emitted if the storage goes through a migration. Refer to the [Setting up](#setting-up) section for details on the storage setup process itself.
```swift
dataStack.reactive
.addStorage(
SQLiteStore(fileName: "core_data.sqlite")
)
.sink(
receiveCompletion: { result in
// ...
},
receiveValue: { (progress) in
print("\(round(progress.fractionCompleted * 100)) %") // 0.0 ~ 1.0
switch progress {
case .migrating(let storage, let nsProgress):
// ...
case .finished(let storage, let migrationRequired):
// ...
}
}
)
.store(in: &cancellables)
```
[Transactions](#saving-and-processing-transactions) are also available as publishers through `DataStack.reactive.perform(_:)`, which returns a Combine `Future` that emits any type returned from the closure parameter:
```swift
dataStack.reactive
.perform(
asynchronous: { (transaction) -> (inserted: Set| Data | Example |
|---|---|
Signature:
ForEach(_: [ObjectSnapshot<O>])
Closure:
ObjectSnapshot<O>
|
let array: [ObjectSnapshot<Person>]
|
Signature:
ForEach(objectIn: ListSnapshot<O>)
Closure:
ObjectPublisher<O>
|
let listSnapshot: ListSnapshot<Person>
|
Signature:
ForEach(objectIn: [ObjectSnapshot<O>])
Closure:
ObjectPublisher<O>
|
let array: [ObjectSnapshot<Person>]
|
Signature:
ForEach(sectionIn: ListSnapshot<O>)
Closure:
[ListSnapshot<O>.SectionInfo]
|
let listSnapshot: ListSnapshot<Person>
|
Signature:
ForEach(objectIn: ListSnapshot<O>.SectionInfo)
Closure:
ObjectPublisher<O>
|
let listSnapshot: ListSnapshot<Person>
|