mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-11 22:30:34 +01:00
async API prototypes
This commit is contained in:
@@ -783,6 +783,14 @@
|
||||
B5F8496D234898240029D57B /* ListSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8496B234898240029D57B /* ListSnapshot.swift */; };
|
||||
B5F8496E234898240029D57B /* ListSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8496B234898240029D57B /* ListSnapshot.swift */; };
|
||||
B5F8496F234898240029D57B /* ListSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8496B234898240029D57B /* ListSnapshot.swift */; };
|
||||
B5F9C093287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */; };
|
||||
B5F9C094287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */; };
|
||||
B5F9C095287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */; };
|
||||
B5F9C096287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */; };
|
||||
B5F9C098287850D6007AAD2E /* MigrationProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C097287850D6007AAD2E /* MigrationProgress.swift */; };
|
||||
B5F9C099287850D6007AAD2E /* MigrationProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C097287850D6007AAD2E /* MigrationProgress.swift */; };
|
||||
B5F9C09A287850D6007AAD2E /* MigrationProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C097287850D6007AAD2E /* MigrationProgress.swift */; };
|
||||
B5F9C09B287850D6007AAD2E /* MigrationProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C097287850D6007AAD2E /* MigrationProgress.swift */; };
|
||||
B5FAD6A91B50A4B400714891 /* Progress+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6A81B50A4B300714891 /* Progress+Convenience.swift */; };
|
||||
B5FAD6AC1B51285300714891 /* Internals.MigrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6AB1B51285300714891 /* Internals.MigrationManager.swift */; };
|
||||
B5FE4DA21C8481E100FA6A91 /* StorageInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */; };
|
||||
@@ -1061,6 +1069,8 @@
|
||||
B5F5848628633741001F57ED /* Module.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Module.swift; sourceTree = "<group>"; };
|
||||
B5F8496B234898240029D57B /* ListSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListSnapshot.swift; sourceTree = "<group>"; };
|
||||
B5F849702348A6690029D57B /* EnvironmentValues+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EnvironmentValues+DataSources.swift"; sourceTree = "<group>"; };
|
||||
B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataStack+Concurrency.swift"; sourceTree = "<group>"; };
|
||||
B5F9C097287850D6007AAD2E /* MigrationProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationProgress.swift; sourceTree = "<group>"; };
|
||||
B5FAD6A81B50A4B300714891 /* Progress+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Progress+Convenience.swift"; sourceTree = "<group>"; };
|
||||
B5FAD6AB1B51285300714891 /* Internals.MigrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Internals.MigrationManager.swift; sourceTree = "<group>"; };
|
||||
B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageInterface.swift; sourceTree = "<group>"; };
|
||||
@@ -1430,6 +1440,7 @@
|
||||
B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */,
|
||||
B549F6721E56A92800FBAB2D /* CoreDataNativeType.swift */,
|
||||
B5D339F01E94AF5800C880DE /* CoreStoreStrings.swift */,
|
||||
B5F9C091287849CB007AAD2E /* Swift Concurrency */,
|
||||
B5C795BE25D933C200BDACC1 /* Reactive Programming */,
|
||||
B52FEC722596DB6400368BFB /* SwiftUI */,
|
||||
B5E84EDA1AFF84500064E85B /* Setup */,
|
||||
@@ -1500,6 +1511,7 @@
|
||||
B5B866EC25F4800800335476 /* DataStack.AddStoragePublisher.swift */,
|
||||
B5944EFA25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift */,
|
||||
B5B866DF25E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift */,
|
||||
B5F9C097287850D6007AAD2E /* MigrationProgress.swift */,
|
||||
);
|
||||
name = "Reactive Programming";
|
||||
sourceTree = "<group>";
|
||||
@@ -1679,6 +1691,14 @@
|
||||
name = Internals;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5F9C091287849CB007AAD2E /* Swift Concurrency */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */,
|
||||
);
|
||||
name = "Swift Concurrency";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5FE4DA01C84818B00FA6A91 /* StorageInterfaces */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2052,6 +2072,7 @@
|
||||
B5F1DA8D1B9AA97D007C5CBB /* ImportableObject.swift in Sources */,
|
||||
B56965241B356B820075EE4A /* MigrationResult.swift in Sources */,
|
||||
B5C7958F25D7D18000BDACC1 /* ListState.swift in Sources */,
|
||||
B5F9C098287850D6007AAD2E /* MigrationProgress.swift in Sources */,
|
||||
B5FE4DAC1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */,
|
||||
B5E41EC01EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */,
|
||||
B5A1DAC81F111BFA003CF369 /* KeyPath+Querying.swift in Sources */,
|
||||
@@ -2073,6 +2094,7 @@
|
||||
B50E175723517DE4004F033C /* Differentiable.swift in Sources */,
|
||||
B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
||||
B5E84F231AFF84860064E85B /* ListMonitor.swift in Sources */,
|
||||
B5F9C093287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */,
|
||||
B5BF7FC6234D7E460070E741 /* ObjectSnapshot.swift in Sources */,
|
||||
B5E84EF71AFF846E0064E85B /* UnsafeDataTransaction.swift in Sources */,
|
||||
B56964D41B22FFAD0075EE4A /* DataStack+Migration.swift in Sources */,
|
||||
@@ -2266,6 +2288,7 @@
|
||||
B56923C51EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */,
|
||||
82BA18C91C4BBD5900A0916E /* MigrationType.swift in Sources */,
|
||||
B5D8CA772346EAEE0055D7D1 /* DataStack+DataSources.swift in Sources */,
|
||||
B5F9C099287850D6007AAD2E /* MigrationProgress.swift in Sources */,
|
||||
82BA18D01C4BBD7100A0916E /* Internals.MigrationManager.swift in Sources */,
|
||||
B5DE5231230BDA1300A22534 /* CoreStoreDefaults.swift in Sources */,
|
||||
B56E4ED523CDB54A00E1708C /* FieldProtocol.swift in Sources */,
|
||||
@@ -2287,6 +2310,7 @@
|
||||
82BA18BF1C4BBD5300A0916E /* SectionBy.swift in Sources */,
|
||||
B5D339ED1E9495E500C880DE /* CoreStoreObject+Querying.swift in Sources */,
|
||||
B509D7D423C84E1900F42824 /* Transformable.Required.swift in Sources */,
|
||||
B5F9C094287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */,
|
||||
82BA18AC1C4BBD3100A0916E /* SynchronousDataTransaction.swift in Sources */,
|
||||
B50C3EE623D153EA00B29880 /* Field.Coded.swift in Sources */,
|
||||
82BA18C41C4BBD5300A0916E /* ListMonitor.swift in Sources */,
|
||||
@@ -2480,6 +2504,7 @@
|
||||
B5D8CA792346EAEF0055D7D1 /* DataStack+DataSources.swift in Sources */,
|
||||
B56E4ED723CDB54A00E1708C /* FieldProtocol.swift in Sources */,
|
||||
B56923C71EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */,
|
||||
B5F9C09B287850D6007AAD2E /* MigrationProgress.swift in Sources */,
|
||||
B5944EFE25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift in Sources */,
|
||||
B5DE5233230BDA1300A22534 /* CoreStoreDefaults.swift in Sources */,
|
||||
B52DD1A51BE1F92F00949AFE /* ImportableUniqueObject.swift in Sources */,
|
||||
@@ -2501,6 +2526,7 @@
|
||||
B52F74321E9B50D0005F3DAC /* SchemaHistory.swift in Sources */,
|
||||
B509D7D623C84E1900F42824 /* Transformable.Required.swift in Sources */,
|
||||
B50C3EE823D153EA00B29880 /* Field.Coded.swift in Sources */,
|
||||
B5F9C096287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */,
|
||||
B5D339EF1E9495E500C880DE /* CoreStoreObject+Querying.swift in Sources */,
|
||||
B52DD19F1BE1F92C00949AFE /* SynchronousDataTransaction.swift in Sources */,
|
||||
B52DD1CB1BE1F94600949AFE /* Internals.WeakObject.swift in Sources */,
|
||||
@@ -2694,6 +2720,7 @@
|
||||
B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */,
|
||||
B5D8CA782346EAEF0055D7D1 /* DataStack+DataSources.swift in Sources */,
|
||||
B56923C61EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */,
|
||||
B5F9C09A287850D6007AAD2E /* MigrationProgress.swift in Sources */,
|
||||
B56321A71BD65216006C9394 /* MigrationResult.swift in Sources */,
|
||||
B56E4ED623CDB54A00E1708C /* FieldProtocol.swift in Sources */,
|
||||
B5DE5232230BDA1300A22534 /* CoreStoreDefaults.swift in Sources */,
|
||||
@@ -2715,6 +2742,7 @@
|
||||
B52F74311E9B50D0005F3DAC /* SchemaHistory.swift in Sources */,
|
||||
B563218F1BD65216006C9394 /* ImportableObject.swift in Sources */,
|
||||
B509D7D523C84E1900F42824 /* Transformable.Required.swift in Sources */,
|
||||
B5F9C095287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */,
|
||||
B56321991BD65216006C9394 /* OrderBy.swift in Sources */,
|
||||
B50C3EE723D153EA00B29880 /* Field.Coded.swift in Sources */,
|
||||
B5D339EE1E9495E500C880DE /* CoreStoreObject+Querying.swift in Sources */,
|
||||
|
||||
367
Sources/DataStack+Concurrency.swift
Normal file
367
Sources/DataStack+Concurrency.swift
Normal file
@@ -0,0 +1,367 @@
|
||||
//
|
||||
// DataStack+Concurrency.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2021 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DataStack
|
||||
|
||||
extension DataStack {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Swift concurrency utilities for the `DataStack` are exposed through this namespace
|
||||
*/
|
||||
public var async: DataStack.AsyncNamespace {
|
||||
|
||||
return .init(self)
|
||||
}
|
||||
|
||||
// MARK: - ReactiveNamespace
|
||||
|
||||
/**
|
||||
Swift concurrency for the `DataStack` are exposed through this namespace. Extend this type if you need to add other `async` utilities for `DataStack`.
|
||||
*/
|
||||
public struct AsyncNamespace {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
The `DataStack` instance
|
||||
*/
|
||||
public let base: DataStack
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(_ base: DataStack) {
|
||||
|
||||
self.base = base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DataStack.AsyncNamespace
|
||||
|
||||
extension DataStack.AsyncNamespace {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Swift concurrency extension for `CoreStore.DataStack`'s `addStorage(...)` API. Asynchronously adds a `StorageInterface` to the stack.
|
||||
```
|
||||
let storage = try await dataStack.async.addStorage(
|
||||
InMemoryStore(configuration: "Config1")
|
||||
)
|
||||
```
|
||||
- parameter storage: the storage
|
||||
- returns: The `StorageInterface` instance added to the `DataStack`. Note that the `StorageInterface` event value may not always be the same instance as the parameter argument if a previous `StorageInterface` was already added at the same URL and with the same configuration.
|
||||
- throws: A `CoreStoreError` value indicating the failure reason
|
||||
*/
|
||||
public func addStorage<T: StorageInterface>(
|
||||
_ storage: T
|
||||
) async throws -> T {
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
|
||||
self.base.addStorage(
|
||||
storage,
|
||||
completion: continuation.resume(with:)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Swift concurrency extension for `CoreStore.DataStack`'s `addStorage(...)` API. Asynchronously adds a `LocalStorage` to the stack. Migrations are also initiated by default. The event emits `MigrationProgress` `enum` values.
|
||||
```
|
||||
for try await migrationProgress in dataStack.async.addStorage(
|
||||
SQLiteStore(
|
||||
fileName: "core_data.sqlite",
|
||||
configuration: "Config1"
|
||||
)
|
||||
) {
|
||||
|
||||
print("\(round(migrationProgress.fractionCompleted * 100)) %") // 0.0 ~ 1.0
|
||||
}
|
||||
```
|
||||
- parameter storage: the local storage
|
||||
- returns: An `AsyncThrowingStream` that emits a `MigrationProgress` value with metadata for migration progress. Note that the `LocalStorage` event value may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
|
||||
- throws: A `CoreStoreError` value indicating the failure reason
|
||||
*/
|
||||
public func addStorage<T>(
|
||||
_ storage: T
|
||||
) -> AsyncThrowingStream<MigrationProgress<T>, Swift.Error> {
|
||||
|
||||
return .init(
|
||||
bufferingPolicy: .unbounded,
|
||||
{ continuation in
|
||||
|
||||
var progress: Progress? = nil
|
||||
progress = self.base.addStorage(
|
||||
storage,
|
||||
completion: { result in
|
||||
|
||||
progress?.setProgressHandler(nil)
|
||||
|
||||
switch result {
|
||||
|
||||
case .success(let storage):
|
||||
continuation.yield(
|
||||
.finished(
|
||||
storage: storage,
|
||||
migrationRequired: progress != nil
|
||||
)
|
||||
)
|
||||
continuation.finish()
|
||||
|
||||
case .failure(let error):
|
||||
continuation.finish(
|
||||
throwing: error
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
if let progress = progress {
|
||||
|
||||
progress.setProgressHandler { progress in
|
||||
|
||||
continuation.yield(
|
||||
.migrating(
|
||||
storage: storage,
|
||||
progressObject: progress
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Swift concurrency extension for `CoreStore.DataStack`'s `importObject(...)` API. Creates an `ImportableObject` by importing from the specified import source. The event value will be the object instance correctly associated for the `DataStack`.
|
||||
```
|
||||
let object = try await dataStack.async.importObject(
|
||||
Into<Person>(),
|
||||
source: ["name": "John"]
|
||||
)
|
||||
```
|
||||
- parameter into: an `Into` clause specifying the entity type
|
||||
- parameter source: the object to import values from
|
||||
- returns: The object instance correctly associated for the `DataStack` if the object was imported successfully, or `nil` if the `ImportableObject` ignored the `source`.
|
||||
- throws: A `CoreStoreError` value indicating the failure reason
|
||||
*/
|
||||
public func importObject<O: DynamicObject & ImportableObject>(
|
||||
_ into: Into<O>,
|
||||
source: O.ImportSource
|
||||
) async throws -> O? {
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
|
||||
self.base.perform(
|
||||
asynchronous: { (transaction) -> O? in
|
||||
|
||||
return try transaction.importObject(
|
||||
into,
|
||||
source: source
|
||||
)
|
||||
},
|
||||
success: {
|
||||
|
||||
continuation.resume(
|
||||
with: .success($0.flatMap(self.base.fetchExisting))
|
||||
)
|
||||
},
|
||||
failure: continuation.resume(throwing:)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Swift concurrency extension for `CoreStore.DataStack`'s `importObject(...)` API. Updates an existing `ImportableObject` by importing values from the specified import source. The event value will be the object instance correctly associated for the `DataStack`.
|
||||
```
|
||||
let importedPerson = try await dataStack.async.importObject(
|
||||
existingPerson,
|
||||
source: ["name": "John", "age": 30]
|
||||
)
|
||||
```
|
||||
- parameter object: the object to update
|
||||
- parameter source: the object to import values from
|
||||
- returns: The object instance correctly associated for the `DataStack` if the object was imported successfully, or `nil` if the `ImportableObject` ignored the `source`.
|
||||
- throws: A `CoreStoreError` value indicating the failure reason
|
||||
*/
|
||||
public func importObject<O: DynamicObject & ImportableObject>(
|
||||
_ object: O,
|
||||
source: O.ImportSource
|
||||
) async throws -> O? {
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
|
||||
self.base.perform(
|
||||
asynchronous: { (transaction) -> O? in
|
||||
|
||||
guard let object = transaction.edit(object) else {
|
||||
|
||||
try transaction.cancel()
|
||||
}
|
||||
try transaction.importObject(
|
||||
object,
|
||||
source: source
|
||||
)
|
||||
return object
|
||||
},
|
||||
success: {
|
||||
|
||||
continuation.resume(
|
||||
with: .success($0.flatMap(self.base.fetchExisting))
|
||||
)
|
||||
},
|
||||
failure: continuation.resume(throwing:)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Swift concurrency extension for `CoreStore.DataStack`'s `importUniqueObject(...)` API. Updates an existing `ImportableUniqueObject` or creates a new instance by importing from the specified import source. The event value will be the object instance correctly associated for the `DataStack`.
|
||||
```
|
||||
let person = try await dataStack.async.importUniqueObject(
|
||||
Into<Person>(),
|
||||
source: ["name": "John", "age": 30]
|
||||
)
|
||||
```
|
||||
- parameter into: an `Into` clause specifying the entity type
|
||||
- parameter source: the object to import values from
|
||||
- returns: The object instance correctly associated for the `DataStack` if the object was imported successfully, or `nil` if the `ImportableUniqueObject` ignored the `source`.
|
||||
- throws: A `CoreStoreError` value indicating the failure reason
|
||||
*/
|
||||
public func importUniqueObject<O: DynamicObject & ImportableUniqueObject>(
|
||||
_ into: Into<O>,
|
||||
source: O.ImportSource
|
||||
) async throws -> O? {
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
|
||||
self.base.perform(
|
||||
asynchronous: { (transaction) -> O? in
|
||||
|
||||
return try transaction.importUniqueObject(
|
||||
into,
|
||||
source: source
|
||||
)
|
||||
},
|
||||
success: {
|
||||
|
||||
continuation.resume(
|
||||
with: .success($0.flatMap(self.base.fetchExisting))
|
||||
)
|
||||
},
|
||||
failure: continuation.resume(throwing:)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Swift concurrency extension for `CoreStore.DataStack`'s `importUniqueObjects(...)` API. Updates existing `ImportableUniqueObject`s or creates them by importing from the specified array of import sources. `ImportableUniqueObject` methods are called on the objects in the same order as they are in the `sourceArray`, and are returned in an array with that same order. The event values will be object instances correctly associated for the `DataStack`.
|
||||
```
|
||||
let people = try await dataStack.async.importUniqueObjects(
|
||||
Into<Person>(),
|
||||
sourceArray: [
|
||||
["name": "John"],
|
||||
["name": "Bob"],
|
||||
["name": "Joe"]
|
||||
]
|
||||
)
|
||||
```
|
||||
- Warning: If `sourceArray` contains multiple import sources with same ID, no merging will occur and ONLY THE LAST duplicate will be imported.
|
||||
- parameter into: an `Into` clause specifying the entity type
|
||||
- parameter sourceArray: the array of objects to import values from
|
||||
- parameter preProcess: a closure that lets the caller tweak the internal `UniqueIDType`-to-`ImportSource` mapping to be used for importing. Callers can remove from/add to/update `mapping` and return the updated array from the closure.
|
||||
- returns: The imported objects correctly associated for the `DataStack`.
|
||||
- throws: A `CoreStoreError` value indicating the failure reason
|
||||
*/
|
||||
public func importUniqueObjects<O: DynamicObject & ImportableUniqueObject, S: Sequence>(
|
||||
_ into: Into<O>,
|
||||
sourceArray: S,
|
||||
preProcess: @escaping (_ mapping: [O.UniqueIDType: O.ImportSource]) throws -> [O.UniqueIDType: O.ImportSource] = { $0 }
|
||||
) async throws -> [O]
|
||||
where S.Iterator.Element == O.ImportSource {
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
|
||||
self.base.perform(
|
||||
asynchronous: { (transaction) -> [O] in
|
||||
|
||||
return try transaction.importUniqueObjects(
|
||||
into,
|
||||
sourceArray: sourceArray,
|
||||
preProcess: preProcess
|
||||
)
|
||||
},
|
||||
success: {
|
||||
|
||||
continuation.resume(
|
||||
with: .success(self.base.fetchExisting($0))
|
||||
)
|
||||
},
|
||||
failure: continuation.resume(throwing:)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Swift concurrency extension for `CoreStore.DataStack`'s `perform(asynchronous:...)` API. Performs a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. The event value will be the value returned from the `task` closure. Any errors thrown from inside the `task` will be wrapped in a `CoreStoreError` before being thrown from the `async` method. To cancel/rollback changes, call `transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
```
|
||||
let result = try await dataStack.async.perform(
|
||||
asynchronous: { (transaction) -> (inserted: Set<NSManagedObject>, deleted: Set<NSManagedObject>) in
|
||||
|
||||
// ...
|
||||
return (
|
||||
transaction.insertedObjects(),
|
||||
transaction.deletedObjects()
|
||||
)
|
||||
}
|
||||
)
|
||||
let inserted = dataStack.fetchExisting(result.inserted)
|
||||
let deleted = dataStack.fetchExisting(result.deleted)
|
||||
```
|
||||
- parameter task: the asynchronous closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- returns: The value returned from the `task` closure.
|
||||
- throws: A `CoreStoreError` value indicating the failure reason
|
||||
*/
|
||||
public func perform<Output>(
|
||||
_ asynchronous: @escaping (AsynchronousDataTransaction) throws -> Output
|
||||
) async throws -> Output {
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
|
||||
self.base.perform(
|
||||
asynchronous: asynchronous,
|
||||
completion: continuation.resume(with:)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,9 @@ extension DataStack.ReactiveNamespace {
|
||||
- parameter storage: the storage
|
||||
- returns: A `Future` that emits a `StorageInterface` instance added to the `DataStack`. Note that the `StorageInterface` event value may not always be the same instance as the parameter argument if a previous `StorageInterface` was already added at the same URL and with the same configuration.
|
||||
*/
|
||||
public func addStorage<T: StorageInterface>(_ storage: T) -> Future<T, CoreStoreError> {
|
||||
public func addStorage<T: StorageInterface>(
|
||||
_ storage: T
|
||||
) -> Future<T, CoreStoreError> {
|
||||
|
||||
return .init { (promise) in
|
||||
|
||||
@@ -115,7 +117,7 @@ extension DataStack.ReactiveNamespace {
|
||||
}
|
||||
|
||||
/**
|
||||
Reactive extension for `CoreStore.DataStack`'s `addStorage(...)` API. Asynchronously adds a `LocalStorage` to the stack. Migrations are also initiated by default. The event emits `DataStack.AddStoragePublisher.MigrationProgress` `enum` values.
|
||||
Reactive extension for `CoreStore.DataStack`'s `addStorage(...)` API. Asynchronously adds a `LocalStorage` to the stack. Migrations are also initiated by default. The event emits `MigrationProgress` `enum` values.
|
||||
```
|
||||
dataStack.reactive
|
||||
.addStorage(
|
||||
@@ -135,7 +137,7 @@ extension DataStack.ReactiveNamespace {
|
||||
.store(in: &cancellables)
|
||||
```
|
||||
- parameter storage: the local storage
|
||||
- returns: A `DataStack.AddStoragePublisher` that emits a `DataStack.AddStoragePublisher.MigrationProgress` value with metadata for migration progress. Note that the `LocalStorage` event value may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
|
||||
- returns: A `DataStack.AddStoragePublisher` that emits a `MigrationProgress` value with metadata for migration progress. Note that the `LocalStorage` event value may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
|
||||
*/
|
||||
public func addStorage<T: LocalStorage>(_ storage: T) -> DataStack.AddStoragePublisher<T> {
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ extension DataStack {
|
||||
|
||||
// MARK: Publisher
|
||||
|
||||
public typealias Output = MigrationProgress
|
||||
public typealias Output = CoreStore.MigrationProgress<Storage>
|
||||
public typealias Failure = CoreStoreError
|
||||
|
||||
public func receive<S: Subscriber>(subscriber: S) where S.Input == Output, S.Failure == Failure {
|
||||
@@ -63,56 +63,6 @@ extension DataStack {
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - MigrationProgress
|
||||
|
||||
/**
|
||||
A `MigrationProgress` contains info on a `LocalStorage`'s setup progress.
|
||||
|
||||
- SeeAlso: DataStack.reactive.addStorage(_:)
|
||||
*/
|
||||
public enum MigrationProgress {
|
||||
|
||||
/**
|
||||
The `LocalStorage` is currently being migrated
|
||||
*/
|
||||
case migrating(storage: Storage, progressObject: Progress)
|
||||
|
||||
/**
|
||||
The `LocalStorage` has been added to the `DataStack` and is ready for reading and writing
|
||||
*/
|
||||
case finished(storage: Storage, migrationRequired: Bool)
|
||||
|
||||
/**
|
||||
The fraction of the overall work completed by the migration. Returns a value between 0.0 and 1.0, inclusive.
|
||||
*/
|
||||
public var fractionCompleted: Double {
|
||||
|
||||
switch self {
|
||||
|
||||
case .migrating(_, let progressObject):
|
||||
return progressObject.fractionCompleted
|
||||
|
||||
case .finished:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns `true` if the storage was successfully added to the stack, `false` otherwise.
|
||||
*/
|
||||
public var isCompleted: Bool {
|
||||
|
||||
switch self {
|
||||
|
||||
case .migrating:
|
||||
return false
|
||||
|
||||
case .finished:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - AddStorageSubscriber
|
||||
|
||||
@@ -232,6 +182,12 @@ extension DataStack {
|
||||
private let storage: Storage
|
||||
private var subscriber: S?
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "MigrationProgress")
|
||||
public typealias MigrationProgress = CoreStore.MigrationProgress<Storage>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,11 @@ public final class DataStack: Equatable {
|
||||
- parameter bundle: an optional bundle to load .xcdatamodeld models from. If not specified, the main bundle will be used.
|
||||
- parameter migrationChain: the `MigrationChain` that indicates the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
|
||||
*/
|
||||
public convenience init(xcodeModelName: XcodeDataModelFileName = DataStack.applicationName, bundle: Bundle = Bundle.main, migrationChain: MigrationChain = nil) {
|
||||
public convenience init(
|
||||
xcodeModelName: XcodeDataModelFileName = DataStack.applicationName,
|
||||
bundle: Bundle = Bundle.main,
|
||||
migrationChain: MigrationChain = nil
|
||||
) {
|
||||
|
||||
self.init(
|
||||
schemaHistory: SchemaHistory(
|
||||
@@ -79,7 +83,11 @@ public final class DataStack: Equatable {
|
||||
- parameter otherSchema: a list of other `DynamicSchema` instances that represent present/previous/future model versions, in any order
|
||||
- parameter migrationChain: the `MigrationChain` that indicates the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
|
||||
*/
|
||||
public convenience init(_ schema: DynamicSchema, _ otherSchema: DynamicSchema..., migrationChain: MigrationChain = nil) {
|
||||
public convenience init(
|
||||
_ schema: DynamicSchema,
|
||||
_ otherSchema: DynamicSchema...,
|
||||
migrationChain: MigrationChain = nil
|
||||
) {
|
||||
|
||||
self.init(
|
||||
schemaHistory: SchemaHistory(
|
||||
@@ -108,7 +116,9 @@ public final class DataStack: Equatable {
|
||||
```
|
||||
- parameter schemaHistory: the `SchemaHistory` for the stack
|
||||
*/
|
||||
public required init(schemaHistory: SchemaHistory) {
|
||||
public required init(
|
||||
schemaHistory: SchemaHistory
|
||||
) {
|
||||
|
||||
self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: schemaHistory.rawModel)
|
||||
self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator)
|
||||
@@ -139,7 +149,9 @@ public final class DataStack: Equatable {
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `DataStack`'s model.
|
||||
*/
|
||||
public func entityTypesByName(for type: NSManagedObject.Type) -> [EntityName: NSManagedObject.Type] {
|
||||
public func entityTypesByName(
|
||||
for type: NSManagedObject.Type
|
||||
) -> [EntityName: NSManagedObject.Type] {
|
||||
|
||||
var entityTypesByName: [EntityName: NSManagedObject.Type] = [:]
|
||||
for (entityIdentifier, entityDescription) in self.schemaHistory.entityDescriptionsByEntityIdentifier {
|
||||
@@ -163,7 +175,9 @@ public final class DataStack: Equatable {
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `DataStack`'s model.
|
||||
*/
|
||||
public func entityTypesByName(for type: CoreStoreObject.Type) -> [EntityName: CoreStoreObject.Type] {
|
||||
public func entityTypesByName(
|
||||
for type: CoreStoreObject.Type
|
||||
) -> [EntityName: CoreStoreObject.Type] {
|
||||
|
||||
var entityTypesByName: [EntityName: CoreStoreObject.Type] = [:]
|
||||
for (entityIdentifier, entityDescription) in self.schemaHistory.entityDescriptionsByEntityIdentifier {
|
||||
@@ -191,7 +205,9 @@ public final class DataStack: Equatable {
|
||||
/**
|
||||
Returns the `NSEntityDescription` for the specified `NSManagedObject` subclass.
|
||||
*/
|
||||
public func entityDescription(for type: NSManagedObject.Type) -> NSEntityDescription? {
|
||||
public func entityDescription(
|
||||
for type: NSManagedObject.Type
|
||||
) -> NSEntityDescription? {
|
||||
|
||||
return self.entityDescription(for: Internals.EntityIdentifier(type))
|
||||
}
|
||||
@@ -199,7 +215,9 @@ public final class DataStack: Equatable {
|
||||
/**
|
||||
Returns the `NSEntityDescription` for the specified `CoreStoreObject` subclass.
|
||||
*/
|
||||
public func entityDescription(for type: CoreStoreObject.Type) -> NSEntityDescription? {
|
||||
public func entityDescription(
|
||||
for type: CoreStoreObject.Type
|
||||
) -> NSEntityDescription? {
|
||||
|
||||
return self.entityDescription(for: Internals.EntityIdentifier(type))
|
||||
}
|
||||
@@ -207,7 +225,9 @@ public final class DataStack: Equatable {
|
||||
/**
|
||||
Returns the `NSManagedObjectID` for the specified object URI if it exists in the persistent store.
|
||||
*/
|
||||
public func objectID(forURIRepresentation url: URL) -> NSManagedObjectID? {
|
||||
public func objectID(
|
||||
forURIRepresentation url: URL
|
||||
) -> NSManagedObjectID? {
|
||||
|
||||
return self.coordinator.managedObjectID(forURIRepresentation: url)
|
||||
}
|
||||
@@ -236,7 +256,9 @@ public final class DataStack: Equatable {
|
||||
- returns: the `StorageInterface` added to the stack
|
||||
*/
|
||||
@discardableResult
|
||||
public func addStorageAndWait<T: StorageInterface>(_ storage: T) throws -> T {
|
||||
public func addStorageAndWait<T: StorageInterface>(
|
||||
_ storage: T
|
||||
) throws -> T {
|
||||
|
||||
do {
|
||||
|
||||
@@ -275,7 +297,9 @@ public final class DataStack: Equatable {
|
||||
- returns: the local storage added to the stack. Note that this may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
|
||||
*/
|
||||
@discardableResult
|
||||
public func addStorageAndWait<T: LocalStorage>(_ storage: T) throws -> T {
|
||||
public func addStorageAndWait<T: LocalStorage>(
|
||||
_ storage: T
|
||||
) throws -> T {
|
||||
|
||||
return try self.coordinator.performSynchronously {
|
||||
|
||||
@@ -374,7 +398,9 @@ public final class DataStack: Equatable {
|
||||
Prepares deinitializing the `DataStack` by removing all persistent stores. This is not necessary, but can help silence SQLite warnings when actively releasing and recreating `DataStack`s.
|
||||
- parameter completion: the closure to execute after all persistent stores are removed
|
||||
*/
|
||||
public func unsafeRemoveAllPersistentStores(completion: @escaping () -> Void = {}) {
|
||||
public func unsafeRemoveAllPersistentStores(
|
||||
completion: @escaping () -> Void = {}
|
||||
) {
|
||||
|
||||
let coordinator = self.coordinator
|
||||
coordinator.performAsynchronously {
|
||||
@@ -441,7 +467,7 @@ public final class DataStack: Equatable {
|
||||
internal let mainContext: NSManagedObjectContext
|
||||
internal let schemaHistory: SchemaHistory
|
||||
internal let childTransactionQueue = DispatchQueue.serial("com.coreStore.dataStack.childTransactionQueue", qos: .utility)
|
||||
internal let storeMetadataUpdateQueue = DispatchQueue.concurrent("com.coreStore.persistentStoreBarrierQueue", qos: .userInteractive)
|
||||
internal let storeMetadataLock: NSRecursiveLock = .init()
|
||||
internal let migrationQueue: OperationQueue = Internals.with {
|
||||
|
||||
let migrationQueue = OperationQueue()
|
||||
@@ -452,56 +478,68 @@ public final class DataStack: Equatable {
|
||||
return migrationQueue
|
||||
}
|
||||
|
||||
internal func persistentStoreForStorage(_ storage: StorageInterface) -> NSPersistentStore? {
|
||||
internal func persistentStoreForStorage(
|
||||
_ storage: StorageInterface
|
||||
) -> NSPersistentStore? {
|
||||
|
||||
return self.coordinator.persistentStores
|
||||
.filter { $0.storageInterface === storage }
|
||||
.first
|
||||
}
|
||||
|
||||
internal func persistentStores(for entityIdentifier: Internals.EntityIdentifier) -> [NSPersistentStore]? {
|
||||
|
||||
var returnValue: [NSPersistentStore]? = nil
|
||||
self.storeMetadataUpdateQueue.sync(flags: .barrier) {
|
||||
|
||||
returnValue = self.finalConfigurationsByEntityIdentifier[entityIdentifier]?
|
||||
.map({ self.persistentStoresByFinalConfiguration[$0]! }) ?? []
|
||||
internal func persistentStores(
|
||||
for entityIdentifier: Internals.EntityIdentifier
|
||||
) -> [NSPersistentStore]? {
|
||||
|
||||
self.storeMetadataLock.lock()
|
||||
defer {
|
||||
self.storeMetadataLock.unlock()
|
||||
}
|
||||
return returnValue
|
||||
return self.finalConfigurationsByEntityIdentifier[entityIdentifier]?
|
||||
.map({ self.persistentStoresByFinalConfiguration[$0]! }) ?? []
|
||||
}
|
||||
|
||||
internal func persistentStore(for entityIdentifier: Internals.EntityIdentifier, configuration: ModelConfiguration, inferStoreIfPossible: Bool) -> (store: NSPersistentStore?, isAmbiguous: Bool) {
|
||||
|
||||
return self.storeMetadataUpdateQueue.sync(flags: .barrier) { () -> (store: NSPersistentStore?, isAmbiguous: Bool) in
|
||||
|
||||
let configurationsForEntity = self.finalConfigurationsByEntityIdentifier[entityIdentifier] ?? []
|
||||
if let configuration = configuration {
|
||||
|
||||
if configurationsForEntity.contains(configuration) {
|
||||
|
||||
return (store: self.persistentStoresByFinalConfiguration[configuration], isAmbiguous: false)
|
||||
}
|
||||
else if !inferStoreIfPossible {
|
||||
|
||||
return (store: nil, isAmbiguous: false)
|
||||
}
|
||||
internal func persistentStore(
|
||||
for entityIdentifier: Internals.EntityIdentifier,
|
||||
configuration: ModelConfiguration,
|
||||
inferStoreIfPossible: Bool
|
||||
) -> (store: NSPersistentStore?, isAmbiguous: Bool) {
|
||||
|
||||
self.storeMetadataLock.lock()
|
||||
defer {
|
||||
self.storeMetadataLock.unlock()
|
||||
}
|
||||
let configurationsForEntity = self.finalConfigurationsByEntityIdentifier[entityIdentifier] ?? []
|
||||
if let configuration = configuration {
|
||||
|
||||
if configurationsForEntity.contains(configuration) {
|
||||
|
||||
return (store: self.persistentStoresByFinalConfiguration[configuration], isAmbiguous: false)
|
||||
}
|
||||
|
||||
switch configurationsForEntity.count {
|
||||
|
||||
case 0:
|
||||
else if !inferStoreIfPossible {
|
||||
|
||||
return (store: nil, isAmbiguous: false)
|
||||
|
||||
case 1 where inferStoreIfPossible:
|
||||
return (store: self.persistentStoresByFinalConfiguration[configurationsForEntity.first!], isAmbiguous: false)
|
||||
|
||||
default:
|
||||
return (store: nil, isAmbiguous: true)
|
||||
}
|
||||
}
|
||||
|
||||
switch configurationsForEntity.count {
|
||||
|
||||
case 0:
|
||||
return (store: nil, isAmbiguous: false)
|
||||
|
||||
case 1 where inferStoreIfPossible:
|
||||
return (store: self.persistentStoresByFinalConfiguration[configurationsForEntity.first!], isAmbiguous: false)
|
||||
|
||||
default:
|
||||
return (store: nil, isAmbiguous: true)
|
||||
}
|
||||
}
|
||||
|
||||
internal func createPersistentStoreFromStorage(_ storage: StorageInterface, finalURL: URL?, finalStoreOptions: [AnyHashable: Any]?) throws -> NSPersistentStore {
|
||||
internal func createPersistentStoreFromStorage(
|
||||
_ storage: StorageInterface,
|
||||
finalURL: URL?,
|
||||
finalStoreOptions: [AnyHashable: Any]?
|
||||
) throws -> NSPersistentStore {
|
||||
|
||||
let persistentStore = try self.coordinator.addPersistentStore(
|
||||
ofType: type(of: storage).storeType,
|
||||
@@ -510,8 +548,13 @@ public final class DataStack: Equatable {
|
||||
options: finalStoreOptions
|
||||
)
|
||||
persistentStore.storageInterface = storage
|
||||
|
||||
self.storeMetadataUpdateQueue.async(flags: .barrier) {
|
||||
|
||||
do {
|
||||
|
||||
self.storeMetadataLock.lock()
|
||||
defer {
|
||||
self.storeMetadataLock.unlock()
|
||||
}
|
||||
|
||||
let configurationName = persistentStore.configurationName
|
||||
self.persistentStoresByFinalConfiguration[configurationName] = persistentStore
|
||||
@@ -534,7 +577,9 @@ public final class DataStack: Equatable {
|
||||
return persistentStore
|
||||
}
|
||||
|
||||
internal func entityDescription(for entityIdentifier: Internals.EntityIdentifier) -> NSEntityDescription? {
|
||||
internal func entityDescription(
|
||||
for entityIdentifier: Internals.EntityIdentifier
|
||||
) -> NSEntityDescription? {
|
||||
|
||||
return self.schemaHistory.entityDescriptionsByEntityIdentifier[entityIdentifier]
|
||||
}
|
||||
|
||||
79
Sources/MigrationProgress.swift
Normal file
79
Sources/MigrationProgress.swift
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// MigrationProgress.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2021 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - MigrationProgress
|
||||
|
||||
/**
|
||||
A `MigrationProgress` contains info on a `LocalStorage`'s setup progress.
|
||||
|
||||
- SeeAlso: DataStack.reactive.addStorage(_:)
|
||||
- SeeAlso: DataStack.async.addStorage(_:)
|
||||
*/
|
||||
public enum MigrationProgress<T: LocalStorage> {
|
||||
|
||||
/**
|
||||
The `LocalStorage` is currently being migrated
|
||||
*/
|
||||
case migrating(storage: T, progressObject: Progress)
|
||||
|
||||
/**
|
||||
The `LocalStorage` has been added to the `DataStack` and is ready for reading and writing
|
||||
*/
|
||||
case finished(storage: T, migrationRequired: Bool)
|
||||
|
||||
/**
|
||||
The fraction of the overall work completed by the migration. Returns a value between 0.0 and 1.0, inclusive.
|
||||
*/
|
||||
public var fractionCompleted: Double {
|
||||
|
||||
switch self {
|
||||
|
||||
case .migrating(_, let progressObject):
|
||||
return progressObject.fractionCompleted
|
||||
|
||||
case .finished:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns `true` if the storage was successfully added to the stack, `false` otherwise.
|
||||
*/
|
||||
public var isCompleted: Bool {
|
||||
|
||||
switch self {
|
||||
|
||||
case .migrating:
|
||||
return false
|
||||
|
||||
case .finished:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user