mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-05-31 11:50:42 +02:00
Merge branch 'develop' into prototype/propertyWrappers
# Conflicts: # CoreStoreTests/DynamicModelTests.swift
This commit is contained in:
@@ -30,7 +30,7 @@ import CoreData
|
||||
// MARK: - AsynchronousDataTransaction
|
||||
|
||||
/**
|
||||
The `AsynchronousDataTransaction` provides an interface for `DynamicObject` creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from `DataStack.perform(asynchronous:...)`, or from `CoreStore.perform(synchronous:...)`.
|
||||
The `AsynchronousDataTransaction` provides an interface for `DynamicObject` creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from `DataStack.perform(asynchronous:...)`.
|
||||
*/
|
||||
public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
|
||||
@@ -66,7 +66,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
- parameter into: the `Into` clause indicating the destination `NSManagedObject` or `CoreStoreObject` entity type and the destination configuration
|
||||
- returns: a new `NSManagedObject` or `CoreStoreObject` instance of the specified entity type.
|
||||
*/
|
||||
public override func create<D>(_ into: Into<D>) -> D {
|
||||
public override func create<O>(_ into: Into<O>) -> O {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
@@ -82,7 +82,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
- parameter object: the `NSManagedObject` or `CoreStoreObject` to be edited
|
||||
- returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`.
|
||||
*/
|
||||
public override func edit<D: DynamicObject>(_ object: D?) -> D? {
|
||||
public override func edit<O: DynamicObject>(_ object: O?) -> O? {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
@@ -99,7 +99,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
- parameter objectID: the `NSManagedObjectID` for the object to be edited
|
||||
- returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`.
|
||||
*/
|
||||
public override func edit<D>(_ into: Into<D>, _ objectID: NSManagedObjectID) -> D? {
|
||||
public override func edit<O>(_ into: Into<O>, _ objectID: NSManagedObjectID) -> O? {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
@@ -108,51 +108,50 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
|
||||
return super.edit(into, objectID)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Deletes a specified `NSManagedObject` or `CoreStoreObject`.
|
||||
|
||||
- parameter object: the `NSManagedObject` or `CoreStoreObject` to be deleted
|
||||
Deletes the objects with the specified `NSManagedObjectID`s.
|
||||
|
||||
- parameter objectIDs: the `NSManagedObjectID`s of the objects to delete
|
||||
*/
|
||||
public override func delete<D: DynamicObject>(_ object: D?) {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to delete an entity of type \(Internals.typeName(object)) from an already committed \(Internals.typeName(self))."
|
||||
)
|
||||
|
||||
super.delete(object)
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes the specified `DynamicObject`s.
|
||||
|
||||
- parameter object1: the `DynamicObject` to be deleted
|
||||
- parameter object2: another `DynamicObject` to be deleted
|
||||
- parameter objects: other `DynamicObject`s to be deleted
|
||||
*/
|
||||
public override func delete<D: DynamicObject>(_ object1: D?, _ object2: D?, _ objects: D?...) {
|
||||
|
||||
public override func delete<S: Sequence>(objectIDs: S) where S.Iterator.Element: NSManagedObjectID {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to delete an entities from an already committed \(Internals.typeName(self))."
|
||||
)
|
||||
|
||||
super.delete(([object1, object2] + objects).compactMap { $0 })
|
||||
|
||||
super.delete(objectIDs: objectIDs)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Deletes the specified `DynamicObject`s.
|
||||
|
||||
- parameter objects: the `DynamicObject`s to be deleted
|
||||
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by series of `ObjectRepresentation`s.
|
||||
|
||||
- parameter object: the `ObjectRepresentation` representing an `NSManagedObject` or `CoreStoreObject` to be deleted
|
||||
- parameter objects: other `ObjectRepresentation`s representing `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
||||
*/
|
||||
public override func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: DynamicObject {
|
||||
|
||||
public override func delete<O: ObjectRepresentation>(_ object: O?, _ objects: O?...) {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to delete an entities from an already committed \(Internals.typeName(self))."
|
||||
)
|
||||
|
||||
|
||||
super.delete(([object] + objects).compactMap { $0 })
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by an `ObjectRepresenation`.
|
||||
|
||||
- parameter objects: the `ObjectRepresenation`s representing `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
||||
*/
|
||||
public override func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: ObjectRepresentation {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to delete an entities from an already committed \(Internals.typeName(self))."
|
||||
)
|
||||
|
||||
super.delete(objects)
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,10 @@ import CoreData
|
||||
|
||||
// MARK: - AttributeProtocol
|
||||
|
||||
internal protocol AttributeProtocol: AnyObject {
|
||||
internal protocol AttributeProtocol: PropertyProtocol {
|
||||
|
||||
static var attributeType: NSAttributeType { get }
|
||||
|
||||
var keyPath: KeyPathString { get }
|
||||
var isOptional: Bool { get }
|
||||
var isTransient: Bool { get }
|
||||
var allowsExternalBinaryDataStorage: Bool { get }
|
||||
@@ -44,5 +43,5 @@ internal protocol AttributeProtocol: AnyObject {
|
||||
var rawObject: CoreStoreManagedObject? { get set }
|
||||
var getter: CoreStoreManagedObject.CustomGetter? { get }
|
||||
var setter: CoreStoreManagedObject.CustomSetter? { get }
|
||||
var valueForSnapshot: Any { get }
|
||||
var valueForSnapshot: Any? { get }
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ extension BaseDataTransaction {
|
||||
- throws: an `Error` thrown from any of the `ImportableObject` methods
|
||||
- returns: the created `ImportableObject` instance, or `nil` if the import was ignored
|
||||
*/
|
||||
public func importObject<D: ImportableObject>(
|
||||
_ into: Into<D>,
|
||||
source: D.ImportSource) throws -> D? {
|
||||
public func importObject<O: ImportableObject>(
|
||||
_ into: Into<O>,
|
||||
source: O.ImportSource) throws -> O? {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -65,13 +65,13 @@ extension BaseDataTransaction {
|
||||
/**
|
||||
Updates an existing `ImportableObject` by importing values from the specified import source.
|
||||
|
||||
- parameter object: the `NSManagedObject` to update
|
||||
- parameter object: the `ImportableObject` to update
|
||||
- parameter source: the object to import values from
|
||||
- throws: an `Error` thrown from any of the `ImportableObject` methods
|
||||
*/
|
||||
public func importObject<D: ImportableObject>(
|
||||
_ object: D,
|
||||
source: D.ImportSource) throws {
|
||||
public func importObject<O: ImportableObject>(
|
||||
_ object: O,
|
||||
source: O.ImportSource) throws {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -97,9 +97,9 @@ extension BaseDataTransaction {
|
||||
- throws: an `Error` thrown from any of the `ImportableObject` methods
|
||||
- returns: the array of created `ImportableObject` instances
|
||||
*/
|
||||
public func importObjects<D: ImportableObject, S: Sequence>(
|
||||
_ into: Into<D>,
|
||||
sourceArray: S) throws -> [D] where S.Iterator.Element == D.ImportSource {
|
||||
public func importObjects<O: ImportableObject, S: Sequence>(
|
||||
_ into: Into<O>,
|
||||
sourceArray: S) throws -> [O] where S.Iterator.Element == O.ImportSource {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -108,7 +108,7 @@ extension BaseDataTransaction {
|
||||
|
||||
return try autoreleasepool {
|
||||
|
||||
return try sourceArray.compactMap { (source) -> D? in
|
||||
return try sourceArray.compactMap { (source) -> O? in
|
||||
|
||||
let entityType = into.entityClass
|
||||
guard entityType.shouldInsert(from: source, in: self) else {
|
||||
@@ -133,9 +133,9 @@ extension BaseDataTransaction {
|
||||
- throws: an `Error` thrown from any of the `ImportableUniqueObject` methods
|
||||
- returns: the created/updated `ImportableUniqueObject` instance, or `nil` if the import was ignored
|
||||
*/
|
||||
public func importUniqueObject<D: ImportableUniqueObject>(
|
||||
_ into: Into<D>,
|
||||
source: D.ImportSource) throws -> D? {
|
||||
public func importUniqueObject<O: ImportableUniqueObject>(
|
||||
_ into: Into<O>,
|
||||
source: O.ImportSource) throws -> O? {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -151,7 +151,7 @@ extension BaseDataTransaction {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let object = try self.fetchOne(From(entityType), Where<D>(uniqueIDKeyPath, isEqualTo: uniqueIDValue)) {
|
||||
if let object = try self.fetchOne(From(entityType), Where<O>(uniqueIDKeyPath, isEqualTo: uniqueIDValue)) {
|
||||
|
||||
guard entityType.shouldUpdate(from: source, in: self) else {
|
||||
|
||||
@@ -185,10 +185,10 @@ extension BaseDataTransaction {
|
||||
- throws: an `Error` thrown from any of the `ImportableUniqueObject` methods
|
||||
- returns: the array of created/updated `ImportableUniqueObject` instances
|
||||
*/
|
||||
public func importUniqueObjects<D: ImportableUniqueObject, S: Sequence>(
|
||||
_ into: Into<D>,
|
||||
public func importUniqueObjects<O: ImportableUniqueObject, S: Sequence>(
|
||||
_ into: Into<O>,
|
||||
sourceArray: S,
|
||||
preProcess: @escaping (_ mapping: [D.UniqueIDType: D.ImportSource]) throws -> [D.UniqueIDType: D.ImportSource] = { $0 }) throws -> [D] where S.Iterator.Element == D.ImportSource {
|
||||
preProcess: @escaping (_ mapping: [O.UniqueIDType: O.ImportSource]) throws -> [O.UniqueIDType: O.ImportSource] = { $0 }) throws -> [O] where S.Iterator.Element == O.ImportSource {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -198,10 +198,10 @@ extension BaseDataTransaction {
|
||||
return try autoreleasepool {
|
||||
|
||||
let entityType = into.entityClass
|
||||
var importSourceByID = Dictionary<D.UniqueIDType, D.ImportSource>()
|
||||
var importSourceByID = Dictionary<O.UniqueIDType, O.ImportSource>()
|
||||
let sortedIDs = try autoreleasepool {
|
||||
|
||||
return try sourceArray.compactMap { (source) -> D.UniqueIDType? in
|
||||
return try sourceArray.compactMap { (source) -> O.UniqueIDType? in
|
||||
|
||||
guard let uniqueIDValue = try entityType.uniqueID(from: source, in: self) else {
|
||||
|
||||
@@ -214,13 +214,13 @@ extension BaseDataTransaction {
|
||||
|
||||
importSourceByID = try autoreleasepool { try preProcess(importSourceByID) }
|
||||
|
||||
var existingObjectsByID = Dictionary<D.UniqueIDType, D>()
|
||||
var existingObjectsByID = Dictionary<O.UniqueIDType, O>()
|
||||
try self
|
||||
.fetchAll(From(entityType), Where<D>(entityType.uniqueIDKeyPath, isMemberOf: sortedIDs))
|
||||
.fetchAll(From(entityType), Where<O>(entityType.uniqueIDKeyPath, isMemberOf: sortedIDs))
|
||||
.forEach { existingObjectsByID[$0.uniqueIDValue] = $0 }
|
||||
|
||||
var processedObjectIDs = Set<D.UniqueIDType>()
|
||||
var result = [D]()
|
||||
var processedObjectIDs = Set<O.UniqueIDType>()
|
||||
var result = [O]()
|
||||
|
||||
for objectID in sortedIDs where !processedObjectIDs.contains(objectID) {
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the number of `DynamicObject`s deleted
|
||||
*/
|
||||
@discardableResult
|
||||
public func deleteAll<D>(_ from: From<D>, _ deleteClauses: DeleteClause...) throws -> Int {
|
||||
public func deleteAll<O>(_ from: From<O>, _ deleteClauses: DeleteClause...) throws -> Int {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -56,7 +56,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the number of `DynamicObject`s deleted
|
||||
*/
|
||||
@discardableResult
|
||||
public func deleteAll<D>(_ from: From<D>, _ deleteClauses: [DeleteClause]) throws -> Int {
|
||||
public func deleteAll<O>(_ from: From<O>, _ deleteClauses: [DeleteClause]) throws -> Int {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -93,7 +93,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- parameter object: a reference to the object created/fetched outside the transaction
|
||||
- returns: the `DynamicObject` instance if the object exists in the transaction, or `nil` if not found.
|
||||
*/
|
||||
public func fetchExisting<D: DynamicObject>(_ object: D) -> D? {
|
||||
public func fetchExisting<O: DynamicObject>(_ object: O) -> O? {
|
||||
|
||||
return self.context.fetchExisting(object)
|
||||
}
|
||||
@@ -104,7 +104,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- parameter objectID: the `NSManagedObjectID` for the object
|
||||
- returns: the `DynamicObject` instance if the object exists in the transaction, or `nil` if not found.
|
||||
*/
|
||||
public func fetchExisting<D: DynamicObject>(_ objectID: NSManagedObjectID) -> D? {
|
||||
public func fetchExisting<O: DynamicObject>(_ objectID: NSManagedObjectID) -> O? {
|
||||
|
||||
return self.context.fetchExisting(objectID)
|
||||
}
|
||||
@@ -115,7 +115,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- parameter objects: an array of `DynamicObject`s created/fetched outside the transaction
|
||||
- returns: the `DynamicObject` array for objects that exists in the transaction
|
||||
*/
|
||||
public func fetchExisting<D: DynamicObject, S: Sequence>(_ objects: S) -> [D] where S.Iterator.Element == D {
|
||||
public func fetchExisting<O: DynamicObject, S: Sequence>(_ objects: S) -> [O] where S.Iterator.Element == O {
|
||||
|
||||
return self.context.fetchExisting(objects)
|
||||
}
|
||||
@@ -126,7 +126,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- parameter objectIDs: the `NSManagedObjectID` array for the objects
|
||||
- returns: the `DynamicObject` array for objects that exists in the transaction
|
||||
*/
|
||||
public func fetchExisting<D: DynamicObject, S: Sequence>(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID {
|
||||
public func fetchExisting<O: DynamicObject, S: Sequence>(_ objectIDs: S) -> [O] where S.Iterator.Element == NSManagedObjectID {
|
||||
|
||||
return self.context.fetchExisting(objectIDs)
|
||||
}
|
||||
@@ -139,7 +139,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchOne<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> D? {
|
||||
public func fetchOne<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> O? {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -156,7 +156,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchOne<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> D? {
|
||||
public func fetchOne<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> O? {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -195,7 +195,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchAll<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> [D] {
|
||||
public func fetchAll<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> [O] {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -212,7 +212,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchAll<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> [D] {
|
||||
public func fetchAll<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> [O] {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -251,7 +251,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the number of `DynamicObject`s that satisfy the specified `FetchClause`s
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchCount<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> Int {
|
||||
public func fetchCount<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> Int {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -268,7 +268,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the number of `DynamicObject`s that satisfy the specified `FetchClause`s
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchCount<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> Int {
|
||||
public func fetchCount<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> Int {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -307,7 +307,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> NSManagedObjectID? {
|
||||
public func fetchObjectID<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> NSManagedObjectID? {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -324,7 +324,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> NSManagedObjectID? {
|
||||
public func fetchObjectID<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> NSManagedObjectID? {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -363,7 +363,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> [NSManagedObjectID] {
|
||||
public func fetchObjectIDs<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> [NSManagedObjectID] {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -380,7 +380,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> [NSManagedObjectID] {
|
||||
public func fetchObjectIDs<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> [NSManagedObjectID] {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -425,7 +425,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the result of the the query, or `nil` if no match was found. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: QueryClause...) throws -> U? {
|
||||
public func queryValue<O, U: QueryableAttributeType>(_ from: From<O>, _ selectClause: Select<O, U>, _ queryClauses: QueryClause...) throws -> U? {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -445,7 +445,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the result of the the query, or `nil` if no match was found. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: [QueryClause]) throws -> U? {
|
||||
public func queryValue<O, U: QueryableAttributeType>(_ from: From<O>, _ selectClause: Select<O, U>, _ queryClauses: [QueryClause]) throws -> U? {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -489,7 +489,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: QueryClause...) throws -> [[String: Any]] {
|
||||
public func queryAttributes<O>(_ from: From<O>, _ selectClause: Select<O, NSDictionary>, _ queryClauses: QueryClause...) throws -> [[String: Any]] {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -509,7 +509,7 @@ extension BaseDataTransaction: FetchableSource, QueryableSource {
|
||||
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: [QueryClause]) throws -> [[String: Any]] {
|
||||
public func queryAttributes<O>(_ from: From<O>, _ selectClause: Select<O, NSDictionary>, _ queryClauses: [QueryClause]) throws -> [[String: Any]] {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
|
||||
@@ -50,7 +50,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
- parameter into: the `Into` clause indicating the destination `NSManagedObject` or `CoreStoreObject` entity type and the destination configuration
|
||||
- returns: a new `NSManagedObject` or `CoreStoreObject` instance of the specified entity type.
|
||||
*/
|
||||
public func create<D>(_ into: Into<D>) -> D {
|
||||
public func create<O>(_ into: Into<O>) -> O {
|
||||
|
||||
let entityClass = into.entityClass
|
||||
Internals.assert(
|
||||
@@ -121,7 +121,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
- parameter object: the `NSManagedObject` or `CoreStoreObject` type to be edited
|
||||
- returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`.
|
||||
*/
|
||||
public func edit<D: DynamicObject>(_ object: D?) -> D? {
|
||||
public func edit<O: DynamicObject>(_ object: O?) -> O? {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -141,7 +141,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
- parameter objectID: the `NSManagedObjectID` for the object to be edited
|
||||
- returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`.
|
||||
*/
|
||||
public func edit<D>(_ into: Into<D>, _ objectID: NSManagedObjectID) -> D? {
|
||||
public func edit<O>(_ into: Into<O>, _ objectID: NSManagedObjectID) -> O? {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -154,49 +154,56 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
)
|
||||
return self.fetchExisting(objectID)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Deletes a specified `NSManagedObject` or `CoreStoreObject`.
|
||||
|
||||
- parameter object: the `NSManagedObject` or `CoreStoreObject` to be deleted
|
||||
Deletes the objects with the specified `NSManagedObjectID`s.
|
||||
|
||||
- parameter objectIDs: the `NSManagedObjectID`s of the objects to delete
|
||||
*/
|
||||
public func delete<D: DynamicObject>(_ object: D?) {
|
||||
|
||||
public func delete<S: Sequence>(objectIDs: S) where S.Iterator.Element: NSManagedObjectID {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
"Attempted to delete an entity outside its designated queue."
|
||||
)
|
||||
let context = self.context
|
||||
object
|
||||
.flatMap(context.fetchExisting)
|
||||
.flatMap({ context.delete($0.cs_toRaw()) })
|
||||
objectIDs.forEach {
|
||||
|
||||
context.fetchExisting($0).map(context.delete(_:))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s.
|
||||
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by series of `ObjectRepresentation`s.
|
||||
|
||||
- parameter object1: the `NSManagedObject` or `CoreStoreObject` to be deleted
|
||||
- parameter object2: another `NSManagedObject` or `CoreStoreObject` to be deleted
|
||||
- parameter objects: other `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
||||
- parameter object: the `ObjectRepresentation` representing an `NSManagedObject` or `CoreStoreObject` to be deleted
|
||||
- parameter objects: other `ObjectRepresentation`s representing `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
||||
*/
|
||||
public func delete<D: DynamicObject>(_ object1: D?, _ object2: D?, _ objects: D?...) {
|
||||
public func delete<O: ObjectRepresentation>(_ object: O?, _ objects: O?...) {
|
||||
|
||||
self.delete(([object1, object2] + objects).compactMap { $0 })
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
"Attempted to delete an entity outside its designated queue."
|
||||
)
|
||||
self.delete(([object] + objects).compactMap { $0 })
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s.
|
||||
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by an `ObjectRepresenation`.
|
||||
|
||||
- parameter objects: the `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
||||
- parameter objects: the `ObjectRepresenation`s representing `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
||||
*/
|
||||
public func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: DynamicObject {
|
||||
public func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: ObjectRepresentation {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
"Attempted to delete entities outside their designated queue."
|
||||
)
|
||||
let context = self.context
|
||||
objects.forEach { context.fetchExisting($0).flatMap({ context.delete($0.cs_toRaw()) }) }
|
||||
objects.forEach {
|
||||
|
||||
$0.asEditable(in: self).map({ context.delete($0.cs_toRaw()) })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,10 +224,10 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
/**
|
||||
Returns `true` if the object has any property values changed. This method should not be called after the `commit()` method was called.
|
||||
|
||||
- parameter entity: the `DynamicObject` instance
|
||||
- parameter object: the `DynamicObject` instance
|
||||
- returns: `true` if the object has any property values changed.
|
||||
*/
|
||||
public func objectHasPersistentChangedValues<D: DynamicObject>(_ entity: D) -> Bool {
|
||||
public func objectHasPersistentChangedValues<O: DynamicObject>(_ object: O) -> Bool {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -230,7 +237,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
!self.isCommitted,
|
||||
"Attempted to access inserted objects from an already committed \(Internals.typeName(self))."
|
||||
)
|
||||
return entity.cs_toRaw().hasPersistentChangedValues
|
||||
return object.cs_toRaw().hasPersistentChangedValues
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,7 +246,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
- parameter entity: the `DynamicObject` subclass to filter
|
||||
- returns: a `Set` of pending `DynamicObject`s of the specified type that were inserted to the transaction.
|
||||
*/
|
||||
public func insertedObjects<D: DynamicObject>(_ entity: D.Type) -> Set<D> {
|
||||
public func insertedObjects<O: DynamicObject>(_ entity: O.Type) -> Set<O> {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -276,7 +283,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
- parameter entity: the `DynamicObject` subclass to filter
|
||||
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were inserted to the transaction.
|
||||
*/
|
||||
public func insertedObjectIDs<D: DynamicObject>(_ entity: D.Type) -> Set<NSManagedObjectID> {
|
||||
public func insertedObjectIDs<O: DynamicObject>(_ entity: O.Type) -> Set<NSManagedObjectID> {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -295,7 +302,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
- parameter entity: the `DynamicObject` subclass to filter
|
||||
- returns: a `Set` of pending `DynamicObject`s of the specified type that were updated in the transaction.
|
||||
*/
|
||||
public func updatedObjects<D: DynamicObject>(_ entity: D.Type) -> Set<D> {
|
||||
public func updatedObjects<O: DynamicObject>(_ entity: O.Type) -> Set<O> {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -332,7 +339,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
- parameter entity: the `DynamicObject` subclass to filter
|
||||
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were updated in the transaction.
|
||||
*/
|
||||
public func updatedObjectIDs<D: DynamicObject>(_ entity: D.Type) -> Set<NSManagedObjectID> {
|
||||
public func updatedObjectIDs<O: DynamicObject>(_ entity: O.Type) -> Set<NSManagedObjectID> {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -351,7 +358,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
- parameter entity: the `DynamicObject` subclass to filter
|
||||
- returns: a `Set` of pending `DynamicObject`s of the specified type that were deleted from the transaction.
|
||||
*/
|
||||
public func deletedObjects<D: DynamicObject>(_ entity: D.Type) -> Set<D> {
|
||||
public func deletedObjects<O: DynamicObject>(_ entity: O.Type) -> Set<O> {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
@@ -389,7 +396,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
- parameter entity: the `DynamicObject` subclass to filter
|
||||
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction.
|
||||
*/
|
||||
public func deletedObjectIDs<D: DynamicObject>(_ entity: D.Type) -> Set<NSManagedObjectID> {
|
||||
public func deletedObjectIDs<O: DynamicObject>(_ entity: O.Type) -> Set<NSManagedObjectID> {
|
||||
|
||||
Internals.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
|
||||
@@ -38,7 +38,7 @@ extension CSCoreStore {
|
||||
@objc
|
||||
public static var modelVersion: String {
|
||||
|
||||
return CoreStore.modelVersion
|
||||
return self.defaultStack.modelVersion
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,7 @@ extension CSCoreStore {
|
||||
@objc
|
||||
public static func entityTypesByNameForType(_ type: NSManagedObject.Type) -> [EntityName: NSManagedObject.Type] {
|
||||
|
||||
return CoreStore.entityTypesByName(for: type)
|
||||
return self.defaultStack.bridgeToSwift.entityTypesByName(for: type)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ extension CSCoreStore {
|
||||
@objc
|
||||
public static func entityDescriptionForClass(_ type: NSManagedObject.Type) -> NSEntityDescription? {
|
||||
|
||||
return CoreStore.entityDescription(for: type)
|
||||
return self.defaultStack.bridgeToSwift.entityDescription(for: type)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,7 +65,7 @@ extension CSCoreStore {
|
||||
|
||||
return bridge {
|
||||
|
||||
CoreStore.beginUnsafe()
|
||||
self.defaultStack.bridgeToSwift.beginUnsafe()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ extension CSCoreStore {
|
||||
|
||||
return bridge {
|
||||
|
||||
CoreStore.beginUnsafe(supportsUndo: supportsUndo)
|
||||
self.defaultStack.bridgeToSwift.beginUnsafe(supportsUndo: supportsUndo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,6 @@ extension CSCoreStore {
|
||||
@objc
|
||||
public static func refreshAndMergeAllObjects() {
|
||||
|
||||
CoreStore.refreshAndMergeAllObjects()
|
||||
self.defaultStack.refreshAndMergeAllObjects()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ public final class CSCoreStore: NSObject {
|
||||
@objc
|
||||
public static var defaultStack: CSDataStack {
|
||||
|
||||
get { return Shared.defaultStack.bridgeToObjectiveC }
|
||||
set { Shared.defaultStack = newValue.bridgeToSwift }
|
||||
get { return CoreStoreDefaults.dataStack.bridgeToObjectiveC }
|
||||
set { CoreStoreDefaults.dataStack = newValue.bridgeToSwift }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ extension CSDataStack {
|
||||
Creates a `CSObjectMonitor` for the specified `NSManagedObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `NSManagedObject`.
|
||||
|
||||
- parameter object: the `NSManagedObject` to observe changes from
|
||||
- returns: a `ObjectMonitor` that monitors changes to `object`
|
||||
- returns: an `ObjectMonitor` that monitors changes to `object`
|
||||
*/
|
||||
@objc
|
||||
public func monitorObject(_ object: NSManagedObject) -> CSObjectMonitor {
|
||||
|
||||
@@ -145,7 +145,7 @@ public final class CSFrom: NSObject {
|
||||
|
||||
public let bridgeToSwift: From<NSManagedObject>
|
||||
|
||||
public init<D: NSManagedObject>(_ swiftValue: From<D>) {
|
||||
public init<O: NSManagedObject>(_ swiftValue: From<O>) {
|
||||
|
||||
self.bridgeToSwift = swiftValue.downcast()
|
||||
super.init()
|
||||
@@ -155,7 +155,7 @@ public final class CSFrom: NSObject {
|
||||
|
||||
// MARK: - From
|
||||
|
||||
extension From where D: NSManagedObject {
|
||||
extension From where O: NSManagedObject {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ public final class CSGroupBy: NSObject, CSQueryClause {
|
||||
|
||||
public let bridgeToSwift: GroupBy<NSManagedObject>
|
||||
|
||||
public init<D: NSManagedObject>(_ swiftValue: GroupBy<D>) {
|
||||
public init<O: NSManagedObject>(_ swiftValue: GroupBy<O>) {
|
||||
|
||||
self.bridgeToSwift = swiftValue.downcast()
|
||||
super.init()
|
||||
@@ -114,7 +114,7 @@ public final class CSGroupBy: NSObject, CSQueryClause {
|
||||
|
||||
// MARK: - GroupBy
|
||||
|
||||
extension GroupBy where D: NSManagedObject {
|
||||
extension GroupBy where O: NSManagedObject {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ public final class CSInto: NSObject {
|
||||
|
||||
public let bridgeToSwift: Into<NSManagedObject>
|
||||
|
||||
public required init<D: NSManagedObject>(_ swiftValue: Into<D>) {
|
||||
public required init<O: NSManagedObject>(_ swiftValue: Into<O>) {
|
||||
|
||||
self.bridgeToSwift = swiftValue.downcast()
|
||||
super.init()
|
||||
@@ -122,7 +122,7 @@ public final class CSInto: NSObject {
|
||||
|
||||
// MARK: - Into
|
||||
|
||||
extension Into where D: NSManagedObject {
|
||||
extension Into where O: NSManagedObject {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ public final class CSOrderBy: NSObject, CSFetchClause, CSQueryClause, CSDeleteCl
|
||||
|
||||
public let bridgeToSwift: OrderBy<NSManagedObject>
|
||||
|
||||
public init<D: NSManagedObject>(_ swiftValue: OrderBy<D>) {
|
||||
public init<O: NSManagedObject>(_ swiftValue: OrderBy<O>) {
|
||||
|
||||
self.bridgeToSwift = swiftValue.downcast()
|
||||
super.init()
|
||||
@@ -122,7 +122,7 @@ public final class CSOrderBy: NSObject, CSFetchClause, CSQueryClause, CSDeleteCl
|
||||
|
||||
// MARK: - OrderBy
|
||||
|
||||
extension OrderBy where D: NSManagedObject {
|
||||
extension OrderBy where O: NSManagedObject {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ public final class CSSectionBy: NSObject {
|
||||
|
||||
public let bridgeToSwift: SectionBy<NSManagedObject>
|
||||
|
||||
public init<D>(_ swiftValue: SectionBy<D>) {
|
||||
public init<O>(_ swiftValue: SectionBy<O>) {
|
||||
|
||||
self.bridgeToSwift = swiftValue.downcast()
|
||||
super.init()
|
||||
|
||||
@@ -177,7 +177,7 @@ public final class CSSelectTerm: NSObject {
|
||||
|
||||
public let bridgeToSwift: SelectTerm<NSManagedObject>
|
||||
|
||||
public init<D: NSManagedObject>(_ swiftValue: SelectTerm<D>) {
|
||||
public init<O: NSManagedObject>(_ swiftValue: SelectTerm<O>) {
|
||||
|
||||
self.bridgeToSwift = swiftValue.downcast()
|
||||
super.init()
|
||||
@@ -187,7 +187,7 @@ public final class CSSelectTerm: NSObject {
|
||||
|
||||
// MARK: - SelectTerm
|
||||
|
||||
extension SelectTerm where D: NSManagedObject {
|
||||
extension SelectTerm where O: NSManagedObject {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -383,7 +383,7 @@ public final class CSSelect: NSObject {
|
||||
|
||||
// MARK: CoreStoreObjectiveCType
|
||||
|
||||
public init<D: NSManagedObject, T: QueryableAttributeType>(_ swiftValue: Select<D, T>) {
|
||||
public init<O: NSManagedObject, T: QueryableAttributeType>(_ swiftValue: Select<O, T>) {
|
||||
|
||||
self.attributeType = T.cs_rawAttributeType
|
||||
self.selectTerms = swiftValue.selectTerms.map({ $0.downcast() })
|
||||
@@ -391,7 +391,7 @@ public final class CSSelect: NSObject {
|
||||
super.init()
|
||||
}
|
||||
|
||||
public init<D: NSManagedObject, T>(_ swiftValue: Select<D, T>) {
|
||||
public init<O: NSManagedObject, T>(_ swiftValue: Select<O, T>) {
|
||||
|
||||
self.attributeType = .undefinedAttributeType
|
||||
self.selectTerms = swiftValue.selectTerms.map({ $0.downcast() })
|
||||
@@ -502,7 +502,7 @@ public final class CSSelect: NSObject {
|
||||
|
||||
// MARK: - Select
|
||||
|
||||
extension Select where D: NSManagedObject {
|
||||
extension Select where O: NSManagedObject {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau
|
||||
|
||||
public let bridgeToSwift: Where<NSManagedObject>
|
||||
|
||||
public init<D: NSManagedObject>(_ swiftValue: Where<D>) {
|
||||
public init<O: NSManagedObject>(_ swiftValue: Where<O>) {
|
||||
|
||||
self.bridgeToSwift = swiftValue.downcast()
|
||||
super.init()
|
||||
@@ -161,7 +161,7 @@ public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau
|
||||
|
||||
// MARK: - Where
|
||||
|
||||
extension Where where D: NSManagedObject {
|
||||
extension Where where O: NSManagedObject {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
|
||||
@@ -54,50 +54,6 @@ extension AsynchronousDataTransaction: CustomDebugStringConvertible, CoreStoreDe
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CloudStorageOptions
|
||||
|
||||
extension CloudStorageOptions: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
|
||||
return formattedDebugDescription(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreDebugStringConvertible
|
||||
|
||||
public var coreStoreDumpString: String {
|
||||
|
||||
var flags = [String]()
|
||||
if self.contains(.recreateLocalStoreOnModelMismatch) {
|
||||
|
||||
flags.append(".recreateLocalStoreOnModelMismatch")
|
||||
}
|
||||
if self.contains(.allowSynchronousLightweightMigration) {
|
||||
|
||||
flags.append(".allowSynchronousLightweightMigration")
|
||||
}
|
||||
switch flags.count {
|
||||
|
||||
case 0:
|
||||
return "[.none]"
|
||||
|
||||
case 1:
|
||||
return "[.\(flags[0])]"
|
||||
|
||||
default:
|
||||
var string = "[\n"
|
||||
string.append(flags.joined(separator: ",\n"))
|
||||
string.indent(1)
|
||||
string.append("\n]")
|
||||
return string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CoreStoreError
|
||||
|
||||
extension CoreStoreError: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
@@ -417,15 +373,34 @@ fileprivate struct CoreStoreFetchedSectionInfoWrapper: CoreStoreDebugStringConve
|
||||
var coreStoreDumpString: String {
|
||||
|
||||
return createFormattedString(
|
||||
"\"\(self.sectionInfo.name)\" (", ")",
|
||||
("numberOfObjects", self.sectionInfo.numberOfObjects),
|
||||
("indexTitle", self.sectionInfo.indexTitle as Any)
|
||||
"\"\(self.sectionName)\" (", ")",
|
||||
("numberOfObjects", self.numberOfObjects),
|
||||
("indexTitle", self.sectionIndexTitle as Any)
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
let sectionInfo: NSFetchedResultsSectionInfo
|
||||
fileprivate init(_ sectionInfo: NSFetchedResultsSectionInfo) {
|
||||
|
||||
self.sectionName = sectionInfo.name
|
||||
self.numberOfObjects = sectionInfo.numberOfObjects
|
||||
self.sectionIndexTitle = sectionInfo.indexTitle
|
||||
}
|
||||
|
||||
fileprivate init(_ section: Internals.DiffableDataSourceSnapshot.Section) {
|
||||
|
||||
self.sectionName = section.differenceIdentifier
|
||||
self.numberOfObjects = section.elements.count
|
||||
self.sectionIndexTitle = nil
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let sectionName: String
|
||||
private let sectionIndexTitle: String?
|
||||
private let numberOfObjects: Int
|
||||
}
|
||||
|
||||
@available(macOS 10.12, *)
|
||||
@@ -453,6 +428,56 @@ extension ListMonitor: CustomDebugStringConvertible, CoreStoreDebugStringConvert
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ListPublisher
|
||||
|
||||
@available(macOS 10.12, *)
|
||||
extension ListPublisher: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
|
||||
return formattedDebugDescription(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreDebugStringConvertible
|
||||
|
||||
public var coreStoreDumpString: String {
|
||||
|
||||
return createFormattedString(
|
||||
"(", ")",
|
||||
("snapshot", self.snapshot)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ListSnapshot
|
||||
|
||||
extension ListSnapshot: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
|
||||
return formattedDebugDescription(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreDebugStringConvertible
|
||||
|
||||
public var coreStoreDumpString: String {
|
||||
|
||||
return createFormattedString(
|
||||
"(", ")",
|
||||
("numberOfObjects", self.numberOfItems),
|
||||
("sections", self.diffableSnapshot.sections.map(CoreStoreFetchedSectionInfoWrapper.init))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - LocalStorageOptions
|
||||
|
||||
extension LocalStorageOptions: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
@@ -612,6 +637,56 @@ extension ObjectMonitor: CustomDebugStringConvertible, CoreStoreDebugStringConve
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ObjectPublisher
|
||||
|
||||
extension ObjectPublisher: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
|
||||
return formattedDebugDescription(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreDebugStringConvertible
|
||||
|
||||
public var coreStoreDumpString: String {
|
||||
|
||||
return createFormattedString(
|
||||
"(", ")",
|
||||
("objectID", self.objectID()),
|
||||
("object", self.object as Any)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ObjectSnapshot
|
||||
|
||||
extension ObjectSnapshot: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
|
||||
return formattedDebugDescription(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreDebugStringConvertible
|
||||
|
||||
public var coreStoreDumpString: String {
|
||||
|
||||
return createFormattedString(
|
||||
"(", ")",
|
||||
("objectID", self.objectID()),
|
||||
("dictionaryForValues", self.dictionaryForValues())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - OrderBy
|
||||
|
||||
extension OrderBy: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
@@ -1204,7 +1279,7 @@ extension NSEntityDescription: CoreStoreDebugStringConvertible {
|
||||
|
||||
info.append(("compoundIndexes", self.compoundIndexes))
|
||||
}
|
||||
if #available(macOS 10.11, *) {
|
||||
if #available(macOS 10.11, iOS 9.0, *) {
|
||||
|
||||
info.append(("uniquenessConstraints", self.uniquenessConstraints))
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ extension Internals {
|
||||
@inline(__always)
|
||||
internal static func log(_ level: LogLevel, message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
|
||||
|
||||
Shared.logger.log(
|
||||
CoreStoreDefaults.logger.log(
|
||||
level: level,
|
||||
message: message,
|
||||
fileName: fileName,
|
||||
@@ -47,7 +47,7 @@ extension Internals {
|
||||
@inline(__always)
|
||||
internal static func log(_ error: CoreStoreError, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
|
||||
|
||||
Shared.logger.log(
|
||||
CoreStoreDefaults.logger.log(
|
||||
error: error,
|
||||
message: message,
|
||||
fileName: fileName,
|
||||
@@ -59,7 +59,7 @@ extension Internals {
|
||||
@inline(__always)
|
||||
internal static func assert( _ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
|
||||
|
||||
Shared.logger.assert(
|
||||
CoreStoreDefaults.logger.assert(
|
||||
condition(),
|
||||
message: message(),
|
||||
fileName: fileName,
|
||||
@@ -71,7 +71,7 @@ extension Internals {
|
||||
@inline(__always)
|
||||
internal static func abort(_ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) -> Never {
|
||||
|
||||
Shared.logger.abort(
|
||||
CoreStoreDefaults.logger.abort(
|
||||
message,
|
||||
fileName: fileName,
|
||||
lineNumber: lineNumber,
|
||||
|
||||
@@ -33,9 +33,9 @@ import CoreData
|
||||
extension CoreStore {
|
||||
|
||||
/**
|
||||
Asynchronously adds a `StorageInterface` to the `defaultStack`. Migrations are also initiated by default.
|
||||
Asynchronously adds a `StorageInterface` to the `CoreStoreDefaults.dataStack`. Migrations are also initiated by default.
|
||||
```
|
||||
CoreStore.addStorage(
|
||||
dataStack.addStorage(
|
||||
InMemoryStore(configuration: "Config1"),
|
||||
completion: { result in
|
||||
switch result {
|
||||
@@ -50,13 +50,13 @@ extension CoreStore {
|
||||
*/
|
||||
public static func addStorage<T>(_ storage: T, completion: @escaping (SetupResult<T>) -> Void) {
|
||||
|
||||
Shared.defaultStack.addStorage(storage, completion: completion)
|
||||
CoreStoreDefaults.dataStack.addStorage(storage, completion: completion)
|
||||
}
|
||||
|
||||
/**
|
||||
Asynchronously adds a `LocalStorage` to the `defaultStack`. Migrations are also initiated by default.
|
||||
Asynchronously adds a `LocalStorage` to the `CoreStoreDefaults.dataStack`. Migrations are also initiated by default.
|
||||
```
|
||||
let migrationProgress = CoreStore.addStorage(
|
||||
let migrationProgress = dataStack.addStorage(
|
||||
SQLiteStore(fileName: "core_data.sqlite", configuration: "Config1"),
|
||||
completion: { result in
|
||||
switch result {
|
||||
@@ -72,43 +72,11 @@ extension CoreStore {
|
||||
*/
|
||||
public static func addStorage<T: LocalStorage>(_ storage: T, completion: @escaping (SetupResult<T>) -> Void) -> Progress? {
|
||||
|
||||
return Shared.defaultStack.addStorage(storage, completion: completion)
|
||||
}
|
||||
|
||||
/**
|
||||
Asynchronously adds a `CloudStorage` to the `defaultStack`. Migrations are also initiated by default.
|
||||
```
|
||||
guard let storage = ICloudStore(
|
||||
ubiquitousContentName: "MyAppCloudData",
|
||||
ubiquitousContentTransactionLogsSubdirectory: "logs/config1",
|
||||
ubiquitousContainerID: "iCloud.com.mycompany.myapp.containername",
|
||||
ubiquitousPeerToken: "9614d658014f4151a95d8048fb717cf0",
|
||||
configuration: "Config1",
|
||||
cloudStorageOptions: .recreateLocalStoreOnModelMismatch
|
||||
) else {
|
||||
// iCloud is not available on the device
|
||||
return
|
||||
}
|
||||
let migrationProgress = dataStack.addStorage(
|
||||
storage,
|
||||
completion: { result in
|
||||
switch result {
|
||||
case .success(let storage): // ...
|
||||
case .failure(let error): // ...
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter storage: the cloud storage
|
||||
- parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `SetupResult` argument indicates the result. Note that the `CloudStorage` associated to the `SetupResult.success` may not always be the same instance as the parameter argument if a previous `CloudStorage` was already added at the same URL and with the same configuration.
|
||||
*/
|
||||
public static func addStorage<T: CloudStorage>(_ storage: T, completion: @escaping (SetupResult<T>) -> Void) {
|
||||
|
||||
Shared.defaultStack.addStorage(storage, completion: completion)
|
||||
return CoreStoreDefaults.dataStack.addStorage(storage, completion: completion)
|
||||
}
|
||||
|
||||
/**
|
||||
Migrates a local storage to match the `defaultStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
|
||||
Migrates a local storage to match the `CoreStoreDefaults.dataStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
|
||||
|
||||
- parameter storage: the local storage
|
||||
- parameter completion: the closure to be executed on the main queue when the migration completes, either due to success or failure. The closure's `MigrationResult` argument indicates the result. This closure is NOT executed if an error is thrown, but will be executed with a `.failure` result if an error occurs asynchronously.
|
||||
@@ -117,11 +85,11 @@ extension CoreStore {
|
||||
*/
|
||||
public static func upgradeStorageIfNeeded<T: LocalStorage>(_ storage: T, completion: @escaping (MigrationResult) -> Void) throws -> Progress? {
|
||||
|
||||
return try Shared.defaultStack.upgradeStorageIfNeeded(storage, completion: completion)
|
||||
return try CoreStoreDefaults.dataStack.upgradeStorageIfNeeded(storage, completion: completion)
|
||||
}
|
||||
|
||||
/**
|
||||
Checks the migration steps required for the storage to match the `defaultStack`'s managed object model version.
|
||||
Checks the migration steps required for the storage to match the `CoreStoreDefaults.dataStack`'s managed object model version.
|
||||
|
||||
- parameter storage: the local storage
|
||||
- throws: a `CoreStoreError` value indicating the failure
|
||||
@@ -129,6 +97,6 @@ extension CoreStore {
|
||||
*/
|
||||
public static func requiredMigrationsForStorage<T: LocalStorage>(_ storage: T) throws -> [MigrationType] {
|
||||
|
||||
return try Shared.defaultStack.requiredMigrationsForStorage(storage)
|
||||
return try CoreStoreDefaults.dataStack.requiredMigrationsForStorage(storage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,44 +34,44 @@ import CoreData
|
||||
extension CoreStore {
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, creates an `ObjectMonitor` for the specified `DynamicObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `DynamicObject`.
|
||||
Using the `CoreStoreDefaults.dataStack`, creates an `ObjectMonitor` for the specified `DynamicObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `DynamicObject`.
|
||||
|
||||
- parameter object: the `DynamicObject` to observe changes from
|
||||
- returns: a `ObjectMonitor` that monitors changes to `object`
|
||||
- returns: an `ObjectMonitor` that monitors changes to `object`
|
||||
*/
|
||||
public static func monitorObject<D>(_ object: D) -> ObjectMonitor<D> {
|
||||
public static func monitorObject<O: DynamicObject>(_ object: O) -> ObjectMonitor<O> {
|
||||
|
||||
return Shared.defaultStack.monitorObject(object)
|
||||
return CoreStoreDefaults.dataStack.monitorObject(object)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
Using the `CoreStoreDefaults.dataStack`, creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public static func monitorList<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> ListMonitor<D> {
|
||||
public static func monitorList<O>(_ from: From<O>, _ fetchClauses: FetchClause...) -> ListMonitor<O> {
|
||||
|
||||
return Shared.defaultStack.monitorList(from, fetchClauses)
|
||||
return CoreStoreDefaults.dataStack.monitorList(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
Using the `CoreStoreDefaults.dataStack`, creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public static func monitorList<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> ListMonitor<D> {
|
||||
public static func monitorList<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) -> ListMonitor<O> {
|
||||
|
||||
return Shared.defaultStack.monitorList(from, fetchClauses)
|
||||
return CoreStoreDefaults.dataStack.monitorList(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
```
|
||||
let monitor = CoreStore.monitorList(
|
||||
let monitor = dataStack.monitorList(
|
||||
From<MyPersonEntity>()
|
||||
.where(\.age > 18)
|
||||
.orderBy(.ascending(\.age))
|
||||
@@ -82,38 +82,38 @@ extension CoreStore {
|
||||
*/
|
||||
public static func monitorList<B: FetchChainableBuilderType>(_ clauseChain: B) -> ListMonitor<B.ObjectType> {
|
||||
|
||||
return Shared.defaultStack.monitorList(clauseChain.from, clauseChain.fetchClauses)
|
||||
return CoreStoreDefaults.dataStack.monitorList(clauseChain.from, clauseChain.fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
Using the `CoreStoreDefaults.dataStack`, asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
|
||||
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public static func monitorList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ fetchClauses: FetchClause...) {
|
||||
public static func monitorList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ fetchClauses: FetchClause...) {
|
||||
|
||||
Shared.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
|
||||
CoreStoreDefaults.dataStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
Using the `CoreStoreDefaults.dataStack`, asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
|
||||
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public static func monitorList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ fetchClauses: [FetchClause]) {
|
||||
public static func monitorList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ fetchClauses: [FetchClause]) {
|
||||
|
||||
Shared.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
|
||||
CoreStoreDefaults.dataStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
|
||||
```
|
||||
CoreStore.monitorList(
|
||||
dataStack.monitorList(
|
||||
createAsynchronously: { (monitor) in
|
||||
self.monitor = monitor
|
||||
},
|
||||
@@ -127,7 +127,7 @@ extension CoreStore {
|
||||
*/
|
||||
public static func monitorList<B: FetchChainableBuilderType>(createAsynchronously: @escaping (ListMonitor<B.ObjectType>) -> Void, _ clauseChain: B) {
|
||||
|
||||
Shared.defaultStack.monitorList(
|
||||
CoreStoreDefaults.dataStack.monitorList(
|
||||
createAsynchronously: createAsynchronously,
|
||||
clauseChain.from,
|
||||
clauseChain.fetchClauses
|
||||
@@ -135,35 +135,35 @@ extension CoreStore {
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
Using the `CoreStoreDefaults.dataStack`, creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public static func monitorSectionedList<D>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: FetchClause...) -> ListMonitor<D> {
|
||||
public static func monitorSectionedList<O>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: FetchClause...) -> ListMonitor<O> {
|
||||
|
||||
return Shared.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses)
|
||||
return CoreStoreDefaults.dataStack.monitorSectionedList(from, sectionBy, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
Using the `CoreStoreDefaults.dataStack`, creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public static func monitorSectionedList<D>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: [FetchClause]) -> ListMonitor<D> {
|
||||
public static func monitorSectionedList<O>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: [FetchClause]) -> ListMonitor<O> {
|
||||
|
||||
return Shared.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses)
|
||||
return CoreStoreDefaults.dataStack.monitorSectionedList(from, sectionBy, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses.
|
||||
```
|
||||
let monitor = CoreStore.monitorSectionedList(
|
||||
let monitor = dataStack.monitorSectionedList(
|
||||
From<MyPersonEntity>()
|
||||
.sectionBy(\.age, { "\($0!) years old" })
|
||||
.where(\.age > 18)
|
||||
@@ -175,7 +175,7 @@ extension CoreStore {
|
||||
*/
|
||||
public static func monitorSectionedList<B: SectionMonitorBuilderType>(_ clauseChain: B) -> ListMonitor<B.ObjectType> {
|
||||
|
||||
return Shared.defaultStack.monitorSectionedList(
|
||||
return CoreStoreDefaults.dataStack.monitorSectionedList(
|
||||
clauseChain.from,
|
||||
clauseChain.sectionBy,
|
||||
clauseChain.fetchClauses
|
||||
@@ -183,35 +183,35 @@ extension CoreStore {
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
Using the `CoreStoreDefaults.dataStack`, asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
|
||||
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public static func monitorSectionedList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: FetchClause...) {
|
||||
public static func monitorSectionedList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: FetchClause...) {
|
||||
|
||||
Shared.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
|
||||
CoreStoreDefaults.dataStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
Using the `CoreStoreDefaults.dataStack`, asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
|
||||
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public static func monitorSectionedList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: [FetchClause]) {
|
||||
public static func monitorSectionedList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: [FetchClause]) {
|
||||
|
||||
Shared.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
|
||||
CoreStoreDefaults.dataStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses.
|
||||
```
|
||||
CoreStore.monitorSectionedList(
|
||||
dataStack.monitorSectionedList(
|
||||
createAsynchronously: { (monitor) in
|
||||
self.monitor = monitor
|
||||
},
|
||||
@@ -226,7 +226,7 @@ extension CoreStore {
|
||||
*/
|
||||
public static func monitorSectionedList<B: SectionMonitorBuilderType>(createAsynchronously: @escaping (ListMonitor<B.ObjectType>) -> Void, _ clauseChain: B) {
|
||||
|
||||
Shared.defaultStack.monitorSectionedList(
|
||||
CoreStoreDefaults.dataStack.monitorSectionedList(
|
||||
createAsynchronously: createAsynchronously,
|
||||
clauseChain.from,
|
||||
clauseChain.sectionBy,
|
||||
|
||||
@@ -33,79 +33,79 @@ import CoreData
|
||||
extension CoreStore {
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the `DynamicObject` instance in the `DataStack`'s context from a reference created from a transaction or from a different managed object context.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the `DynamicObject` instance in the `DataStack`'s context from a reference created from a transaction or from a different managed object context.
|
||||
|
||||
- parameter object: a reference to the object created/fetched outside the `DataStack`
|
||||
- returns: the `DynamicObject` instance if the object exists in the `DataStack`, or `nil` if not found.
|
||||
*/
|
||||
public static func fetchExisting<D: DynamicObject>(_ object: D) -> D? {
|
||||
public static func fetchExisting<O: DynamicObject>(_ object: O) -> O? {
|
||||
|
||||
return Shared.defaultStack.fetchExisting(object)
|
||||
return CoreStoreDefaults.dataStack.fetchExisting(object)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the `DynamicObject` instance in the `DataStack`'s context from an `NSManagedObjectID`.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the `DynamicObject` instance in the `DataStack`'s context from an `NSManagedObjectID`.
|
||||
|
||||
- parameter objectID: the `NSManagedObjectID` for the object
|
||||
- returns: the `DynamicObject` instance if the object exists in the `DataStack`, or `nil` if not found.
|
||||
*/
|
||||
public static func fetchExisting<D: DynamicObject>(_ objectID: NSManagedObjectID) -> D? {
|
||||
public static func fetchExisting<O: DynamicObject>(_ objectID: NSManagedObjectID) -> O? {
|
||||
|
||||
return Shared.defaultStack.fetchExisting(objectID)
|
||||
return CoreStoreDefaults.dataStack.fetchExisting(objectID)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the `DynamicObject` instances in the `DataStack`'s context from references created from a transaction or from a different managed object context.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the `DynamicObject` instances in the `DataStack`'s context from references created from a transaction or from a different managed object context.
|
||||
|
||||
- parameter objects: an array of `DynamicObject`s created/fetched outside the `DataStack`
|
||||
- returns: the `DynamicObject` array for objects that exists in the `DataStack`
|
||||
*/
|
||||
public static func fetchExisting<D: DynamicObject, S: Sequence>(_ objects: S) -> [D] where S.Iterator.Element == D {
|
||||
public static func fetchExisting<O: DynamicObject, S: Sequence>(_ objects: S) -> [O] where S.Iterator.Element == O {
|
||||
|
||||
return Shared.defaultStack.fetchExisting(objects)
|
||||
return CoreStoreDefaults.dataStack.fetchExisting(objects)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the `DynamicObject` instances in the `DataStack`'s context from a list of `NSManagedObjectID`.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the `DynamicObject` instances in the `DataStack`'s context from a list of `NSManagedObjectID`.
|
||||
|
||||
- parameter objectIDs: the `NSManagedObjectID` array for the objects
|
||||
- returns: the `DynamicObject` array for objects that exists in the `DataStack`
|
||||
*/
|
||||
public static func fetchExisting<D: DynamicObject, S: Sequence>(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID {
|
||||
public static func fetchExisting<O: DynamicObject, S: Sequence>(_ objectIDs: S) -> [O] where S.Iterator.Element == NSManagedObjectID {
|
||||
|
||||
return Shared.defaultStack.fetchExisting(objectIDs)
|
||||
return CoreStoreDefaults.dataStack.fetchExisting(objectIDs)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func fetchOne<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> D? {
|
||||
public static func fetchOne<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> O? {
|
||||
|
||||
return try Shared.defaultStack.fetchOne(from, fetchClauses)
|
||||
return try CoreStoreDefaults.dataStack.fetchOne(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func fetchOne<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> D? {
|
||||
public static func fetchOne<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> O? {
|
||||
|
||||
return try Shared.defaultStack.fetchOne(from, fetchClauses)
|
||||
return try CoreStoreDefaults.dataStack.fetchOne(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Fetches the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
```
|
||||
let youngestTeen = CoreStore.fetchOne(
|
||||
let youngestTeen = dataStack.fetchOne(
|
||||
From<MyPersonEntity>()
|
||||
.where(\.age > 18)
|
||||
.orderBy(.ascending(\.age))
|
||||
@@ -117,39 +117,39 @@ extension CoreStore {
|
||||
*/
|
||||
public static func fetchOne<B: FetchChainableBuilderType>(_ clauseChain: B) throws -> B.ObjectType? {
|
||||
|
||||
return try Shared.defaultStack.fetchOne(clauseChain)
|
||||
return try CoreStoreDefaults.dataStack.fetchOne(clauseChain)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func fetchAll<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> [D] {
|
||||
public static func fetchAll<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> [O] {
|
||||
|
||||
return try Shared.defaultStack.fetchAll(from, fetchClauses)
|
||||
return try CoreStoreDefaults.dataStack.fetchAll(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func fetchAll<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> [D] {
|
||||
public static func fetchAll<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> [O] {
|
||||
|
||||
return try Shared.defaultStack.fetchAll(from, fetchClauses)
|
||||
return try CoreStoreDefaults.dataStack.fetchAll(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Fetches all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
```
|
||||
let people = CoreStore.fetchAll(
|
||||
let people = dataStack.fetchAll(
|
||||
From<MyPersonEntity>()
|
||||
.where(\.age > 18)
|
||||
.orderBy(.ascending(\.age))
|
||||
@@ -161,39 +161,39 @@ extension CoreStore {
|
||||
*/
|
||||
public static func fetchAll<B: FetchChainableBuilderType>(_ clauseChain: B) throws -> [B.ObjectType] {
|
||||
|
||||
return try Shared.defaultStack.fetchAll(clauseChain)
|
||||
return try CoreStoreDefaults.dataStack.fetchAll(clauseChain)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: the number of `DynamicObject`s that satisfy the specified `FetchClause`s
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func fetchCount<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> Int {
|
||||
public static func fetchCount<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> Int {
|
||||
|
||||
return try Shared.defaultStack.fetchCount(from, fetchClauses)
|
||||
return try CoreStoreDefaults.dataStack.fetchCount(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: the number of `DynamicObject`s that satisfy the specified `FetchClause`s
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func fetchCount<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> Int {
|
||||
public static func fetchCount<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> Int {
|
||||
|
||||
return try Shared.defaultStack.fetchCount(from, fetchClauses)
|
||||
return try CoreStoreDefaults.dataStack.fetchCount(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Fetches the number of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
```
|
||||
let numberOfAdults = CoreStore.fetchCount(
|
||||
let numberOfAdults = dataStack.fetchCount(
|
||||
From<MyPersonEntity>()
|
||||
.where(\.age > 18)
|
||||
.orderBy(.ascending(\.age))
|
||||
@@ -205,39 +205,39 @@ extension CoreStore {
|
||||
*/
|
||||
public static func fetchCount<B: FetchChainableBuilderType>(_ clauseChain: B) throws -> Int {
|
||||
|
||||
return try Shared.defaultStack.fetchCount(clauseChain)
|
||||
return try CoreStoreDefaults.dataStack.fetchCount(clauseChain)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> NSManagedObjectID? {
|
||||
public static func fetchObjectID<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> NSManagedObjectID? {
|
||||
|
||||
return try Shared.defaultStack.fetchObjectID(from, fetchClauses)
|
||||
return try CoreStoreDefaults.dataStack.fetchObjectID(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> NSManagedObjectID? {
|
||||
public static func fetchObjectID<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> NSManagedObjectID? {
|
||||
|
||||
return try Shared.defaultStack.fetchObjectID(from, fetchClauses)
|
||||
return try CoreStoreDefaults.dataStack.fetchObjectID(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
```
|
||||
let youngestTeenID = CoreStore.fetchObjectID(
|
||||
let youngestTeenID = dataStack.fetchObjectID(
|
||||
From<MyPersonEntity>()
|
||||
.where(\.age > 18)
|
||||
.orderBy(.ascending(\.age))
|
||||
@@ -249,33 +249,33 @@ extension CoreStore {
|
||||
*/
|
||||
public static func fetchObjectID<B: FetchChainableBuilderType>(_ clauseChain: B) throws -> NSManagedObjectID? {
|
||||
|
||||
return try Shared.defaultStack.fetchObjectID(clauseChain)
|
||||
return try CoreStoreDefaults.dataStack.fetchObjectID(clauseChain)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> [NSManagedObjectID] {
|
||||
public static func fetchObjectIDs<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> [NSManagedObjectID] {
|
||||
|
||||
return try Shared.defaultStack.fetchObjectIDs(from, fetchClauses)
|
||||
return try CoreStoreDefaults.dataStack.fetchObjectIDs(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> [NSManagedObjectID] {
|
||||
public static func fetchObjectIDs<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> [NSManagedObjectID] {
|
||||
|
||||
return try Shared.defaultStack.fetchObjectIDs(from, fetchClauses)
|
||||
return try CoreStoreDefaults.dataStack.fetchObjectIDs(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,11 +293,11 @@ extension CoreStore {
|
||||
*/
|
||||
public static func fetchObjectIDs<B: FetchChainableBuilderType>(_ clauseChain: B) throws -> [NSManagedObjectID] {
|
||||
|
||||
return try Shared.defaultStack.fetchObjectIDs(clauseChain)
|
||||
return try CoreStoreDefaults.dataStack.fetchObjectIDs(clauseChain)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
|
||||
|
||||
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
|
||||
|
||||
@@ -307,13 +307,13 @@ extension CoreStore {
|
||||
- returns: the result of the the query, or `nil` if no match was found. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: QueryClause...) throws -> U? {
|
||||
public static func queryValue<O, U: QueryableAttributeType>(_ from: From<O>, _ selectClause: Select<O, U>, _ queryClauses: QueryClause...) throws -> U? {
|
||||
|
||||
return try Shared.defaultStack.queryValue(from, selectClause, queryClauses)
|
||||
return try CoreStoreDefaults.dataStack.queryValue(from, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
|
||||
|
||||
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
|
||||
|
||||
@@ -323,9 +323,9 @@ extension CoreStore {
|
||||
- returns: the result of the the query, or `nil` if no match was found. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: [QueryClause]) throws -> U? {
|
||||
public static func queryValue<O, U: QueryableAttributeType>(_ from: From<O>, _ selectClause: Select<O, U>, _ queryClauses: [QueryClause]) throws -> U? {
|
||||
|
||||
return try Shared.defaultStack.queryValue(from, selectClause, queryClauses)
|
||||
return try CoreStoreDefaults.dataStack.queryValue(from, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,7 +333,7 @@ extension CoreStore {
|
||||
|
||||
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
|
||||
```
|
||||
let averageAdultAge = CoreStore.queryValue(
|
||||
let averageAdultAge = dataStack.queryValue(
|
||||
From<MyPersonEntity>()
|
||||
.select(Int.self, .average(\.age))
|
||||
.where(\.age > 18)
|
||||
@@ -345,11 +345,11 @@ extension CoreStore {
|
||||
*/
|
||||
public static func queryValue<B: QueryChainableBuilderType>(_ clauseChain: B) throws -> B.ResultType? where B.ResultType: QueryableAttributeType {
|
||||
|
||||
return try Shared.defaultStack.queryValue(clauseChain)
|
||||
return try CoreStoreDefaults.dataStack.queryValue(clauseChain)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
|
||||
|
||||
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
|
||||
|
||||
@@ -359,13 +359,13 @@ extension CoreStore {
|
||||
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: QueryClause...) throws -> [[String: Any]] {
|
||||
public static func queryAttributes<O>(_ from: From<O>, _ selectClause: Select<O, NSDictionary>, _ queryClauses: QueryClause...) throws -> [[String: Any]] {
|
||||
|
||||
return try Shared.defaultStack.queryAttributes(from, selectClause, queryClauses)
|
||||
return try CoreStoreDefaults.dataStack.queryAttributes(from, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
|
||||
Using the `CoreStoreDefaults.dataStack`, queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
|
||||
|
||||
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
|
||||
|
||||
@@ -375,9 +375,9 @@ extension CoreStore {
|
||||
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public static func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: [QueryClause]) throws -> [[String: Any]] {
|
||||
public static func queryAttributes<O>(_ from: From<O>, _ selectClause: Select<O, NSDictionary>, _ queryClauses: [QueryClause]) throws -> [[String: Any]] {
|
||||
|
||||
return try Shared.defaultStack.queryAttributes(from, selectClause, queryClauses)
|
||||
return try CoreStoreDefaults.dataStack.queryAttributes(from, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,7 +385,7 @@ extension CoreStore {
|
||||
|
||||
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
|
||||
```
|
||||
let results = CoreStore.queryAttributes(
|
||||
let results = dataStack.queryAttributes(
|
||||
From<MyPersonEntity>()
|
||||
.select(
|
||||
NSDictionary.self,
|
||||
@@ -406,6 +406,6 @@ extension CoreStore {
|
||||
*/
|
||||
public static func queryAttributes<B: QueryChainableBuilderType>(_ clauseChain: B) throws -> [[String: Any]] where B.ResultType == NSDictionary {
|
||||
|
||||
return try Shared.defaultStack.queryAttributes(clauseChain)
|
||||
return try CoreStoreDefaults.dataStack.queryAttributes(clauseChain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,111 +33,85 @@ import CoreData
|
||||
extension CoreStore {
|
||||
|
||||
/**
|
||||
Returns the `defaultStack`'s model version. The version string is the same as the name of a version-specific .xcdatamodeld file or `CoreStoreSchema`.
|
||||
Returns the `CoreStoreDefaults.dataStack`'s model version. The version string is the same as the name of a version-specific .xcdatamodeld file or `CoreStoreSchema`.
|
||||
*/
|
||||
public static var modelVersion: String {
|
||||
|
||||
return Shared.defaultStack.modelVersion
|
||||
return CoreStoreDefaults.dataStack.modelVersion
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `defaultStack`'s model.
|
||||
Returns the entity name-to-class type mapping from the `CoreStoreDefaults.dataStack`'s model.
|
||||
*/
|
||||
public static func entityTypesByName(for type: NSManagedObject.Type) -> [EntityName: NSManagedObject.Type] {
|
||||
|
||||
return Shared.defaultStack.entityTypesByName(for: type)
|
||||
return CoreStoreDefaults.dataStack.entityTypesByName(for: type)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `defaultStack`'s model.
|
||||
Returns the entity name-to-class type mapping from the `CoreStoreDefaults.dataStack`'s model.
|
||||
*/
|
||||
public static func entityTypesByName(for type: CoreStoreObject.Type) -> [EntityName: CoreStoreObject.Type] {
|
||||
|
||||
return Shared.defaultStack.entityTypesByName(for: type)
|
||||
return CoreStoreDefaults.dataStack.entityTypesByName(for: type)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `NSEntityDescription` for the specified `NSManagedObject` subclass from `defaultStack`'s model.
|
||||
Returns the `NSEntityDescription` for the specified `NSManagedObject` subclass from `CoreStoreDefaults.dataStack`'s model.
|
||||
*/
|
||||
public static func entityDescription(for type: NSManagedObject.Type) -> NSEntityDescription? {
|
||||
|
||||
return Shared.defaultStack.entityDescription(for: type)
|
||||
return CoreStoreDefaults.dataStack.entityDescription(for: type)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `NSEntityDescription` for the specified `CoreStoreObject` subclass from `defaultStack`'s model.
|
||||
Returns the `NSEntityDescription` for the specified `CoreStoreObject` subclass from `CoreStoreDefaults.dataStack`'s model.
|
||||
*/
|
||||
public static func entityDescription(for type: CoreStoreObject.Type) -> NSEntityDescription? {
|
||||
|
||||
return Shared.defaultStack.entityDescription(for: type)
|
||||
return CoreStoreDefaults.dataStack.entityDescription(for: type)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `SQLiteStore` with default parameters and adds it to the `defaultStack`. This method blocks until completion.
|
||||
Creates an `SQLiteStore` with default parameters and adds it to the `CoreStoreDefaults.dataStack`. This method blocks until completion.
|
||||
```
|
||||
try CoreStore.addStorageAndWait()
|
||||
```
|
||||
- returns: the local SQLite storage added to the `defaultStack`
|
||||
- returns: the local SQLite storage added to the `CoreStoreDefaults.dataStack`
|
||||
*/
|
||||
@discardableResult
|
||||
public static func addStorageAndWait() throws -> SQLiteStore {
|
||||
|
||||
return try Shared.defaultStack.addStorageAndWait(SQLiteStore())
|
||||
return try CoreStoreDefaults.dataStack.addStorageAndWait(SQLiteStore())
|
||||
}
|
||||
|
||||
/**
|
||||
Adds a `StorageInterface` to the `defaultStack` and blocks until completion.
|
||||
Adds a `StorageInterface` to the `CoreStoreDefaults.dataStack` and blocks until completion.
|
||||
```
|
||||
try CoreStore.addStorageAndWait(InMemoryStore(configuration: "Config1"))
|
||||
```
|
||||
- parameter storage: the `StorageInterface`
|
||||
- throws: a `CoreStoreError` value indicating the failure
|
||||
- returns: the `StorageInterface` added to the `defaultStack`
|
||||
- returns: the `StorageInterface` added to the `CoreStoreDefaults.dataStack`
|
||||
*/
|
||||
@discardableResult
|
||||
public static func addStorageAndWait<T: StorageInterface>(_ storage: T) throws -> T {
|
||||
|
||||
return try Shared.defaultStack.addStorageAndWait(storage)
|
||||
return try CoreStoreDefaults.dataStack.addStorageAndWait(storage)
|
||||
}
|
||||
|
||||
/**
|
||||
Adds a `LocalStorage` to the `defaultStack` and blocks until completion.
|
||||
Adds a `LocalStorage` to the `CoreStoreDefaults.dataStack` and blocks until completion.
|
||||
```
|
||||
try CoreStore.addStorageAndWait(SQLiteStore(configuration: "Config1"))
|
||||
```
|
||||
- parameter storage: the local storage
|
||||
- throws: a `CoreStoreError` value indicating the failure
|
||||
- returns: the local storage added to the `defaultStack`. 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.
|
||||
- returns: the local storage added to the `CoreStoreDefaults.dataStack`. 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 static func addStorageAndWait<T: LocalStorage>(_ storage: T) throws -> T {
|
||||
|
||||
return try Shared.defaultStack.addStorageAndWait(storage)
|
||||
}
|
||||
|
||||
/**
|
||||
Adds a `CloudStorage` to the `defaultStack` and blocks until completion.
|
||||
```
|
||||
guard let storage = ICloudStore(
|
||||
ubiquitousContentName: "MyAppCloudData",
|
||||
ubiquitousContentTransactionLogsSubdirectory: "logs/config1",
|
||||
ubiquitousContainerID: "iCloud.com.mycompany.myapp.containername",
|
||||
ubiquitousPeerToken: "9614d658014f4151a95d8048fb717cf0",
|
||||
configuration: "Config1",
|
||||
cloudStorageOptions: .recreateLocalStoreOnModelMismatch
|
||||
) else {
|
||||
// iCloud is not available on the device
|
||||
return
|
||||
}
|
||||
try CoreStore.addStorageAndWait(storage)
|
||||
```
|
||||
- parameter storage: the local storage
|
||||
- throws: a `CoreStoreError` value indicating the failure
|
||||
- returns: the cloud storage added to the stack. Note that this may not always be the same instance as the parameter argument if a previous `CloudStorage` was already added at the same URL and with the same configuration.
|
||||
*/
|
||||
@discardableResult
|
||||
public static func addStorageAndWait<T: CloudStorage>(_ storage: T) throws -> T {
|
||||
|
||||
return try Shared.defaultStack.addStorageAndWait(storage)
|
||||
return try CoreStoreDefaults.dataStack.addStorageAndWait(storage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,18 +32,18 @@ import Foundation
|
||||
extension CoreStore {
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, performs a transaction asynchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the wrapped as `.success(T)` in the `completion`'s `Result<T>`. Any errors thrown from inside the `task` will be reported as `.failure(CoreStoreError)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
Using the `CoreStoreDefaults.dataStack`, performs a transaction asynchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the wrapped as `.success(T)` in the `completion`'s `Result<T>`. Any errors thrown from inside the `task` will be reported as `.failure(CoreStoreError)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- 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`.
|
||||
- parameter completion: the closure executed after the save completes. The `Result` argument of the closure will either wrap the return value of `task`, or any uncaught errors thrown from within `task`. Cancelled `task`s will be indicated by `.failure(error: CoreStoreError.userCancelled)`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
||||
*/
|
||||
public static func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, completion: @escaping (AsynchronousDataTransaction.Result<T>) -> Void) {
|
||||
|
||||
Shared.defaultStack.perform(asynchronous: task, completion: completion)
|
||||
CoreStoreDefaults.dataStack.perform(asynchronous: task, completion: completion)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, performs a transaction asynchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the argument of the `success` closure. Any errors thrown from inside the `task` will be wrapped in a `CoreStoreError` and reported in the `failure` closure. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
Using the `CoreStoreDefaults.dataStack`, performs a transaction asynchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the argument of the `success` closure. Any errors thrown from inside the `task` will be wrapped in a `CoreStoreError` and reported in the `failure` closure. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- 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`.
|
||||
- parameter success: the closure executed after the save succeeds. The `T` argument of the closure will be the value returned from `task`.
|
||||
@@ -51,11 +51,11 @@ extension CoreStore {
|
||||
*/
|
||||
public static func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, success: @escaping (T) -> Void, failure: @escaping (CoreStoreError) -> Void) {
|
||||
|
||||
Shared.defaultStack.perform(asynchronous: task, success: success, failure: failure)
|
||||
CoreStoreDefaults.dataStack.perform(asynchronous: task, success: success, failure: failure)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, performs a transaction synchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be thrown from `perform(synchronous:)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
Using the `CoreStoreDefaults.dataStack`, performs a transaction synchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be thrown from `perform(synchronous:)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- parameter task: the synchronous non-escaping 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`.
|
||||
- parameter waitForAllObservers: When `true`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks. When `false`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning. Defaults to `true`.
|
||||
@@ -64,25 +64,25 @@ extension CoreStore {
|
||||
*/
|
||||
public static func perform<T>(synchronous task: ((_ transaction: SynchronousDataTransaction) throws -> T), waitForAllObservers: Bool = true) throws -> T {
|
||||
|
||||
return try Shared.defaultStack.perform(synchronous: task, waitForAllObservers: waitForAllObservers)
|
||||
return try CoreStoreDefaults.dataStack.perform(synchronous: task, waitForAllObservers: waitForAllObservers)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, begins a non-contiguous transaction where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms.
|
||||
Using the `CoreStoreDefaults.dataStack`, begins a non-contiguous transaction where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms.
|
||||
|
||||
- prameter supportsUndo: `undo()`, `redo()`, and `rollback()` methods are only available when this parameter is `true`, otherwise those method will raise an exception. Defaults to `false`. Note that turning on Undo support may heavily impact performance especially on iOS or watchOS where memory is limited.
|
||||
- returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
|
||||
*/
|
||||
public static func beginUnsafe(supportsUndo: Bool = false) -> UnsafeDataTransaction {
|
||||
|
||||
return Shared.defaultStack.beginUnsafe(supportsUndo: supportsUndo)
|
||||
return CoreStoreDefaults.dataStack.beginUnsafe(supportsUndo: supportsUndo)
|
||||
}
|
||||
|
||||
/**
|
||||
Refreshes all registered objects `NSManagedObject`s or `CoreStoreObject`s in the `defaultStack`.
|
||||
Refreshes all registered objects `NSManagedObject`s or `CoreStoreObject`s in the `CoreStoreDefaults.dataStack`.
|
||||
*/
|
||||
public static func refreshAndMergeAllObjects() {
|
||||
|
||||
Shared.defaultStack.refreshAndMergeAllObjects()
|
||||
CoreStoreDefaults.dataStack.refreshAndMergeAllObjects()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,17 +34,17 @@ import CoreData
|
||||
@available(*, deprecated, message: "Call methods directly from the DataStack instead")
|
||||
public enum CoreStore {
|
||||
|
||||
@available(*, unavailable, renamed: "Shared.logger")
|
||||
@available(*, unavailable, renamed: "CoreStoreDefaults.logger")
|
||||
public static var logger: CoreStoreLogger {
|
||||
|
||||
get { return Shared.logger }
|
||||
set { Shared.logger = newValue }
|
||||
get { return CoreStoreDefaults.logger }
|
||||
set { CoreStoreDefaults.logger = newValue }
|
||||
}
|
||||
|
||||
@available(*, unavailable, renamed: "Shared.defaultStack")
|
||||
@available(*, unavailable, renamed: "CoreStoreDefaults.dataStack")
|
||||
public static var defaultStack: DataStack {
|
||||
|
||||
get { return Shared.defaultStack }
|
||||
set { Shared.defaultStack = newValue }
|
||||
get { return CoreStoreDefaults.dataStack }
|
||||
set { CoreStoreDefaults.dataStack = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Shared.swift
|
||||
// CoreStoreDefaults.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
@@ -26,20 +26,25 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - Shared
|
||||
// MARK: - CoreStoreDefaults
|
||||
|
||||
/**
|
||||
Global utilities
|
||||
*/
|
||||
public enum Shared {
|
||||
public enum CoreStoreDefaults {
|
||||
|
||||
/**
|
||||
The `CoreStoreLogger` instance to be used. The default logger is an instance of a `DefaultLogger`.
|
||||
*/
|
||||
public static var logger: CoreStoreLogger = DefaultLogger()
|
||||
|
||||
@available(*, deprecated, message: "Call methods directly from the DataStack instead")
|
||||
public static var defaultStack: DataStack {
|
||||
|
||||
/**
|
||||
The default `DataStack` instance to be used. If `defaultStack` is not set during the first time accessed, a default-configured `DataStack` will be created.
|
||||
- SeeAlso: `DataStack`
|
||||
- Note: Changing `dataStack` is thread safe, but it is recommended to setup `DataStacks` on a common queue (e.g. the main queue).
|
||||
- Important: If `dataStack` is not set during the first time accessed, a default-configured `DataStack` will be created.
|
||||
*/
|
||||
public static var dataStack: DataStack {
|
||||
|
||||
get {
|
||||
|
||||
@@ -43,7 +43,7 @@ public enum LogLevel {
|
||||
// MARK: - CoreStoreLogger
|
||||
|
||||
/**
|
||||
Custom loggers should implement the `CoreStoreLogger` protocol and pass its instance to `CoreStore.logger`. Calls to `log(...)`, `assert(...)`, and `abort(...)` are not tied to a specific queue/thread, so it is the implementer's job to handle thread-safety.
|
||||
Custom loggers should implement the `CoreStoreLogger` protocol and pass its instance to `CoreStoreDefaults.logger`. Calls to `log(...)`, `assert(...)`, and `abort(...)` are not tied to a specific queue/thread, so it is the implementer's job to handle thread-safety.
|
||||
*/
|
||||
public protocol CoreStoreLogger {
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// CoreStoreObject+DataSources.swift
|
||||
// CoreStore iOS
|
||||
//
|
||||
// Created by John Estropia on 2019/10/04.
|
||||
// Copyright © 2019 John Rommel Estropia. All rights reserved.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) || canImport(AppKit)
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
// MARK: - ListPublisher: ObservableObject
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
extension CoreStoreObject: ObservableObject {
|
||||
|
||||
// MARK: ObservableObject
|
||||
|
||||
public var objectWillChange: ObservableObjectPublisher {
|
||||
|
||||
return self.cs_toRaw().objectWillChange
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -33,7 +33,7 @@ extension ValueContainer.Required {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.nickname == "John" }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.nickname == "John" }))
|
||||
```
|
||||
*/
|
||||
public static func == (_ attribute: ValueContainer<O>.Required<V>, _ value: V) -> Where<O> {
|
||||
@@ -44,7 +44,7 @@ extension ValueContainer.Required {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.nickname != "John" }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.nickname != "John" }))
|
||||
```
|
||||
*/
|
||||
public static func != (_ attribute: ValueContainer<O>.Required<V>, _ value: V) -> Where<O> {
|
||||
@@ -55,7 +55,7 @@ extension ValueContainer.Required {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.age < 20 }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.age < 20 }))
|
||||
```
|
||||
*/
|
||||
public static func < (_ attribute: ValueContainer<O>.Required<V>, _ value: V) -> Where<O> {
|
||||
@@ -66,7 +66,7 @@ extension ValueContainer.Required {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.age > 20 }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.age > 20 }))
|
||||
```
|
||||
*/
|
||||
public static func > (_ attribute: ValueContainer<O>.Required<V>, _ value: V) -> Where<O> {
|
||||
@@ -77,7 +77,7 @@ extension ValueContainer.Required {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.age <= 20 }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.age <= 20 }))
|
||||
```
|
||||
*/
|
||||
public static func <= (_ attribute: ValueContainer<O>.Required<V>, _ value: V) -> Where<O> {
|
||||
@@ -88,7 +88,7 @@ extension ValueContainer.Required {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.age >= 20 }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.age >= 20 }))
|
||||
```
|
||||
*/
|
||||
public static func >= (_ attribute: ValueContainer<O>.Required<V>, _ value: V) -> Where<O> {
|
||||
@@ -99,7 +99,7 @@ extension ValueContainer.Required {
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains the value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where({ ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname }))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where({ ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname }))
|
||||
```
|
||||
*/
|
||||
public static func ~= <S: Sequence>(_ sequence: S, _ attribute: ValueContainer<O>.Required<V>) -> Where<O> where S.Iterator.Element == V {
|
||||
@@ -116,7 +116,7 @@ extension ValueContainer.Optional {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.nickname == "John" }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.nickname == "John" }))
|
||||
```
|
||||
*/
|
||||
public static func == (_ attribute: ValueContainer<O>.Optional<V>, _ value: V?) -> Where<O> {
|
||||
@@ -127,7 +127,7 @@ extension ValueContainer.Optional {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.nickname != "John" }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.nickname != "John" }))
|
||||
```
|
||||
*/
|
||||
public static func != (_ attribute: ValueContainer<O>.Optional<V>, _ value: V?) -> Where<O> {
|
||||
@@ -138,7 +138,7 @@ extension ValueContainer.Optional {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.age < 20 }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.age < 20 }))
|
||||
```
|
||||
*/
|
||||
public static func < (_ attribute: ValueContainer<O>.Optional<V>, _ value: V?) -> Where<O> {
|
||||
@@ -156,7 +156,7 @@ extension ValueContainer.Optional {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.age > 20 }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.age > 20 }))
|
||||
```
|
||||
*/
|
||||
public static func > (_ attribute: ValueContainer<O>.Optional<V>, _ value: V?) -> Where<O> {
|
||||
@@ -174,7 +174,7 @@ extension ValueContainer.Optional {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.age <= 20 }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.age <= 20 }))
|
||||
```
|
||||
*/
|
||||
public static func <= (_ attribute: ValueContainer<O>.Optional<V>, _ value: V?) -> Where<O> {
|
||||
@@ -192,7 +192,7 @@ extension ValueContainer.Optional {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where({ $0.age >= 20 }))
|
||||
let person = dataStack.fetchOne(From<Person>().where({ $0.age >= 20 }))
|
||||
```
|
||||
*/
|
||||
public static func >= (_ attribute: ValueContainer<O>.Optional<V>, _ value: V?) -> Where<O> {
|
||||
@@ -210,7 +210,7 @@ extension ValueContainer.Optional {
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains the value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where({ ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname }))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where({ ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname }))
|
||||
```
|
||||
*/
|
||||
public static func ~= <S: Sequence>(_ sequence: S, _ attribute: ValueContainer<O>.Optional<V>) -> Where<O> where S.Iterator.Element == V {
|
||||
@@ -227,7 +227,7 @@ extension RelationshipContainer.ToOne {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where({ $0.master == me }))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where({ $0.master == me }))
|
||||
```
|
||||
*/
|
||||
public static func == (_ relationship: RelationshipContainer<O>.ToOne<D>, _ object: D?) -> Where<O> {
|
||||
@@ -238,7 +238,7 @@ extension RelationshipContainer.ToOne {
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where({ $0.master != me }))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where({ $0.master != me }))
|
||||
```
|
||||
*/
|
||||
public static func != (_ relationship: RelationshipContainer<O>.ToOne<D>, _ object: D?) -> Where<O> {
|
||||
@@ -249,7 +249,7 @@ extension RelationshipContainer.ToOne {
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains the value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where({ [john, joe, bob] ~= $0.master }))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where({ [john, joe, bob] ~= $0.master }))
|
||||
```
|
||||
*/
|
||||
public static func ~= <S: Sequence>(_ sequence: S, _ relationship: RelationshipContainer<O>.ToOne<D>) -> Where<O> where S.Iterator.Element == D {
|
||||
|
||||
@@ -45,7 +45,7 @@ import Foundation
|
||||
```
|
||||
`CoreStoreObject` entities for a model version should be added to `CoreStoreSchema` instance.
|
||||
```
|
||||
CoreStore.defaultStack = DataStack(
|
||||
CoreStoreDefaults.dataStack = DataStack(
|
||||
CoreStoreSchema(
|
||||
modelVersion: "V1",
|
||||
entities: [
|
||||
@@ -63,7 +63,7 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
||||
|
||||
/**
|
||||
Do not call this directly. This is exposed as public only as a required initializer.
|
||||
- Important: subclasses that need a custom initializer should override both `init(_:)` and `init(asMeta:)`, and to call their corresponding super implementations.
|
||||
- Important: subclasses that need a custom initializer should override both `init(rawObject:)` and `init(asMeta:)`, and to call their corresponding super implementations.
|
||||
*/
|
||||
public required init(rawObject: NSManagedObject) {
|
||||
|
||||
@@ -77,7 +77,7 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
||||
|
||||
/**
|
||||
Do not call this directly. This is exposed as public only as a required initializer.
|
||||
- Important: subclasses that need a custom initializer should override both `init(_:)` and `init(asMeta:)`, and to call their corresponding super implementations.
|
||||
- Important: subclasses that need a custom initializer should override both `init(rawObject:)` and `init(asMeta:)`, and to call their corresponding super implementations.
|
||||
*/
|
||||
public required init(asMeta: Void) {
|
||||
|
||||
@@ -116,6 +116,27 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
||||
|
||||
internal let rawObject: CoreStoreManagedObject?
|
||||
internal let isMeta: Bool
|
||||
|
||||
internal class func metaProperties(includeSuperclasses: Bool) -> [PropertyProtocol] {
|
||||
|
||||
func keyPaths(_ allKeyPaths: inout [PropertyProtocol], for dynamicType: CoreStoreObject.Type) {
|
||||
|
||||
allKeyPaths.append(contentsOf: dynamicType.meta.propertyProtocolsByName())
|
||||
guard
|
||||
includeSuperclasses,
|
||||
case let superType as CoreStoreObject.Type = (dynamicType as AnyClass).superclass(),
|
||||
superType != CoreStoreObject.self
|
||||
else {
|
||||
|
||||
return
|
||||
}
|
||||
keyPaths(&allKeyPaths, for: superType)
|
||||
}
|
||||
|
||||
var allKeyPaths: [PropertyProtocol] = []
|
||||
keyPaths(&allKeyPaths, for: self)
|
||||
return allKeyPaths
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
@@ -144,6 +165,22 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func propertyProtocolsByName() -> [PropertyProtocol] {
|
||||
|
||||
Internals.assert(self.isMeta, "'propertyProtocolsByName()' accessed from non-meta instance of \(Internals.typeName(self))")
|
||||
|
||||
let cacheKey = ObjectIdentifier(Self.self)
|
||||
if let properties = Static.propertiesCache[cacheKey] {
|
||||
|
||||
return properties
|
||||
}
|
||||
let values: [PropertyProtocol] = Mirror(reflecting: self)
|
||||
.children
|
||||
.compactMap({ $0.value as? PropertyProtocol })
|
||||
Static.propertiesCache[cacheKey] = values
|
||||
return values
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,4 +224,5 @@ fileprivate enum Static {
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate static var metaCache: [ObjectIdentifier: Any] = [:]
|
||||
fileprivate static var propertiesCache: [ObjectIdentifier: [PropertyProtocol]] = [:]
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ import Foundation
|
||||
let pet = Relationship.ToOne<Animal>("pet", inverse: { $0.master })
|
||||
}
|
||||
|
||||
CoreStore.defaultStack = DataStack(
|
||||
CoreStoreDefaults.dataStack = DataStack(
|
||||
CoreStoreSchema(
|
||||
modelVersion: "V1",
|
||||
entities: [
|
||||
@@ -76,7 +76,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
let pet = Relationship.ToOne<Animal>("pet", inverse: { $0.master })
|
||||
}
|
||||
|
||||
CoreStore.defaultStack = DataStack(
|
||||
CoreStoreDefaults.dataStack = DataStack(
|
||||
CoreStoreSchema(
|
||||
modelVersion: "V1",
|
||||
entities: [
|
||||
@@ -120,7 +120,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
let name = Value.Required<String>("name", initial: "")
|
||||
}
|
||||
|
||||
CoreStore.defaultStack = DataStack(
|
||||
CoreStoreDefaults.dataStack = DataStack(
|
||||
CoreStoreSchema(
|
||||
modelVersion: "V1",
|
||||
entityConfigurations: [
|
||||
@@ -283,9 +283,9 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
|
||||
|
||||
var propertyDescriptions: [NSPropertyDescription] = []
|
||||
for child in Mirror(reflecting: type.meta).children {
|
||||
for property in type.metaProperties(includeSuperclasses: false) {
|
||||
|
||||
switch child.value {
|
||||
switch property {
|
||||
|
||||
case let attribute as AttributeProtocol:
|
||||
Internals.assert(
|
||||
@@ -378,9 +378,10 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
for (entity, entityDescription) in entityDescriptionsByEntity {
|
||||
|
||||
let relationshipsByName = relationshipsByNameByEntity[entity]!
|
||||
for child in Mirror(reflecting: (entity.type as! CoreStoreObject.Type).meta).children {
|
||||
let entityType = entity.type as! CoreStoreObject.Type
|
||||
for property in entityType.metaProperties(includeSuperclasses: false) {
|
||||
|
||||
switch child.value {
|
||||
switch property {
|
||||
|
||||
case let relationship as RelationshipProtocol:
|
||||
let (destinationType, destinationKeyPath) = relationship.inverse
|
||||
@@ -451,7 +452,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
}
|
||||
for (entity, entityDescription) in entityDescriptionsByEntity {
|
||||
|
||||
if #available(macOS 10.11, *) {
|
||||
if #available(macOS 10.11, iOS 9.0, *) {
|
||||
|
||||
let uniqueConstraints = entity.uniqueConstraints.filter({ !$0.isEmpty })
|
||||
if !uniqueConstraints.isEmpty {
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
//
|
||||
// DataStack+DataSources.swift
|
||||
// CoreStore iOS
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) || canImport(AppKit)
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DataStack
|
||||
|
||||
extension DataStack {
|
||||
|
||||
/**
|
||||
Creates an `ObjectPublisher` for the specified `DynamicObject`. Multiple objects may then register themselves to be notified when changes are made to the `DynamicObject`.
|
||||
|
||||
- parameter object: the `DynamicObject` to observe changes from
|
||||
- returns: an `ObjectPublisher` that broadcasts changes to `object`
|
||||
*/
|
||||
public func publishObject<O: DynamicObject>(_ object: O) -> ObjectPublisher<O> {
|
||||
|
||||
return self.publishObject(object.cs_id())
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `ObjectPublisher` for a `DynamicObject` with the specified `ObjectID`. Multiple objects may then register themselves to be notified when changes are made to the `DynamicObject`.
|
||||
|
||||
- parameter objectID: the `ObjectID` of the object to observe changes from
|
||||
- returns: an `ObjectPublisher` that broadcasts changes to `object`
|
||||
*/
|
||||
public func publishObject<O: DynamicObject>(_ objectID: O.ObjectID) -> ObjectPublisher<O> {
|
||||
|
||||
let context = self.unsafeContext()
|
||||
return context.objectPublisher(objectID: objectID)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListPublisher` for the specified `From` and `FetchClause`s. Multiple objects may then register themselves to be notified when changes are made to the fetched results.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListPublisher` that broadcasts changes to the fetched results
|
||||
*/
|
||||
public func publishList<O>(_ from: From<O>, _ fetchClauses: FetchClause...) -> ListPublisher<O> {
|
||||
|
||||
return self.publishList(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListPublisher` for the specified `From` and `FetchClause`s. Multiple objects may then register themselves to be notified when changes are made to the fetched results.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListPublisher` that broadcasts changes to the fetched results
|
||||
*/
|
||||
public func publishList<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) -> ListPublisher<O> {
|
||||
|
||||
return ListPublisher(
|
||||
dataStack: self,
|
||||
from: from,
|
||||
sectionBy: nil,
|
||||
applyFetchClauses: { fetchRequest in
|
||||
|
||||
fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) }
|
||||
|
||||
Internals.assert(
|
||||
fetchRequest.sortDescriptors?.isEmpty == false,
|
||||
"An \(Internals.typeName(ListPublisher<O>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<O>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListPublisher` that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
```
|
||||
let listPublisher = dataStack.listPublisher(
|
||||
From<MyPersonEntity>()
|
||||
.where(\.age > 18)
|
||||
.orderBy(.ascending(\.age))
|
||||
)
|
||||
```
|
||||
Multiple objects may then register themselves to be notified when changes are made to the fetched results.
|
||||
```
|
||||
listPublisher.addObserver(self) { (listPublisher) in
|
||||
// handle changes
|
||||
}
|
||||
```
|
||||
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
|
||||
- returns: a `ListPublisher` that broadcasts changes to the fetched results
|
||||
*/
|
||||
public func publishList<B: FetchChainableBuilderType>(_ clauseChain: B) -> ListPublisher<B.ObjectType> {
|
||||
|
||||
return self.publishList(
|
||||
clauseChain.from,
|
||||
clauseChain.fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListPublisher` for a sectioned list that satisfy the fetch clauses. Multiple objects may then register themselves to be notified when changes are made to the fetched results.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListPublisher` that broadcasts changes to the fetched results
|
||||
*/
|
||||
public func publishList<O>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: FetchClause...) -> ListPublisher<O> {
|
||||
|
||||
return self.publishList(
|
||||
from,
|
||||
sectionBy,
|
||||
fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListPublisher` for a sectioned list that satisfy the fetch clauses. Multiple objects may then register themselves to be notified when changes are made to the fetched results.
|
||||
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListPublisher` that broadcasts changes to the fetched results
|
||||
*/
|
||||
public func publishList<O>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: [FetchClause]) -> ListPublisher<O> {
|
||||
|
||||
return ListPublisher(
|
||||
dataStack: self,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
applyFetchClauses: { fetchRequest in
|
||||
|
||||
fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) }
|
||||
|
||||
Internals.assert(
|
||||
fetchRequest.sortDescriptors?.isEmpty == false,
|
||||
"An \(Internals.typeName(ListPublisher<O>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<O>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListPublisher` for a sectioned list that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
```
|
||||
let listPublisher = dataStack.listPublisher(
|
||||
From<MyPersonEntity>()
|
||||
.sectionBy(\.age, { "\($0!) years old" })
|
||||
.where(\.age > 18)
|
||||
.orderBy(.ascending(\.age))
|
||||
)
|
||||
```
|
||||
Multiple objects may then register themselves to be notified when changes are made to the fetched results.
|
||||
```
|
||||
listPublisher.addObserver(self) { (listPublisher) in
|
||||
// handle changes
|
||||
}
|
||||
```
|
||||
- parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses
|
||||
- returns: a `ListPublisher` that broadcasts changes to the fetched results
|
||||
*/
|
||||
public func publishList<B: SectionMonitorBuilderType>(_ clauseChain: B) -> ListPublisher<B.ObjectType> {
|
||||
|
||||
return self.publishList(
|
||||
clauseChain.from,
|
||||
clauseChain.sectionBy,
|
||||
clauseChain.fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "publishObject(_:)")
|
||||
public func objectPublisher<O: DynamicObject>(_ object: O) -> ObjectPublisher<O> {
|
||||
|
||||
return self.publishObject(object)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "publishList(_:_:)")
|
||||
public func listPublisher<O>(_ from: From<O>, _ fetchClauses: FetchClause...) -> ListPublisher<O> {
|
||||
|
||||
return self.publishList(from, fetchClauses)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "publishList(_:_:)")
|
||||
public func listPublisher<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) -> ListPublisher<O> {
|
||||
|
||||
return self.publishList(from, fetchClauses)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "publishList(_:)")
|
||||
public func listPublisher<B: FetchChainableBuilderType>(_ clauseChain: B) -> ListPublisher<B.ObjectType> {
|
||||
|
||||
return self.publishList(clauseChain)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "publishList(_:_:_:)")
|
||||
public func listPublisher<O>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: FetchClause...) -> ListPublisher<O> {
|
||||
|
||||
return self.publishList(from, sectionBy, fetchClauses)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "publishList(_:_:_:)")
|
||||
public func listPublisher<O>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: [FetchClause]) -> ListPublisher<O> {
|
||||
|
||||
return self.publishList(from, sectionBy, fetchClauses)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "publishList(_:)")
|
||||
public func listPublisher<B: SectionMonitorBuilderType>(_ clauseChain: B) -> ListPublisher<B.ObjectType> {
|
||||
|
||||
return self.publishList(clauseChain)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -248,142 +248,6 @@ extension DataStack {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Asynchronously adds a `CloudStorage` to the stack. Migrations are also initiated by default.
|
||||
```
|
||||
guard let storage = ICloudStore(
|
||||
ubiquitousContentName: "MyAppCloudData",
|
||||
ubiquitousContentTransactionLogsSubdirectory: "logs/config1",
|
||||
ubiquitousContainerID: "iCloud.com.mycompany.myapp.containername",
|
||||
ubiquitousPeerToken: "9614d658014f4151a95d8048fb717cf0",
|
||||
configuration: "Config1",
|
||||
cloudStorageOptions: .recreateLocalStoreOnModelMismatch
|
||||
) else {
|
||||
// iCloud is not available on the device
|
||||
return
|
||||
}
|
||||
dataStack.addStorage(
|
||||
storage,
|
||||
completion: { result in
|
||||
switch result {
|
||||
case .success(let storage): // ...
|
||||
case .failure(let error): // ...
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter storage: the cloud storage
|
||||
- parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `SetupResult` argument indicates the result. Note that the `CloudStorage` associated to the `SetupResult.success` may not always be the same instance as the parameter argument if a previous `CloudStorage` was already added at the same URL and with the same configuration.
|
||||
*/
|
||||
public func addStorage<T: CloudStorage>(_ storage: T, completion: @escaping (SetupResult<T>) -> Void) {
|
||||
|
||||
let cacheFileURL = storage.cacheFileURL
|
||||
self.coordinator.performSynchronously {
|
||||
|
||||
if let _ = self.persistentStoreForStorage(storage) {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
completion(.success(storage))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if let persistentStore = self.coordinator.persistentStore(for: cacheFileURL as URL) {
|
||||
|
||||
if let existingStorage = persistentStore.storageInterface as? T,
|
||||
storage.matchesPersistentStore(persistentStore) {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
completion(.success(existingStorage))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let error = CoreStoreError.differentStorageExistsAtURL(existingPersistentStoreURL: cacheFileURL)
|
||||
Internals.log(
|
||||
error,
|
||||
"Failed to add \(Internals.typeName(storage)) at \"\(cacheFileURL)\" because a different \(Internals.typeName(NSPersistentStore.self)) at that URL already exists."
|
||||
)
|
||||
DispatchQueue.main.async {
|
||||
|
||||
completion(.failure(error))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
var cloudStorageOptions = storage.cloudStorageOptions
|
||||
cloudStorageOptions.remove(.recreateLocalStoreOnModelMismatch)
|
||||
|
||||
let storeOptions = storage.dictionary(forOptions: cloudStorageOptions)
|
||||
do {
|
||||
|
||||
_ = try self.createPersistentStoreFromStorage(
|
||||
storage,
|
||||
finalURL: cacheFileURL,
|
||||
finalStoreOptions: storeOptions
|
||||
)
|
||||
DispatchQueue.main.async {
|
||||
|
||||
completion(.success(storage))
|
||||
}
|
||||
}
|
||||
catch let error as NSError where storage.cloudStorageOptions.contains(.recreateLocalStoreOnModelMismatch) && error.isCoreDataMigrationError {
|
||||
|
||||
let finalStoreOptions = storage.dictionary(forOptions: storage.cloudStorageOptions)
|
||||
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStore(
|
||||
ofType: type(of: storage).storeType,
|
||||
at: cacheFileURL,
|
||||
options: storeOptions
|
||||
)
|
||||
_ = try self.schemaHistory
|
||||
.schema(for: metadata)
|
||||
.flatMap({ try storage.cs_eraseStorageAndWait(soureModel: $0.rawModel()) })
|
||||
_ = try self.createPersistentStoreFromStorage(
|
||||
storage,
|
||||
finalURL: cacheFileURL,
|
||||
finalStoreOptions: finalStoreOptions
|
||||
)
|
||||
}
|
||||
}
|
||||
catch let error as NSError
|
||||
where error.code == NSFileReadNoSuchFileError && error.domain == NSCocoaErrorDomain {
|
||||
|
||||
do {
|
||||
|
||||
_ = try self.addStorageAndWait(storage)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
completion(.success(storage))
|
||||
}
|
||||
}
|
||||
catch {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
completion(.failure(CoreStoreError(error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
|
||||
let storeError = CoreStoreError(error)
|
||||
Internals.log(
|
||||
storeError,
|
||||
"Failed to load \(Internals.typeName(NSPersistentStore.self)) metadata."
|
||||
)
|
||||
DispatchQueue.main.async {
|
||||
|
||||
completion(.failure(storeError))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Migrates a local storage to match the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
|
||||
|
||||
@@ -750,7 +614,7 @@ extension DataStack {
|
||||
}
|
||||
let fileManager = FileManager.default
|
||||
let systemTemporaryDirectoryURL: URL
|
||||
if #available(macOS 10.12, *) {
|
||||
if #available(macOS 10.12, iOS 10.0, *) {
|
||||
|
||||
systemTemporaryDirectoryURL = fileManager.temporaryDirectory
|
||||
}
|
||||
|
||||
@@ -36,15 +36,15 @@ extension DataStack {
|
||||
Creates an `ObjectMonitor` for the specified `DynamicObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `DynamicObject`.
|
||||
|
||||
- parameter object: the `DynamicObject` to observe changes from
|
||||
- returns: a `ObjectMonitor` that monitors changes to `object`
|
||||
- returns: an `ObjectMonitor` that monitors changes to `object`
|
||||
*/
|
||||
public func monitorObject<D>(_ object: D) -> ObjectMonitor<D> {
|
||||
public func monitorObject<O: DynamicObject>(_ object: O) -> ObjectMonitor<O> {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
"Attempted to observe objects from \(Internals.typeName(self)) outside the main thread."
|
||||
)
|
||||
return ObjectMonitor(dataStack: self, object: object)
|
||||
return .init(objectID: object.cs_id(), context: self.unsafeContext())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,7 +54,7 @@ extension DataStack {
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public func monitorList<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> ListMonitor<D> {
|
||||
public func monitorList<O>(_ from: From<O>, _ fetchClauses: FetchClause...) -> ListMonitor<O> {
|
||||
|
||||
return self.monitorList(from, fetchClauses)
|
||||
}
|
||||
@@ -66,7 +66,7 @@ extension DataStack {
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public func monitorList<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> ListMonitor<D> {
|
||||
public func monitorList<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) -> ListMonitor<O> {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -82,7 +82,7 @@ extension DataStack {
|
||||
|
||||
Internals.assert(
|
||||
fetchRequest.sortDescriptors?.isEmpty == false,
|
||||
"An \(Internals.typeName(ListMonitor<D>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<D>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
"An \(Internals.typeName(ListMonitor<O>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<O>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -102,7 +102,10 @@ extension DataStack {
|
||||
*/
|
||||
public func monitorList<B: FetchChainableBuilderType>(_ clauseChain: B) -> ListMonitor<B.ObjectType> {
|
||||
|
||||
return self.monitorList(clauseChain.from, clauseChain.fetchClauses)
|
||||
return self.monitorList(
|
||||
clauseChain.from,
|
||||
clauseChain.fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,9 +115,13 @@ extension DataStack {
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public func monitorList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ fetchClauses: FetchClause...) {
|
||||
public func monitorList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ fetchClauses: FetchClause...) {
|
||||
|
||||
self.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
|
||||
self.monitorList(
|
||||
createAsynchronously: createAsynchronously,
|
||||
from,
|
||||
fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +131,7 @@ extension DataStack {
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public func monitorList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ fetchClauses: [FetchClause]) {
|
||||
public func monitorList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ fetchClauses: [FetchClause]) {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -140,7 +147,7 @@ extension DataStack {
|
||||
|
||||
Internals.assert(
|
||||
fetchRequest.sortDescriptors?.isEmpty == false,
|
||||
"An \(Internals.typeName(ListMonitor<D>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<D>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
"An \(Internals.typeName(ListMonitor<O>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<O>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
)
|
||||
},
|
||||
createAsynchronously: createAsynchronously
|
||||
@@ -180,9 +187,13 @@ extension DataStack {
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public func monitorSectionedList<D>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: FetchClause...) -> ListMonitor<D> {
|
||||
public func monitorSectionedList<O>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: FetchClause...) -> ListMonitor<O> {
|
||||
|
||||
return self.monitorSectionedList(from, sectionBy, fetchClauses)
|
||||
return self.monitorSectionedList(
|
||||
from,
|
||||
sectionBy,
|
||||
fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +204,7 @@ extension DataStack {
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public func monitorSectionedList<D>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: [FetchClause]) -> ListMonitor<D> {
|
||||
public func monitorSectionedList<O>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: [FetchClause]) -> ListMonitor<O> {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -210,7 +221,7 @@ extension DataStack {
|
||||
|
||||
Internals.assert(
|
||||
fetchRequest.sortDescriptors?.isEmpty == false,
|
||||
"An \(Internals.typeName(ListMonitor<D>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<D>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
"An \(Internals.typeName(ListMonitor<O>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<O>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -246,9 +257,14 @@ extension DataStack {
|
||||
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public func monitorSectionedList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: FetchClause...) {
|
||||
public func monitorSectionedList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: FetchClause...) {
|
||||
|
||||
self.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
|
||||
self.monitorSectionedList(
|
||||
createAsynchronously: createAsynchronously,
|
||||
from,
|
||||
sectionBy,
|
||||
fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,7 +275,7 @@ extension DataStack {
|
||||
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public func monitorSectionedList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: [FetchClause]) {
|
||||
public func monitorSectionedList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: [FetchClause]) {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -276,7 +292,7 @@ extension DataStack {
|
||||
|
||||
Internals.assert(
|
||||
fetchRequest.sortDescriptors?.isEmpty == false,
|
||||
"An \(Internals.typeName(ListMonitor<D>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<D>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
"An \(Internals.typeName(ListMonitor<O>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<O>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
)
|
||||
},
|
||||
createAsynchronously: createAsynchronously
|
||||
|
||||
@@ -39,7 +39,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- parameter object: a reference to the object created/fetched outside the `DataStack`
|
||||
- returns: the `DynamicObject` instance if the object exists in the `DataStack`, or `nil` if not found.
|
||||
*/
|
||||
public func fetchExisting<D: DynamicObject>(_ object: D) -> D? {
|
||||
public func fetchExisting<O: DynamicObject>(_ object: O) -> O? {
|
||||
|
||||
return self.mainContext.fetchExisting(object)
|
||||
}
|
||||
@@ -50,7 +50,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- parameter objectID: the `NSManagedObjectID` for the object
|
||||
- returns: the `DynamicObject` instance if the object exists in the `DataStack`, or `nil` if not found.
|
||||
*/
|
||||
public func fetchExisting<D: DynamicObject>(_ objectID: NSManagedObjectID) -> D? {
|
||||
public func fetchExisting<O: DynamicObject>(_ objectID: NSManagedObjectID) -> O? {
|
||||
|
||||
return self.mainContext.fetchExisting(objectID)
|
||||
}
|
||||
@@ -61,7 +61,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- parameter objects: an array of `DynamicObject`s created/fetched outside the `DataStack`
|
||||
- returns: the `DynamicObject` array for objects that exists in the `DataStack`
|
||||
*/
|
||||
public func fetchExisting<D: DynamicObject, S: Sequence>(_ objects: S) -> [D] where S.Iterator.Element == D {
|
||||
public func fetchExisting<O: DynamicObject, S: Sequence>(_ objects: S) -> [O] where S.Iterator.Element == O {
|
||||
|
||||
return self.mainContext.fetchExisting(objects)
|
||||
}
|
||||
@@ -72,7 +72,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- parameter objectIDs: the `NSManagedObjectID` array for the objects
|
||||
- returns: the `DynamicObject` array for objects that exists in the `DataStack`
|
||||
*/
|
||||
public func fetchExisting<D: DynamicObject, S: Sequence>(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID {
|
||||
public func fetchExisting<O: DynamicObject, S: Sequence>(_ objectIDs: S) -> [O] where S.Iterator.Element == NSManagedObjectID {
|
||||
|
||||
return self.mainContext.fetchExisting(objectIDs)
|
||||
}
|
||||
@@ -85,7 +85,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchOne<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> D? {
|
||||
public func fetchOne<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> O? {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -102,7 +102,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchOne<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> D? {
|
||||
public func fetchOne<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> O? {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -141,7 +141,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchAll<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> [D] {
|
||||
public func fetchAll<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> [O] {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -158,7 +158,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchAll<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> [D] {
|
||||
public func fetchAll<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> [O] {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -197,7 +197,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the number of `DynamicObject`s that satisfy the specified `FetchClause`s
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchCount<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> Int {
|
||||
public func fetchCount<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> Int {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -214,7 +214,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the number of `DynamicObject`s that satisfy the specified `FetchClause`s
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchCount<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> Int {
|
||||
public func fetchCount<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> Int {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -253,7 +253,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> NSManagedObjectID? {
|
||||
public func fetchObjectID<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> NSManagedObjectID? {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -270,7 +270,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> NSManagedObjectID? {
|
||||
public func fetchObjectID<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> NSManagedObjectID? {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -309,7 +309,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> [NSManagedObjectID] {
|
||||
public func fetchObjectIDs<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> [NSManagedObjectID] {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -326,7 +326,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> [NSManagedObjectID] {
|
||||
public func fetchObjectIDs<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> [NSManagedObjectID] {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -371,7 +371,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the result of the the query, or `nil` if no match was found. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: QueryClause...) throws -> U? {
|
||||
public func queryValue<O, U: QueryableAttributeType>(_ from: From<O>, _ selectClause: Select<O, U>, _ queryClauses: QueryClause...) throws -> U? {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -391,7 +391,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the result of the the query, or `nil` if no match was found. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: [QueryClause]) throws -> U? {
|
||||
public func queryValue<O, U: QueryableAttributeType>(_ from: From<O>, _ selectClause: Select<O, U>, _ queryClauses: [QueryClause]) throws -> U? {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -435,7 +435,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: QueryClause...) throws -> [[String: Any]] {
|
||||
public func queryAttributes<O>(_ from: From<O>, _ selectClause: Select<O, NSDictionary>, _ queryClauses: QueryClause...) throws -> [[String: Any]] {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -455,7 +455,7 @@ extension DataStack: FetchableSource, QueryableSource {
|
||||
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
public func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: [QueryClause]) throws -> [[String: Any]] {
|
||||
public func queryAttributes<O>(_ from: From<O>, _ selectClause: Select<O, NSDictionary>, _ queryClauses: [QueryClause]) throws -> [[String: Any]] {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
|
||||
+3
-96
@@ -63,7 +63,7 @@ public final class DataStack: Equatable {
|
||||
/**
|
||||
Convenience initializer for `DataStack` that creates a `SchemaHistory` from a list of `DynamicSchema` versions.
|
||||
```
|
||||
CoreStore.defaultStack = DataStack(
|
||||
CoreStoreDefaults.dataStack = DataStack(
|
||||
XcodeDataModelSchema(modelName: "MyModelV1"),
|
||||
CoreStoreSchema(
|
||||
modelVersion: "MyModelV2",
|
||||
@@ -92,7 +92,7 @@ public final class DataStack: Equatable {
|
||||
/**
|
||||
Initializes a `DataStack` from a `SchemaHistory` instance.
|
||||
```
|
||||
CoreStore.defaultStack = DataStack(
|
||||
CoreStoreDefaults.dataStack = DataStack(
|
||||
schemaHistory: SchemaHistory(
|
||||
XcodeDataModelSchema(modelName: "MyModelV1"),
|
||||
CoreStoreSchema(
|
||||
@@ -370,99 +370,6 @@ public final class DataStack: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Adds a `CloudStorage` to the stack and blocks until completion.
|
||||
```
|
||||
guard let storage = ICloudStore(
|
||||
ubiquitousContentName: "MyAppCloudData",
|
||||
ubiquitousContentTransactionLogsSubdirectory: "logs/config1",
|
||||
ubiquitousContainerID: "iCloud.com.mycompany.myapp.containername",
|
||||
ubiquitousPeerToken: "9614d658014f4151a95d8048fb717cf0",
|
||||
configuration: "Config1",
|
||||
cloudStorageOptions: .recreateLocalStoreOnModelMismatch
|
||||
) else {
|
||||
// iCloud is not available on the device
|
||||
return
|
||||
}
|
||||
try dataStack.addStorageAndWait(storage)
|
||||
```
|
||||
- parameter storage: the local storage
|
||||
- throws: a `CoreStoreError` value indicating the failure
|
||||
- returns: the cloud storage added to the stack. Note that this may not always be the same instance as the parameter argument if a previous `CloudStorage` was already added at the same URL and with the same configuration.
|
||||
*/
|
||||
@discardableResult
|
||||
public func addStorageAndWait<T: CloudStorage>(_ storage: T) throws -> T {
|
||||
|
||||
return try self.coordinator.performSynchronously {
|
||||
|
||||
if let _ = self.persistentStoreForStorage(storage) {
|
||||
|
||||
return storage
|
||||
}
|
||||
|
||||
let cacheFileURL = storage.cacheFileURL
|
||||
if let persistentStore = self.coordinator.persistentStore(for: cacheFileURL as URL) {
|
||||
|
||||
if let existingStorage = persistentStore.storageInterface as? T,
|
||||
storage.matchesPersistentStore(persistentStore) {
|
||||
|
||||
return existingStorage
|
||||
}
|
||||
|
||||
let error = CoreStoreError.differentStorageExistsAtURL(existingPersistentStoreURL: cacheFileURL)
|
||||
Internals.log(
|
||||
error,
|
||||
"Failed to add \(Internals.typeName(storage)) at \"\(cacheFileURL)\" because a different \(Internals.typeName(NSPersistentStore.self)) at that URL already exists."
|
||||
)
|
||||
throw error
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
var cloudStorageOptions = storage.cloudStorageOptions
|
||||
cloudStorageOptions.remove(.recreateLocalStoreOnModelMismatch)
|
||||
|
||||
let storeOptions = storage.dictionary(forOptions: cloudStorageOptions)
|
||||
do {
|
||||
|
||||
_ = try self.createPersistentStoreFromStorage(
|
||||
storage,
|
||||
finalURL: cacheFileURL,
|
||||
finalStoreOptions: storeOptions
|
||||
)
|
||||
return storage
|
||||
}
|
||||
catch let error as NSError where storage.cloudStorageOptions.contains(.recreateLocalStoreOnModelMismatch) && error.isCoreDataMigrationError {
|
||||
|
||||
let finalStoreOptions = storage.dictionary(forOptions: storage.cloudStorageOptions)
|
||||
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStore(
|
||||
ofType: type(of: storage).storeType,
|
||||
at: cacheFileURL,
|
||||
options: storeOptions
|
||||
)
|
||||
_ = try self.schemaHistory
|
||||
.schema(for: metadata)
|
||||
.flatMap({ try storage.cs_eraseStorageAndWait(soureModel: $0.rawModel()) })
|
||||
_ = try self.createPersistentStoreFromStorage(
|
||||
storage,
|
||||
finalURL: cacheFileURL,
|
||||
finalStoreOptions: finalStoreOptions
|
||||
)
|
||||
return storage
|
||||
}
|
||||
}
|
||||
catch {
|
||||
|
||||
let storeError = CoreStoreError(error)
|
||||
Internals.log(
|
||||
storeError,
|
||||
"Failed to add \(Internals.typeName(storage)) to the stack."
|
||||
)
|
||||
throw storeError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: 3rd Party Utilities
|
||||
|
||||
@@ -472,7 +379,7 @@ public final class DataStack: Equatable {
|
||||
enum Static {
|
||||
static var myDataKey: Void?
|
||||
}
|
||||
CoreStore.defaultStack.userInfo[&Static.myDataKey] = myObject
|
||||
CoreStoreDefaults.dataStack.userInfo[&Static.myDataKey] = myObject
|
||||
```
|
||||
- Important: Do not use this method to store thread-sensitive data.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// DiffableDataSource.BaseAdapter.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - BaseAdapter
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.BaseAdapter` serves as a superclass for consumers of `ListPublisher` and `ListSnapshot` diffable data.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.TableViewAdapter<Person>(
|
||||
tableView: self.tableView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (tableView, indexPath, person) in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class BaseAdapter<O: DynamicObject, T: Target>: NSObject {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
The object type represented by this dataSource
|
||||
*/
|
||||
public typealias ObjectType = O
|
||||
|
||||
/**
|
||||
The target to be updated by this dataSource
|
||||
*/
|
||||
public let target: T
|
||||
|
||||
/**
|
||||
The `DataStack` where object fetches are performed
|
||||
*/
|
||||
public let dataStack: DataStack
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.BaseAdapter` object. This instance needs to be held on (retained) for as long as the target's lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.TableViewAdapterAdapter<Person>(
|
||||
tableView: self.tableView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (tableView, indexPath, person) in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter tableView: the `UITableView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.TableViewAdapter`.
|
||||
- parameter dataStack: the `DataStack` instance that the dataSource will fetch objects from
|
||||
- parameter cellProvider: a closure that configures and returns the `UITableViewCell` for the object
|
||||
*/
|
||||
public init(target: T, dataStack: DataStack) {
|
||||
|
||||
self.target = target
|
||||
self.dataStack = dataStack
|
||||
self.dispatcher = Internals.DiffableDataUIDispatcher<O>(dataStack: dataStack)
|
||||
}
|
||||
|
||||
/**
|
||||
Clears the target.
|
||||
- parameter animatingDifferences: if `true`, animations may be applied accordingly. Defaults to `true`.
|
||||
*/
|
||||
open func purge(animatingDifferences: Bool = true, completion: @escaping () -> Void = {}) {
|
||||
|
||||
self.dispatcher.purge(
|
||||
target: self.target,
|
||||
animatingDifferences: animatingDifferences,
|
||||
performUpdates: { target, changeset, setSections in
|
||||
|
||||
target.reload(
|
||||
using: changeset,
|
||||
animated: animatingDifferences,
|
||||
setData: setSections
|
||||
)
|
||||
},
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Reloads the target using a `ListSnapshot`. This is typically from the `snapshot` property of a `ListPublisher`:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
``
|
||||
- parameter snapshot: the `ListSnapshot` used to reload the target with. This is typically from the `snapshot` property of a `ListPublisher`.
|
||||
- parameter animatingDifferences: if `true`, animations may be applied accordingly. Defaults to `true`.
|
||||
*/
|
||||
open func apply(_ snapshot: ListSnapshot<O>, animatingDifferences: Bool = true, completion: @escaping () -> Void = {}) {
|
||||
|
||||
let diffableSnapshot = snapshot.diffableSnapshot
|
||||
self.dispatcher.apply(
|
||||
diffableSnapshot,
|
||||
target: self.target,
|
||||
animatingDifferences: animatingDifferences,
|
||||
performUpdates: { target, changeset, setSections in
|
||||
|
||||
target.reload(
|
||||
using: changeset,
|
||||
animated: animatingDifferences,
|
||||
setData: setSections
|
||||
)
|
||||
},
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the number of sections
|
||||
|
||||
- parameter indexPath: the `IndexPath` to search for
|
||||
- returns: the number of sections
|
||||
*/
|
||||
public func numberOfSections() -> Int {
|
||||
|
||||
return self.dispatcher.numberOfSections()
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the number of items at the specified section, or `nil` if the section is not found
|
||||
|
||||
- parameter section: the section index to search for
|
||||
- returns: the number of items at the specified section, or `nil` if the section is not found
|
||||
*/
|
||||
public func numberOfItems(inSection section: Int) -> Int? {
|
||||
|
||||
return self.dispatcher.numberOfItems(inSection: section)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the section identifier at the specified index, or `nil` if not found
|
||||
|
||||
- parameter section: the section index to search for
|
||||
- returns: the section identifier at the specified indec, or `nil` if not found
|
||||
*/
|
||||
public func sectionID(for section: Int) -> String? {
|
||||
|
||||
return self.dispatcher.sectionIdentifier(inSection: section)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the object identifier for the item at the specified `IndexPath`, or `nil` if not found
|
||||
|
||||
- parameter indexPath: the `IndexPath` to search for
|
||||
- returns: the object identifier for the item at the specified `IndexPath`, or `nil` if not found
|
||||
*/
|
||||
public func itemID(for indexPath: IndexPath) -> O.ObjectID? {
|
||||
|
||||
return self.dispatcher.itemIdentifier(for: indexPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `IndexPath` for the item with the specified object identifier, or `nil` if not found
|
||||
|
||||
- parameter itemID: the object identifier to search for
|
||||
- returns: the `IndexPath` for the item with the specified object identifier, or `nil` if not found
|
||||
*/
|
||||
public func indexPath(for itemID: O.ObjectID) -> IndexPath? {
|
||||
|
||||
return self.dispatcher.indexPath(for: itemID)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal let dispatcher: Internals.DiffableDataUIDispatcher<O>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// DiffableDataSource.CollectionViewAdapter-AppKit.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if canImport(AppKit) && os(macOS)
|
||||
|
||||
import AppKit
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - CollectionView
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.CollectionViewAdapter` serves as a `NSCollectionViewDataSource` that handles `ListPublisher` snapshots for a `NSCollectionView`. Subclasses of `DiffableDataSource.CollectionViewAdapter` may override some `NSCollectionViewDataSource` methods as needed.
|
||||
The `DiffableDataSource.CollectionViewAdapter` instance needs to be held on (retained) for as long as the `NSCollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionViewAdapter<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
itemProvider: { (collectionView, indexPath, person) in
|
||||
let item = collectionView.makeItem(withIdentifier: .collectionViewItem, for: indexPath) as! PersonItem
|
||||
item.setPerson(person)
|
||||
return item
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
`DiffableDataSource.CollectionViewAdapter` fully handles the reload animations.
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class CollectionViewAdapter<O: DynamicObject>: BaseAdapter<O, DefaultCollectionViewTarget<NSCollectionView>>, NSCollectionViewDataSource {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.CollectionViewAdapter`. This instance needs to be held on (retained) for as long as the `NSCollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionViewAdapter<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
itemProvider: { (collectionView, indexPath, person) in
|
||||
let item = collectionView.makeItem(withIdentifier: .collectionViewItem, for: indexPath) as! PersonItem
|
||||
item.setPerson(person)
|
||||
return item
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter collectionView: the `NSCollectionView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.CollectionViewAdapter`.
|
||||
- parameter dataStack: the `DataStack` instance that the dataSource will fetch objects from
|
||||
- parameter itemProvider: a closure that configures and returns the `NSCollectionViewItem` for the object
|
||||
*/
|
||||
@nonobjc
|
||||
public init(collectionView: NSCollectionView, dataStack: DataStack, itemProvider: @escaping (NSCollectionView, IndexPath, O) -> NSCollectionViewItem?, supplementaryViewProvider: @escaping (NSCollectionView, String, IndexPath) -> NSView? = { _, _, _ in nil }) {
|
||||
|
||||
self.itemProvider = itemProvider
|
||||
self.supplementaryViewProvider = supplementaryViewProvider
|
||||
|
||||
super.init(target: .init(collectionView), dataStack: dataStack)
|
||||
|
||||
collectionView.dataSource = self
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSCollectionViewDataSource
|
||||
|
||||
@objc
|
||||
public dynamic func numberOfSections(in collectionView: NSCollectionView) -> Int {
|
||||
|
||||
return self.numberOfSections()
|
||||
}
|
||||
|
||||
@objc
|
||||
public dynamic func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
|
||||
return self.numberOfItems(inSection: section) ?? 0
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
||||
|
||||
guard let objectID = self.itemID(for: indexPath) else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) already removed from list")
|
||||
}
|
||||
guard let object = self.dataStack.fetchExisting(objectID) as O? else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) has been deleted")
|
||||
}
|
||||
guard let item = self.itemProvider(collectionView, indexPath, object) else {
|
||||
|
||||
Internals.abort("\(Internals.typeName(NSCollectionViewDataSource.self)) returned a `nil` item for \(Internals.typeName(IndexPath.self)) \(indexPath)")
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView {
|
||||
|
||||
guard let view = self.supplementaryViewProvider(collectionView, kind, indexPath) else {
|
||||
|
||||
return NSView()
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let itemProvider: (NSCollectionView, IndexPath, O) -> NSCollectionViewItem?
|
||||
private let supplementaryViewProvider: (NSCollectionView, String, IndexPath) -> NSView?
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DefaultCollectionViewTarget
|
||||
|
||||
public struct DefaultCollectionViewTarget<T: NSCollectionView>: Target {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public typealias Base = T
|
||||
|
||||
public private(set) weak var base: Base?
|
||||
|
||||
public init(_ base: Base) {
|
||||
|
||||
self.base = base
|
||||
}
|
||||
|
||||
|
||||
// MARK: DiffableDataSource.Target:
|
||||
|
||||
public var shouldSuspendBatchUpdates: Bool {
|
||||
|
||||
return self.base?.window == nil
|
||||
}
|
||||
|
||||
public func deleteSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.deleteSections(indices)
|
||||
}
|
||||
|
||||
public func insertSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.insertSections(indices)
|
||||
}
|
||||
|
||||
public func reloadSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.reloadSections(indices)
|
||||
}
|
||||
|
||||
public func moveSection(at index: IndexSet.Element, to newIndex: IndexSet.Element, animated: Bool) {
|
||||
|
||||
self.base?.moveSection(index, toSection: newIndex)
|
||||
}
|
||||
|
||||
public func deleteItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.deleteItems(at: Set(indexPaths))
|
||||
}
|
||||
|
||||
public func insertItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.insertItems(at: Set(indexPaths))
|
||||
}
|
||||
|
||||
public func reloadItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.reloadItems(at: Set(indexPaths))
|
||||
}
|
||||
|
||||
public func moveItem(at indexPath: IndexPath, to newIndexPath: IndexPath, animated: Bool) {
|
||||
|
||||
self.base?.moveItem(at: indexPath, to: newIndexPath)
|
||||
}
|
||||
|
||||
public func performBatchUpdates(updates: () -> Void, animated: Bool) {
|
||||
|
||||
self.base?.animator().performBatchUpdates(updates, completionHandler: nil)
|
||||
}
|
||||
|
||||
public func reloadData() {
|
||||
|
||||
self.base?.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Deprecated
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
@available(*, deprecated, renamed: "CollectionViewAdapter")
|
||||
public typealias CollectionView = CollectionViewAdapter
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,231 @@
|
||||
//
|
||||
// DiffableDataSource.CollectionViewAdapter-UIKit.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) && (os(iOS) || os(tvOS))
|
||||
|
||||
import UIKit
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - CollectionView
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.CollectionViewAdapter` serves as a `UICollectionViewDataSource` that handles `ListPublisher` snapshots for a `UICollectionView`. Subclasses of `DiffableDataSource.CollectionViewAdapter` may override some `UICollectionViewDataSource` methods as needed.
|
||||
The `DiffableDataSource.CollectionViewAdapter` instance needs to be held on (retained) for as long as the `UICollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionViewAdapter<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (collectionView, indexPath, person) in
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
`DiffableDataSource.CollectionViewAdapter` fully handles the reload animations.
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class CollectionViewAdapter<O: DynamicObject>: BaseAdapter<O, DefaultCollectionViewTarget<UICollectionView>>, UICollectionViewDataSource {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.CollectionViewAdapter`. This instance needs to be held on (retained) for as long as the `UICollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionViewAdapter<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (collectionView, indexPath, person) in
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter collectionView: the `UICollectionView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.CollectionViewAdapter`.
|
||||
- parameter dataStack: the `DataStack` instance that the dataSource will fetch objects from
|
||||
- parameter cellProvider: a closure that configures and returns the `UICollectionViewCell` for the object
|
||||
- parameter supplementaryViewProvider: an optional closure for providing `UICollectionReusableView` supplementary views. If not set, defaults to returning `nil`
|
||||
*/
|
||||
public init(collectionView: UICollectionView, dataStack: DataStack, cellProvider: @escaping (UICollectionView, IndexPath, O) -> UICollectionViewCell?, supplementaryViewProvider: @escaping (UICollectionView, String, IndexPath) -> UICollectionReusableView? = { _, _, _ in nil }) {
|
||||
|
||||
self.cellProvider = cellProvider
|
||||
self.supplementaryViewProvider = supplementaryViewProvider
|
||||
|
||||
super.init(target: .init(collectionView), dataStack: dataStack)
|
||||
|
||||
collectionView.dataSource = self
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UICollectionViewDataSource
|
||||
|
||||
@objc
|
||||
public dynamic func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||
|
||||
return self.numberOfSections()
|
||||
}
|
||||
|
||||
@objc
|
||||
public dynamic func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
|
||||
return self.numberOfItems(inSection: section) ?? 0
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
|
||||
guard let objectID = self.itemID(for: indexPath) else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) already removed from list")
|
||||
}
|
||||
guard let object = self.dataStack.fetchExisting(objectID) as O? else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) has been deleted")
|
||||
}
|
||||
guard let cell = self.cellProvider(collectionView, indexPath, object) else {
|
||||
|
||||
Internals.abort("\(Internals.typeName(UICollectionViewDataSource.self)) returned a `nil` cell for \(Internals.typeName(IndexPath.self)) \(indexPath)")
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
|
||||
guard let view = self.supplementaryViewProvider(collectionView, kind, indexPath) else {
|
||||
|
||||
return UICollectionReusableView()
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let cellProvider: (UICollectionView, IndexPath, O) -> UICollectionViewCell?
|
||||
private let supplementaryViewProvider: (UICollectionView, String, IndexPath) -> UICollectionReusableView?
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DefaultCollectionViewTarget
|
||||
|
||||
public struct DefaultCollectionViewTarget<T: UICollectionView>: Target {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public typealias Base = T
|
||||
|
||||
public private(set) weak var base: Base?
|
||||
|
||||
public init(_ base: Base) {
|
||||
|
||||
self.base = base
|
||||
}
|
||||
|
||||
|
||||
// MARK: DiffableDataSource.Target
|
||||
|
||||
public var shouldSuspendBatchUpdates: Bool {
|
||||
|
||||
return self.base?.window == nil
|
||||
}
|
||||
|
||||
public func deleteSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.deleteSections(indices)
|
||||
}
|
||||
|
||||
public func insertSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.insertSections(indices)
|
||||
}
|
||||
|
||||
public func reloadSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.reloadSections(indices)
|
||||
}
|
||||
|
||||
public func moveSection(at index: IndexSet.Element, to newIndex: IndexSet.Element, animated: Bool) {
|
||||
|
||||
self.base?.moveSection(index, toSection: newIndex)
|
||||
}
|
||||
|
||||
public func deleteItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.deleteItems(at: indexPaths)
|
||||
}
|
||||
|
||||
public func insertItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.insertItems(at: indexPaths)
|
||||
}
|
||||
|
||||
public func reloadItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.reloadItems(at: indexPaths)
|
||||
}
|
||||
|
||||
public func moveItem(at indexPath: IndexPath, to newIndexPath: IndexPath, animated: Bool) {
|
||||
|
||||
self.base?.moveItem(at: indexPath, to: newIndexPath)
|
||||
}
|
||||
|
||||
public func performBatchUpdates(updates: () -> Void, animated: Bool) {
|
||||
|
||||
self.base?.performBatchUpdates(updates, completion: nil)
|
||||
}
|
||||
|
||||
public func reloadData() {
|
||||
|
||||
self.base?.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Deprecated
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
@available(*, deprecated, renamed: "CollectionViewAdapter")
|
||||
public typealias CollectionView = CollectionViewAdapter
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,266 @@
|
||||
//
|
||||
// DiffableDataSource.TableViewAdapter-UIKit.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) && (os(iOS) || os(tvOS))
|
||||
|
||||
import UIKit
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - TableViewAdapter
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.TableViewAdapterAdapter` serves as a `UITableViewDataSource` that handles `ListPublisher` snapshots for a `UITableView`. Subclasses of `DiffableDataSource.TableViewAdapter` may override some `UITableViewDataSource` methods as needed.
|
||||
The `DiffableDataSource.TableViewAdapterAdapter` instance needs to be held on (retained) for as long as the `UITableView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.TableViewAdapter<Person>(
|
||||
tableView: self.tableView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (tableView, indexPath, person) in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
`DiffableDataSource.TableViewAdapter` fully handles the reload animations.
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class TableViewAdapter<O: DynamicObject>: BaseAdapter<O, DefaultTableViewTarget<UITableView>>, UITableViewDataSource {
|
||||
|
||||
// MARK: Publi
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.TableViewAdapter`. This instance needs to be held on (retained) for as long as the `UITableView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.TableViewAdapter<Person>(
|
||||
tableView: self.tableView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (tableView, indexPath, person) in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter tableView: the `UITableView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.TableViewAdapter`.
|
||||
- parameter dataStack: the `DataStack` instance that the dataSource will fetch objects from
|
||||
- parameter cellProvider: a closure that configures and returns the `UITableViewCell` for the object
|
||||
*/
|
||||
public init(tableView: UITableView, dataStack: DataStack, cellProvider: @escaping (UITableView, IndexPath, O) -> UITableViewCell?) {
|
||||
|
||||
self.cellProvider = cellProvider
|
||||
super.init(target: .init(tableView), dataStack: dataStack)
|
||||
|
||||
tableView.dataSource = self
|
||||
}
|
||||
|
||||
/**
|
||||
The target `UITableView`
|
||||
*/
|
||||
public var tableView: UITableView? {
|
||||
|
||||
return self.target.base
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
@objc
|
||||
public dynamic func numberOfSections(in tableView: UITableView) -> Int {
|
||||
|
||||
return self.numberOfSections()
|
||||
}
|
||||
|
||||
@objc
|
||||
public dynamic func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
|
||||
return self.numberOfItems(inSection: section) ?? 0
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
|
||||
return self.sectionID(for: section)
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
guard let objectID = self.itemID(for: indexPath) else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) already removed from list")
|
||||
}
|
||||
guard let object = self.dataStack.fetchExisting(objectID) as O? else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) has been deleted")
|
||||
}
|
||||
guard let cell = self.cellProvider(tableView, indexPath, object) else {
|
||||
|
||||
Internals.abort("\(Internals.typeName(UITableViewDataSource.self)) returned a `nil` cell for \(Internals.typeName(IndexPath.self)) \(indexPath)")
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
|
||||
|
||||
return .delete
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@nonobjc
|
||||
private let cellProvider: (UITableView, IndexPath, O) -> UITableViewCell?
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DefaultTableViewTarget
|
||||
|
||||
public struct DefaultTableViewTarget<T: UITableView>: Target {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public typealias Base = T
|
||||
|
||||
public private(set) weak var base: Base?
|
||||
|
||||
public init(_ base: Base) {
|
||||
|
||||
self.base = base
|
||||
}
|
||||
|
||||
|
||||
// MARK: DiffableDataSource.Target
|
||||
|
||||
public var shouldSuspendBatchUpdates: Bool {
|
||||
|
||||
return self.base?.window == nil
|
||||
}
|
||||
|
||||
public func deleteSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.deleteSections(indices, with: .automatic)
|
||||
}
|
||||
|
||||
public func insertSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.insertSections(indices, with: .automatic)
|
||||
}
|
||||
|
||||
public func reloadSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.reloadSections(indices, with: .automatic)
|
||||
}
|
||||
|
||||
public func moveSection(at index: IndexSet.Element, to newIndex: IndexSet.Element, animated: Bool) {
|
||||
|
||||
self.base?.moveSection(index, toSection: newIndex)
|
||||
}
|
||||
|
||||
public func deleteItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.deleteRows(at: indexPaths, with: .automatic)
|
||||
}
|
||||
|
||||
public func insertItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.insertRows(at: indexPaths, with: .automatic)
|
||||
}
|
||||
|
||||
public func reloadItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.reloadRows(at: indexPaths, with: .automatic)
|
||||
}
|
||||
|
||||
public func moveItem(at indexPath: IndexPath, to newIndexPath: IndexPath, animated: Bool) {
|
||||
|
||||
self.base?.moveRow(at: indexPath, to: newIndexPath)
|
||||
}
|
||||
|
||||
public func performBatchUpdates(updates: () -> Void, animated: Bool) {
|
||||
|
||||
guard let base = self.base else {
|
||||
|
||||
return
|
||||
}
|
||||
if #available(iOS 11.0, tvOS 11.0, *) {
|
||||
|
||||
base.performBatchUpdates(updates)
|
||||
}
|
||||
else {
|
||||
|
||||
base.beginUpdates()
|
||||
updates()
|
||||
base.endUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
public func reloadData() {
|
||||
|
||||
self.base?.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Deprecated
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
@available(*, deprecated, renamed: "TableViewAdapter")
|
||||
public typealias TableView = TableViewAdapter
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// DiffableDataSource.Target.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
|
||||
// MARK: - DiffableDataSourc
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - Target
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.Target` protocol allows custom views to consume `ListSnapshot` diffable data similar to how `DiffableDataSource.TableViewAdapter` and `DiffableDataSource.CollectionViewAdapter` reloads data for their corresponding views.
|
||||
*/
|
||||
public typealias Target = DiffableDataSourceTarget
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource.Target
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.Target` protocol allows custom views to consume `ListSnapshot` diffable data similar to how `DiffableDataSource.TableViewAdapter` and `DiffableDataSource.CollectionViewAdapter` reloads data for their corresponding views.
|
||||
*/
|
||||
public protocol DiffableDataSourceTarget {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Whether `reloadData()` should be executed instead of `performBatchUpdates(updates:animated:)`.
|
||||
*/
|
||||
var shouldSuspendBatchUpdates: Bool { get }
|
||||
|
||||
/**
|
||||
Deletes one or more sections.
|
||||
*/
|
||||
func deleteSections(at indices: IndexSet, animated: Bool)
|
||||
|
||||
/**
|
||||
Inserts one or more sections
|
||||
*/
|
||||
func insertSections(at indices: IndexSet, animated: Bool)
|
||||
|
||||
/**
|
||||
Reloads the specified sections.
|
||||
*/
|
||||
func reloadSections(at indices: IndexSet, animated: Bool)
|
||||
|
||||
/**
|
||||
Moves a section to a new location.
|
||||
*/
|
||||
func moveSection(at index: IndexSet.Element, to newIndex: IndexSet.Element, animated: Bool)
|
||||
|
||||
/**
|
||||
Deletes the items specified by an array of index paths.
|
||||
*/
|
||||
func deleteItems(at indexPaths: [IndexPath], animated: Bool)
|
||||
|
||||
/**
|
||||
Inserts items at the locations identified by an array of index paths.
|
||||
*/
|
||||
func insertItems(at indexPaths: [IndexPath], animated: Bool)
|
||||
|
||||
/**
|
||||
Reloads the specified items.
|
||||
*/
|
||||
func reloadItems(at indexPaths: [IndexPath], animated: Bool)
|
||||
|
||||
/**
|
||||
Moves the item at a specified location to a destination location.
|
||||
*/
|
||||
func moveItem(at indexPath: IndexPath, to newIndexPath: IndexPath, animated: Bool)
|
||||
|
||||
/**
|
||||
Animates multiple insert, delete, reload, and move operations as a group.
|
||||
*/
|
||||
func performBatchUpdates(updates: () -> Void, animated: Bool)
|
||||
|
||||
/**
|
||||
Reloads all sections and items.
|
||||
*/
|
||||
func reloadData()
|
||||
}
|
||||
|
||||
extension DiffableDataSource.Target {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func reload<C, O>(
|
||||
using stagedChangeset: Internals.DiffableDataUIDispatcher<O>.StagedChangeset<C>,
|
||||
animated: Bool,
|
||||
interrupt: ((Internals.DiffableDataUIDispatcher<O>.Changeset<C>) -> Bool)? = nil,
|
||||
setData: (C) -> Void
|
||||
) {
|
||||
|
||||
if self.shouldSuspendBatchUpdates, let data = stagedChangeset.last?.data {
|
||||
|
||||
setData(data)
|
||||
self.reloadData()
|
||||
return
|
||||
}
|
||||
for changeset in stagedChangeset {
|
||||
|
||||
if let interrupt = interrupt,
|
||||
interrupt(changeset),
|
||||
let data = stagedChangeset.last?.data {
|
||||
|
||||
setData(data)
|
||||
self.reloadData()
|
||||
return
|
||||
}
|
||||
self.performBatchUpdates(
|
||||
updates: {
|
||||
|
||||
setData(changeset.data)
|
||||
|
||||
if !changeset.sectionDeleted.isEmpty {
|
||||
|
||||
self.deleteSections(
|
||||
at: IndexSet(changeset.sectionDeleted),
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
if !changeset.sectionInserted.isEmpty {
|
||||
|
||||
self.insertSections(
|
||||
at: IndexSet(changeset.sectionInserted),
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
if !changeset.sectionUpdated.isEmpty {
|
||||
|
||||
self.reloadSections(
|
||||
at: IndexSet(changeset.sectionUpdated),
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
for (source, target) in changeset.sectionMoved {
|
||||
|
||||
self.moveSection(
|
||||
at: source,
|
||||
to: target,
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
if !changeset.elementDeleted.isEmpty {
|
||||
|
||||
self.deleteItems(
|
||||
at: changeset.elementDeleted.map {
|
||||
|
||||
IndexPath(item: $0.element, section: $0.section)
|
||||
},
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
if !changeset.elementInserted.isEmpty {
|
||||
|
||||
self.insertItems(
|
||||
at: changeset.elementInserted.map {
|
||||
|
||||
IndexPath(item: $0.element, section: $0.section)
|
||||
},
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
if !changeset.elementUpdated.isEmpty {
|
||||
|
||||
self.reloadItems(
|
||||
at: changeset.elementUpdated.map {
|
||||
|
||||
IndexPath(item: $0.element, section: $0.section)
|
||||
},
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
for (source, target) in changeset.elementMoved {
|
||||
|
||||
self.moveItem(
|
||||
at: IndexPath(item: source.element, section: source.section),
|
||||
to: IndexPath(item: target.element, section: target.section),
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
},
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// File.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if os(iOS) || os(tvOS) || os(macOS)
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
/**
|
||||
Namespace for diffable data source types. See `DiffableDataSource.TableViewAdapter` and `DiffableDataSource.CollectionViewAdapter` for actual implementations
|
||||
*/
|
||||
public enum DiffableDataSource {}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// DiffableDataSourceSnapshotProtocol.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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: - DiffableDataSourceSnapshotProtocol
|
||||
|
||||
internal protocol DiffableDataSourceSnapshotProtocol {
|
||||
|
||||
init()
|
||||
|
||||
var numberOfItems: Int { get }
|
||||
var numberOfSections: Int { get }
|
||||
var sectionIdentifiers: [String] { get }
|
||||
var itemIdentifiers: [NSManagedObjectID] { get }
|
||||
|
||||
func numberOfItems(inSection identifier: String) -> Int
|
||||
func itemIdentifiers(inSection identifier: String) -> [NSManagedObjectID]
|
||||
func sectionIdentifier(containingItem identifier: NSManagedObjectID) -> String?
|
||||
func indexOfItem(_ identifier: NSManagedObjectID) -> Int?
|
||||
func indexOfSection(_ identifier: String) -> Int?
|
||||
|
||||
mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: String?)
|
||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], beforeItem beforeIdentifier: NSManagedObjectID)
|
||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], afterItem afterIdentifier: NSManagedObjectID)
|
||||
mutating func deleteItems(_ identifiers: [NSManagedObjectID])
|
||||
mutating func deleteAllItems()
|
||||
mutating func moveItem(_ identifier: NSManagedObjectID, beforeItem toIdentifier: NSManagedObjectID)
|
||||
mutating func moveItem(_ identifier: NSManagedObjectID, afterItem toIdentifier: NSManagedObjectID)
|
||||
mutating func reloadItems(_ identifiers: [NSManagedObjectID])
|
||||
mutating func appendSections(_ identifiers: [String])
|
||||
mutating func insertSections(_ identifiers: [String], beforeSection toIdentifier: String)
|
||||
mutating func insertSections(_ identifiers: [String], afterSection toIdentifier: String)
|
||||
mutating func deleteSections(_ identifiers: [String])
|
||||
mutating func moveSection(_ identifier: String, beforeSection toIdentifier: String)
|
||||
mutating func moveSection(_ identifier: String, afterSection toIdentifier: String)
|
||||
mutating func reloadSections(_ identifiers: [String])
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Differentiable.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
|
||||
// MARK: - Differentiable
|
||||
|
||||
@usableFromInline
|
||||
internal protocol Differentiable {
|
||||
|
||||
associatedtype DifferenceIdentifier: Hashable
|
||||
|
||||
var differenceIdentifier: DifferenceIdentifier { get }
|
||||
|
||||
func isContentEqual(to source: Self) -> Bool
|
||||
}
|
||||
|
||||
extension Differentiable where Self: AnyObject {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal var differenceIdentifier: ObjectIdentifier {
|
||||
|
||||
return .init(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DifferentiableSection
|
||||
|
||||
@usableFromInline
|
||||
internal protocol DifferentiableSection: Differentiable {
|
||||
|
||||
associatedtype Collection: Swift.Collection where Collection.Element: Differentiable
|
||||
|
||||
var elements: Collection { get }
|
||||
|
||||
init<S: Sequence>(source: Self, elements: S) where S.Element == Collection.Element
|
||||
}
|
||||
+71
-32
@@ -33,12 +33,21 @@ import CoreData
|
||||
All CoreStore's utilities are designed around `DynamicObject` instances. `NSManagedObject` and `CoreStoreObject` instances all conform to `DynamicObject`.
|
||||
*/
|
||||
public protocol DynamicObject: AnyObject {
|
||||
/**
|
||||
The object ID for this instance
|
||||
*/
|
||||
typealias ObjectID = NSManagedObjectID
|
||||
|
||||
/**
|
||||
Used internally by CoreStore. Do not call directly.
|
||||
*/
|
||||
static func cs_forceCreate(entityDescription: NSEntityDescription, into context: NSManagedObjectContext, assignTo store: NSPersistentStore) -> Self
|
||||
|
||||
/**
|
||||
Used internally by CoreStore. Do not call directly.
|
||||
*/
|
||||
static func cs_snapshotDictionary(id: ObjectID, context: NSManagedObjectContext) -> [String: Any]?
|
||||
|
||||
/**
|
||||
Used internally by CoreStore. Do not call directly.
|
||||
*/
|
||||
@@ -52,22 +61,17 @@ public protocol DynamicObject: AnyObject {
|
||||
/**
|
||||
Used internally by CoreStore. Do not call directly.
|
||||
*/
|
||||
func cs_id() -> NSManagedObjectID
|
||||
func cs_toRaw() -> NSManagedObject
|
||||
|
||||
/**
|
||||
Used internally by CoreStore. Do not call directly.
|
||||
*/
|
||||
func cs_toRaw() -> NSManagedObject
|
||||
func cs_id() -> ObjectID
|
||||
}
|
||||
|
||||
extension DynamicObject {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal static func keyPathBuilder() -> DynamicObjectMeta<Never, Self> {
|
||||
|
||||
return .init(keyPathString: "SELF")
|
||||
}
|
||||
// MARK: Internal
|
||||
|
||||
internal func runtimeType() -> Self.Type {
|
||||
|
||||
@@ -92,6 +96,21 @@ extension NSManagedObject: DynamicObject {
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
public class func cs_snapshotDictionary(id: ObjectID, context: NSManagedObjectContext) -> [String: Any]? {
|
||||
|
||||
guard let object = context.fetchExisting(id) as NSManagedObject? else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let rawObject = object.cs_toRaw()
|
||||
var dictionary = rawObject.dictionaryWithValues(forKeys: Array(rawObject.entity.attributesByName.keys))
|
||||
for case (let key, let target as NSManagedObject) in rawObject.dictionaryWithValues(forKeys: Array(rawObject.entity.relationshipsByName.keys)) {
|
||||
|
||||
dictionary[key] = target.objectID
|
||||
}
|
||||
return dictionary
|
||||
}
|
||||
|
||||
public class func cs_fromRaw(object: NSManagedObject) -> Self {
|
||||
|
||||
@@ -103,24 +122,14 @@ extension NSManagedObject: DynamicObject {
|
||||
return object.isKind(of: self)
|
||||
}
|
||||
|
||||
public func cs_id() -> NSManagedObjectID {
|
||||
|
||||
return self.objectID
|
||||
}
|
||||
|
||||
public func cs_toRaw() -> NSManagedObject {
|
||||
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
extension DynamicObject where Self: NSManagedObject {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func createSnapshot() -> ObjectSnapshot<Self> {
|
||||
public func cs_id() -> ObjectID {
|
||||
|
||||
return ObjectSnapshot(from: self)
|
||||
return self.objectID
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +150,46 @@ extension CoreStoreObject {
|
||||
}
|
||||
return self.cs_fromRaw(object: object)
|
||||
}
|
||||
|
||||
public class func cs_snapshotDictionary(id: ObjectID, context: NSManagedObjectContext) -> [String: Any]? {
|
||||
|
||||
func initializeAttributes(mirror: Mirror, object: Self, into attributes: inout [KeyPathString: Any]) {
|
||||
|
||||
if let superClassMirror = mirror.superclassMirror {
|
||||
|
||||
initializeAttributes(
|
||||
mirror: superClassMirror,
|
||||
object: object,
|
||||
into: &attributes
|
||||
)
|
||||
}
|
||||
for child in mirror.children {
|
||||
|
||||
switch child.value {
|
||||
|
||||
case let property as AttributeProtocol:
|
||||
attributes[property.keyPath] = property.valueForSnapshot
|
||||
|
||||
case let property as RelationshipProtocol:
|
||||
attributes[property.keyPath] = property.valueForSnapshot
|
||||
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
guard let object = context.fetchExisting(id) as CoreStoreObject? else {
|
||||
|
||||
return nil
|
||||
}
|
||||
var values: [KeyPathString: Any] = [:]
|
||||
initializeAttributes(
|
||||
mirror: Mirror(reflecting: object),
|
||||
object: object as! Self,
|
||||
into: &values
|
||||
)
|
||||
return values
|
||||
}
|
||||
|
||||
public class func cs_fromRaw(object: NSManagedObject) -> Self {
|
||||
|
||||
@@ -166,23 +215,13 @@ extension CoreStoreObject {
|
||||
return (self as AnyClass).isSubclass(of: type as AnyClass)
|
||||
}
|
||||
|
||||
public func cs_id() -> NSManagedObjectID {
|
||||
|
||||
return self.rawObject!.objectID
|
||||
}
|
||||
|
||||
public func cs_toRaw() -> NSManagedObject {
|
||||
|
||||
return self.rawObject!
|
||||
}
|
||||
}
|
||||
|
||||
extension DynamicObject where Self: CoreStoreObject {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func createSnapshot() -> ObjectSnapshot<Self> {
|
||||
public func cs_id() -> ObjectID {
|
||||
|
||||
return ObjectSnapshot(from: self)
|
||||
return self.rawObject!.objectID
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ import ObjectiveC
|
||||
let pet = Relationship.ToOne<Animal>("pet", inverse: { $0.master })
|
||||
}
|
||||
|
||||
CoreStore.defaultStack = DataStack(
|
||||
CoreStoreDefaults.dataStack = DataStack(
|
||||
CoreStoreSchema(
|
||||
modelVersion: "V1",
|
||||
entities: [
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// EnvironmentValues+DataSources.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if canImport(SwiftUI) && canImport(Combine)
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - EnvironmentValues
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
extension EnvironmentValues {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
The `DataStack` instance injected to `self`:
|
||||
```
|
||||
@Environment(\.dataStack)
|
||||
var dataStack: DataStack
|
||||
```
|
||||
*/
|
||||
public var dataStack: DataStack {
|
||||
|
||||
get {
|
||||
|
||||
return self[DataStackKey.self]
|
||||
}
|
||||
set {
|
||||
|
||||
self[DataStackKey.self] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DataStackEnvironmentKey
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
fileprivate struct DataStackKey: EnvironmentKey {
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate static var defaultValue: DataStack {
|
||||
|
||||
return CoreStoreDefaults.dataStack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -39,14 +39,20 @@ import CoreData
|
||||
)
|
||||
```
|
||||
*/
|
||||
public struct FetchChainBuilder<D: DynamicObject>: FetchChainableBuilderType {
|
||||
public struct FetchChainBuilder<O: DynamicObject>: FetchChainableBuilderType {
|
||||
|
||||
// MARK: FetchChainableBuilderType
|
||||
|
||||
public typealias ObjectType = D
|
||||
public typealias ObjectType = O
|
||||
|
||||
public var from: From<D>
|
||||
public var from: From<O>
|
||||
public var fetchClauses: [FetchClause] = []
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- parameter object: a reference to the object created/fetched outside the `FetchableSource`'s context
|
||||
- returns: the `DynamicObject` instance if the object exists in the `FetchableSource`'s context, or `nil` if not found.
|
||||
*/
|
||||
func fetchExisting<D: DynamicObject>(_ object: D) -> D?
|
||||
func fetchExisting<O: DynamicObject>(_ object: O) -> O?
|
||||
|
||||
/**
|
||||
Fetches the `DynamicObject` instance in the `FetchableSource`'s context from an `NSManagedObjectID`.
|
||||
@@ -48,7 +48,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- parameter objectID: the `NSManagedObjectID` for the object
|
||||
- returns: the `DynamicObject` instance if the object exists in the `FetchableSource`, or `nil` if not found.
|
||||
*/
|
||||
func fetchExisting<D: DynamicObject>(_ objectID: NSManagedObjectID) -> D?
|
||||
func fetchExisting<O: DynamicObject>(_ objectID: NSManagedObjectID) -> O?
|
||||
|
||||
/**
|
||||
Fetches the `DynamicObject` instances in the `FetchableSource`'s context from references created from another managed object context.
|
||||
@@ -56,7 +56,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- parameter objects: an array of `DynamicObject`s created/fetched outside the `FetchableSource`'s context
|
||||
- returns: the `DynamicObject` array for objects that exists in the `FetchableSource`
|
||||
*/
|
||||
func fetchExisting<D: DynamicObject, S: Sequence>(_ objects: S) -> [D] where S.Iterator.Element == D
|
||||
func fetchExisting<O: DynamicObject, S: Sequence>(_ objects: S) -> [O] where S.Iterator.Element == O
|
||||
|
||||
/**
|
||||
Fetches the `DynamicObject` instances in the `FetchableSource`'s context from a list of `NSManagedObjectID`.
|
||||
@@ -64,7 +64,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- parameter objectIDs: the `NSManagedObjectID` array for the objects
|
||||
- returns: the `DynamicObject` array for objects that exists in the `FetchableSource`'s context
|
||||
*/
|
||||
func fetchExisting<D: DynamicObject, S: Sequence>(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID
|
||||
func fetchExisting<O: DynamicObject, S: Sequence>(_ objectIDs: S) -> [O] where S.Iterator.Element == NSManagedObjectID
|
||||
|
||||
/**
|
||||
Fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
@@ -74,7 +74,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func fetchOne<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> D?
|
||||
func fetchOne<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> O?
|
||||
|
||||
/**
|
||||
Fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
@@ -84,7 +84,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func fetchOne<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> D?
|
||||
func fetchOne<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> O?
|
||||
|
||||
/**
|
||||
Fetches the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
@@ -109,7 +109,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func fetchAll<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> [D]
|
||||
func fetchAll<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> [O]
|
||||
|
||||
/**
|
||||
Fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
@@ -119,7 +119,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func fetchAll<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> [D]
|
||||
func fetchAll<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> [O]
|
||||
|
||||
/**
|
||||
Fetches all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
@@ -144,7 +144,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- returns: the number of `DynamicObject`s that satisfy the specified `FetchClause`s
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func fetchCount<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> Int
|
||||
func fetchCount<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> Int
|
||||
|
||||
/**
|
||||
Fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
@@ -154,7 +154,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- returns: the number of `DynamicObject`s that satisfy the specified `FetchClause`s
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func fetchCount<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> Int
|
||||
func fetchCount<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> Int
|
||||
|
||||
/**
|
||||
Fetches the number of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
@@ -179,7 +179,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> NSManagedObjectID?
|
||||
func fetchObjectID<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> NSManagedObjectID?
|
||||
|
||||
/**
|
||||
Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
@@ -189,7 +189,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s, or `nil` if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> NSManagedObjectID?
|
||||
func fetchObjectID<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> NSManagedObjectID?
|
||||
|
||||
/**
|
||||
Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
@@ -214,7 +214,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> [NSManagedObjectID]
|
||||
func fetchObjectIDs<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> [NSManagedObjectID]
|
||||
|
||||
/**
|
||||
Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
@@ -224,7 +224,7 @@ public protocol FetchableSource: AnyObject {
|
||||
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s, or an empty array if no match was found
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> [NSManagedObjectID]
|
||||
func fetchObjectIDs<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> [NSManagedObjectID]
|
||||
|
||||
/**
|
||||
Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
|
||||
|
||||
+130
-130
@@ -37,7 +37,7 @@ extension From {
|
||||
- parameter clause: the `Where` clause to create a `FetchChainBuilder` with
|
||||
- returns: a `FetchChainBuilder` that starts with the specified `Where` clause
|
||||
*/
|
||||
public func `where`(_ clause: Where<D>) -> FetchChainBuilder<D> {
|
||||
public func `where`(_ clause: Where<O>) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: clause)
|
||||
}
|
||||
@@ -49,9 +49,9 @@ extension From {
|
||||
- parameter args: the arguments for `format`
|
||||
- returns: a `FetchChainBuilder` with a predicate using the specified string format and arguments
|
||||
*/
|
||||
public func `where`(format: String, _ args: Any...) -> FetchChainBuilder<D> {
|
||||
public func `where`(format: String, _ args: Any...) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: Where<D>(format, argumentArray: args))
|
||||
return self.fetchChain(appending: Where<O>(format, argumentArray: args))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,9 +61,9 @@ extension From {
|
||||
- parameter argumentArray: the arguments for `format`
|
||||
- returns: a `FetchChainBuilder` with a predicate using the specified string format and arguments
|
||||
*/
|
||||
public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder<D> {
|
||||
public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: Where<D>(format, argumentArray: argumentArray))
|
||||
return self.fetchChain(appending: Where<O>(format, argumentArray: argumentArray))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +72,7 @@ extension From {
|
||||
- parameter clause: the `OrderBy` clause to create a `FetchChainBuilder` with
|
||||
- returns: a `FetchChainBuilder` that starts with the specified `OrderBy` clause
|
||||
*/
|
||||
public func orderBy(_ clause: OrderBy<D>) -> FetchChainBuilder<D> {
|
||||
public func orderBy(_ clause: OrderBy<O>) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: clause)
|
||||
}
|
||||
@@ -84,9 +84,9 @@ extension From {
|
||||
- parameter sortKeys: a series of other `SortKey`s
|
||||
- returns: a `FetchChainBuilder` with a series of `SortKey`s
|
||||
*/
|
||||
public func orderBy(_ sortKey: OrderBy<D>.SortKey, _ sortKeys: OrderBy<D>.SortKey...) -> FetchChainBuilder<D> {
|
||||
public func orderBy(_ sortKey: OrderBy<O>.SortKey, _ sortKeys: OrderBy<O>.SortKey...) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: OrderBy<D>([sortKey] + sortKeys))
|
||||
return self.fetchChain(appending: OrderBy<O>([sortKey] + sortKeys))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,9 +95,9 @@ extension From {
|
||||
- parameter sortKeys: a series of `SortKey`s
|
||||
- returns: a `FetchChainBuilder` with a series of `SortKey`s
|
||||
*/
|
||||
public func orderBy(_ sortKeys: [OrderBy<D>.SortKey]) -> FetchChainBuilder<D> {
|
||||
public func orderBy(_ sortKeys: [OrderBy<O>.SortKey]) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: OrderBy<D>(sortKeys))
|
||||
return self.fetchChain(appending: OrderBy<O>(sortKeys))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +106,7 @@ extension From {
|
||||
- parameter fetchRequest: the block to customize the `NSFetchRequest`
|
||||
- returns: a `FetchChainBuilder` with closure where the `NSFetchRequest` may be configured
|
||||
*/
|
||||
public func tweak(_ fetchRequest: @escaping (NSFetchRequest<NSFetchRequestResult>) -> Void) -> FetchChainBuilder<D> {
|
||||
public func tweak(_ fetchRequest: @escaping (NSFetchRequest<NSFetchRequestResult>) -> Void) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: Tweak(fetchRequest))
|
||||
}
|
||||
@@ -117,7 +117,7 @@ extension From {
|
||||
- parameter clause: the `FetchClause` to add to the `FetchChainBuilder`
|
||||
- returns: a `FetchChainBuilder` containing the specified `FetchClause`
|
||||
*/
|
||||
public func appending(_ clause: FetchClause) -> FetchChainBuilder<D> {
|
||||
public func appending(_ clause: FetchClause) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: clause)
|
||||
}
|
||||
@@ -128,7 +128,7 @@ extension From {
|
||||
- parameter clauses: the `FetchClause`s to add to the `FetchChainBuilder`
|
||||
- returns: a `FetchChainBuilder` containing the specified `FetchClause`s
|
||||
*/
|
||||
public func appending<S: Sequence>(contentsOf clauses: S) -> FetchChainBuilder<D> where S.Element == FetchClause {
|
||||
public func appending<S: Sequence>(contentsOf clauses: S) -> FetchChainBuilder<O> where S.Element == FetchClause {
|
||||
|
||||
return self.fetchChain(appending: clauses)
|
||||
}
|
||||
@@ -139,7 +139,7 @@ extension From {
|
||||
- parameter clause: the `Select` clause to create a `QueryChainBuilder` with
|
||||
- returns: a `QueryChainBuilder` that starts with the specified `Select` clause
|
||||
*/
|
||||
public func select<R>(_ clause: Select<D, R>) -> QueryChainBuilder<D, R> {
|
||||
public func select<R>(_ clause: Select<O, R>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return .init(
|
||||
from: self,
|
||||
@@ -156,7 +156,7 @@ extension From {
|
||||
- parameter selectTerms: a series of `SelectTerm`s
|
||||
- returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified `SelectTerm`s
|
||||
*/
|
||||
public func select<R>(_ resultType: R.Type, _ selectTerm: SelectTerm<D>, _ selectTerms: SelectTerm<D>...) -> QueryChainBuilder<D, R> {
|
||||
public func select<R>(_ resultType: R.Type, _ selectTerm: SelectTerm<O>, _ selectTerms: SelectTerm<O>...) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.select(resultType, [selectTerm] + selectTerms)
|
||||
}
|
||||
@@ -168,7 +168,7 @@ extension From {
|
||||
- parameter selectTerms: a series of `SelectTerm`s
|
||||
- returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified `SelectTerm`s
|
||||
*/
|
||||
public func select<R>(_ resultType: R.Type, _ selectTerms: [SelectTerm<D>]) -> QueryChainBuilder<D, R> {
|
||||
public func select<R>(_ resultType: R.Type, _ selectTerms: [SelectTerm<O>]) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return .init(
|
||||
from: self,
|
||||
@@ -184,7 +184,7 @@ extension From {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy(_ clause: SectionBy<D>) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy(_ clause: SectionBy<O>) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return .init(
|
||||
from: self,
|
||||
@@ -200,7 +200,7 @@ extension From {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy(_ sectionKeyPath: KeyPathString) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy(_ sectionKeyPath: KeyPathString) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(sectionKeyPath, { $0 })
|
||||
}
|
||||
@@ -214,7 +214,7 @@ extension From {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy(_ sectionKeyPath: KeyPathString, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy(_ sectionKeyPath: KeyPathString, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return .init(
|
||||
from: self,
|
||||
@@ -226,21 +226,21 @@ extension From {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func fetchChain(appending clause: FetchClause) -> FetchChainBuilder<D> {
|
||||
private func fetchChain(appending clause: FetchClause) -> FetchChainBuilder<O> {
|
||||
|
||||
return .init(from: self, fetchClauses: [clause])
|
||||
}
|
||||
|
||||
private func fetchChain<S: Sequence>(appending clauses: S) -> FetchChainBuilder<D> where S.Element == FetchClause {
|
||||
private func fetchChain<S: Sequence>(appending clauses: S) -> FetchChainBuilder<O> where S.Element == FetchClause {
|
||||
|
||||
return .init(from: self, fetchClauses: Array(clauses))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - From where D: NSManagedObject
|
||||
// MARK: - From where O: NSManagedObject
|
||||
|
||||
extension From where D: NSManagedObject {
|
||||
extension From where O: NSManagedObject {
|
||||
|
||||
/**
|
||||
Creates a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path
|
||||
@@ -248,9 +248,9 @@ extension From where D: NSManagedObject {
|
||||
- parameter keyPath: the keyPath to query the value for
|
||||
- returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path
|
||||
*/
|
||||
public func select<R>(_ keyPath: KeyPath<D, R>) -> QueryChainBuilder<D, R> {
|
||||
public func select<R>(_ keyPath: KeyPath<O, R>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.select(R.self, [SelectTerm<D>.attribute(keyPath)])
|
||||
return self.select(R.self, [SelectTerm<O>.attribute(keyPath)])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,7 +260,7 @@ extension From where D: NSManagedObject {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<D, T>) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, T>) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(sectionKeyPath._kvcKeyPathString!, { $0 })
|
||||
}
|
||||
@@ -274,16 +274,16 @@ extension From where D: NSManagedObject {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<D, T>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, T>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(sectionKeyPath._kvcKeyPathString!, sectionIndexTransformer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - From where D: CoreStoreObject
|
||||
// MARK: - From where O: CoreStoreObject
|
||||
|
||||
extension From where D: CoreStoreObject {
|
||||
extension From where O: CoreStoreObject {
|
||||
|
||||
/**
|
||||
Creates a `FetchChainBuilder` that starts with the specified `Where` clause
|
||||
@@ -291,12 +291,12 @@ extension From where D: CoreStoreObject {
|
||||
- parameter clause: a closure that returns a `Where` clause
|
||||
- returns: a `FetchChainBuilder` that starts with the specified `Where` clause
|
||||
*/
|
||||
public func `where`<T: AnyWhereClause>(_ clause: (D) -> T) -> FetchChainBuilder<D> {
|
||||
public func `where`<T: AnyWhereClause>(_ clause: (O) -> T) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: clause(D.meta))
|
||||
return self.fetchChain(appending: clause(O.meta))
|
||||
}
|
||||
|
||||
public func `where`(combinedByAnd clause: Where<D>, _ others: Where<D>...) -> FetchChainBuilder<D> {
|
||||
public func `where`(combinedByAnd clause: Where<O>, _ others: Where<O>...) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: ([clause] + others).combinedByAnd())
|
||||
}
|
||||
@@ -307,9 +307,9 @@ extension From where D: CoreStoreObject {
|
||||
- parameter keyPath: the keyPath to query the value for
|
||||
- returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path
|
||||
*/
|
||||
public func select<R>(_ keyPath: KeyPath<D, ValueContainer<D>.Required<R>>) -> QueryChainBuilder<D, R> {
|
||||
public func select<R>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<R>>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.select(R.self, [SelectTerm<D>.attribute(keyPath)])
|
||||
return self.select(R.self, [SelectTerm<O>.attribute(keyPath)])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,9 +318,9 @@ extension From where D: CoreStoreObject {
|
||||
- parameter keyPath: the keyPath to query the value for
|
||||
- returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path
|
||||
*/
|
||||
public func select<R>(_ keyPath: KeyPath<D, ValueContainer<D>.Optional<R>>) -> QueryChainBuilder<D, R> {
|
||||
public func select<R>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<R>>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.select(R.self, [SelectTerm<D>.attribute(keyPath)])
|
||||
return self.select(R.self, [SelectTerm<O>.attribute(keyPath)])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -329,9 +329,9 @@ extension From where D: CoreStoreObject {
|
||||
- parameter keyPath: the keyPath to query the value for
|
||||
- returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path
|
||||
*/
|
||||
public func select<R>(_ keyPath: KeyPath<D, TransformableContainer<D>.Required<R>>) -> QueryChainBuilder<D, R> {
|
||||
public func select<R>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<R>>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.select(R.self, [SelectTerm<D>.attribute(keyPath)])
|
||||
return self.select(R.self, [SelectTerm<O>.attribute(keyPath)])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,9 +340,9 @@ extension From where D: CoreStoreObject {
|
||||
- parameter keyPath: the keyPath to query the value for
|
||||
- returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified key path
|
||||
*/
|
||||
public func select<R>(_ keyPath: KeyPath<D, TransformableContainer<D>.Optional<R>>) -> QueryChainBuilder<D, R> {
|
||||
public func select<R>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<R>>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.select(R.self, [SelectTerm<D>.attribute(keyPath)])
|
||||
return self.select(R.self, [SelectTerm<O>.attribute(keyPath)])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,9 +352,9 @@ extension From where D: CoreStoreObject {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<D, ValueContainer<D>.Required<T>>) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, ValueContainer<O>.Required<T>>) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 })
|
||||
return self.sectionBy(O.meta[keyPath: sectionKeyPath].keyPath, { $0 })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,9 +364,9 @@ extension From where D: CoreStoreObject {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<D, ValueContainer<D>.Optional<T>>) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, ValueContainer<O>.Optional<T>>) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 })
|
||||
return self.sectionBy(O.meta[keyPath: sectionKeyPath].keyPath, { $0 })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -376,9 +376,9 @@ extension From where D: CoreStoreObject {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<D, TransformableContainer<D>.Required<T>>) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, TransformableContainer<O>.Required<T>>) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 })
|
||||
return self.sectionBy(O.meta[keyPath: sectionKeyPath].keyPath, { $0 })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,9 +388,9 @@ extension From where D: CoreStoreObject {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<D, TransformableContainer<D>.Optional<T>>) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, TransformableContainer<O>.Optional<T>>) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 })
|
||||
return self.sectionBy(O.meta[keyPath: sectionKeyPath].keyPath, { $0 })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -402,9 +402,9 @@ extension From where D: CoreStoreObject {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<D, ValueContainer<D>.Required<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, ValueContainer<O>.Required<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
return self.sectionBy(O.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,9 +416,9 @@ extension From where D: CoreStoreObject {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<D, ValueContainer<D>.Optional<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, ValueContainer<O>.Optional<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
return self.sectionBy(O.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -430,9 +430,9 @@ extension From where D: CoreStoreObject {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<D, TransformableContainer<D>.Required<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, TransformableContainer<O>.Required<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
return self.sectionBy(O.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -444,9 +444,9 @@ extension From where D: CoreStoreObject {
|
||||
- returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<D, TransformableContainer<D>.Optional<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<D> {
|
||||
public func sectionBy<T>(_ sectionKeyPath: KeyPath<O, TransformableContainer<O>.Optional<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
return self.sectionBy(O.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,7 +461,7 @@ extension FetchChainBuilder {
|
||||
- parameter clause: a `Where` clause to add to the fetch builder
|
||||
- returns: a new `FetchChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`(_ clause: Where<D>) -> FetchChainBuilder<D> {
|
||||
public func `where`(_ clause: Where<O>) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: clause)
|
||||
}
|
||||
@@ -473,9 +473,9 @@ extension FetchChainBuilder {
|
||||
- parameter args: the arguments for `format`
|
||||
- returns: a new `FetchChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`(format: String, _ args: Any...) -> FetchChainBuilder<D> {
|
||||
public func `where`(format: String, _ args: Any...) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: Where<D>(format, argumentArray: args))
|
||||
return self.fetchChain(appending: Where<O>(format, argumentArray: args))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,9 +485,9 @@ extension FetchChainBuilder {
|
||||
- parameter argumentArray: the arguments for `format`
|
||||
- returns: a new `FetchChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder<D> {
|
||||
public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: Where<D>(format, argumentArray: argumentArray))
|
||||
return self.fetchChain(appending: Where<O>(format, argumentArray: argumentArray))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -496,7 +496,7 @@ extension FetchChainBuilder {
|
||||
- parameter clause: the `OrderBy` clause to add
|
||||
- returns: a new `FetchChainBuilder` containing the `OrderBy` clause
|
||||
*/
|
||||
public func orderBy(_ clause: OrderBy<D>) -> FetchChainBuilder<D> {
|
||||
public func orderBy(_ clause: OrderBy<O>) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: clause)
|
||||
}
|
||||
@@ -508,9 +508,9 @@ extension FetchChainBuilder {
|
||||
- parameter sortKeys: a series of other `SortKey`s
|
||||
- returns: a new `FetchChainBuilder` containing the `OrderBy` clause
|
||||
*/
|
||||
public func orderBy(_ sortKey: OrderBy<D>.SortKey, _ sortKeys: OrderBy<D>.SortKey...) -> FetchChainBuilder<D> {
|
||||
public func orderBy(_ sortKey: OrderBy<O>.SortKey, _ sortKeys: OrderBy<O>.SortKey...) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: OrderBy<D>([sortKey] + sortKeys))
|
||||
return self.fetchChain(appending: OrderBy<O>([sortKey] + sortKeys))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,9 +519,9 @@ extension FetchChainBuilder {
|
||||
- parameter sortKeys: a series of `SortKey`s
|
||||
- returns: a new `FetchChainBuilder` containing the `OrderBy` clause
|
||||
*/
|
||||
public func orderBy(_ sortKeys: [OrderBy<D>.SortKey]) -> FetchChainBuilder<D> {
|
||||
public func orderBy(_ sortKeys: [OrderBy<O>.SortKey]) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: OrderBy<D>(sortKeys))
|
||||
return self.fetchChain(appending: OrderBy<O>(sortKeys))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,7 +530,7 @@ extension FetchChainBuilder {
|
||||
- parameter fetchRequest: the block to customize the `NSFetchRequest`
|
||||
- returns: a new `FetchChainBuilder` containing the `Tweak` clause
|
||||
*/
|
||||
public func tweak(_ fetchRequest: @escaping (NSFetchRequest<NSFetchRequestResult>) -> Void) -> FetchChainBuilder<D> {
|
||||
public func tweak(_ fetchRequest: @escaping (NSFetchRequest<NSFetchRequestResult>) -> Void) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: Tweak(fetchRequest))
|
||||
}
|
||||
@@ -541,7 +541,7 @@ extension FetchChainBuilder {
|
||||
- parameter clause: the `FetchClause` to add to the `FetchChainBuilder`
|
||||
- returns: a new `FetchChainBuilder` containing the `FetchClause`
|
||||
*/
|
||||
public func appending(_ clause: FetchClause) -> FetchChainBuilder<D> {
|
||||
public func appending(_ clause: FetchClause) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: clause)
|
||||
}
|
||||
@@ -552,7 +552,7 @@ extension FetchChainBuilder {
|
||||
- parameter clauses: the `FetchClause`s to add to the `FetchChainBuilder`
|
||||
- returns: a new `FetchChainBuilder` containing the `FetchClause`s
|
||||
*/
|
||||
public func appending<S: Sequence>(contentsOf clauses: S) -> FetchChainBuilder<D> where S.Element == FetchClause {
|
||||
public func appending<S: Sequence>(contentsOf clauses: S) -> FetchChainBuilder<O> where S.Element == FetchClause {
|
||||
|
||||
return self.fetchChain(appending: clauses)
|
||||
}
|
||||
@@ -560,7 +560,7 @@ extension FetchChainBuilder {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func fetchChain(appending clause: FetchClause) -> FetchChainBuilder<D> {
|
||||
private func fetchChain(appending clause: FetchClause) -> FetchChainBuilder<O> {
|
||||
|
||||
return .init(
|
||||
from: self.from,
|
||||
@@ -568,7 +568,7 @@ extension FetchChainBuilder {
|
||||
)
|
||||
}
|
||||
|
||||
private func fetchChain<S: Sequence>(appending clauses: S) -> FetchChainBuilder<D> where S.Element == FetchClause {
|
||||
private func fetchChain<S: Sequence>(appending clauses: S) -> FetchChainBuilder<O> where S.Element == FetchClause {
|
||||
|
||||
return .init(
|
||||
from: self.from,
|
||||
@@ -578,13 +578,13 @@ extension FetchChainBuilder {
|
||||
}
|
||||
|
||||
|
||||
// MARK: - FetchChainBuilder where D: CoreStoreObject
|
||||
// MARK: - FetchChainBuilder where O: CoreStoreObject
|
||||
|
||||
extension FetchChainBuilder where D: CoreStoreObject {
|
||||
extension FetchChainBuilder where O: CoreStoreObject {
|
||||
|
||||
public func `where`<T: AnyWhereClause>(_ clause: (D) -> T) -> FetchChainBuilder<D> {
|
||||
public func `where`<T: AnyWhereClause>(_ clause: (O) -> T) -> FetchChainBuilder<O> {
|
||||
|
||||
return self.fetchChain(appending: clause(D.meta))
|
||||
return self.fetchChain(appending: clause(O.meta))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,7 +599,7 @@ extension QueryChainBuilder {
|
||||
- parameter clause: a `Where` clause to add to the query builder
|
||||
- returns: a new `QueryChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`(_ clause: Where<D>) -> QueryChainBuilder<D, R> {
|
||||
public func `where`(_ clause: Where<O>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: clause)
|
||||
}
|
||||
@@ -611,9 +611,9 @@ extension QueryChainBuilder {
|
||||
- parameter args: the arguments for `format`
|
||||
- returns: a new `QueryChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`(format: String, _ args: Any...) -> QueryChainBuilder<D, R> {
|
||||
public func `where`(format: String, _ args: Any...) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: Where<D>(format, argumentArray: args))
|
||||
return self.queryChain(appending: Where<O>(format, argumentArray: args))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -623,9 +623,9 @@ extension QueryChainBuilder {
|
||||
- parameter argumentArray: the arguments for `format`
|
||||
- returns: a new `QueryChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`(format: String, argumentArray: [Any]?) -> QueryChainBuilder<D, R> {
|
||||
public func `where`(format: String, argumentArray: [Any]?) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: Where<D>(format, argumentArray: argumentArray))
|
||||
return self.queryChain(appending: Where<O>(format, argumentArray: argumentArray))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -634,7 +634,7 @@ extension QueryChainBuilder {
|
||||
- parameter clause: the `OrderBy` clause to add
|
||||
- returns: a new `QueryChainBuilder` containing the `OrderBy` clause
|
||||
*/
|
||||
public func orderBy(_ clause: OrderBy<D>) -> QueryChainBuilder<D, R> {
|
||||
public func orderBy(_ clause: OrderBy<O>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: clause)
|
||||
}
|
||||
@@ -646,9 +646,9 @@ extension QueryChainBuilder {
|
||||
- parameter sortKeys: a series of other `SortKey`s
|
||||
- returns: a new `QueryChainBuilder` containing the `OrderBy` clause
|
||||
*/
|
||||
public func orderBy(_ sortKey: OrderBy<D>.SortKey, _ sortKeys: OrderBy<D>.SortKey...) -> QueryChainBuilder<D, R> {
|
||||
public func orderBy(_ sortKey: OrderBy<O>.SortKey, _ sortKeys: OrderBy<O>.SortKey...) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: OrderBy<D>([sortKey] + sortKeys))
|
||||
return self.queryChain(appending: OrderBy<O>([sortKey] + sortKeys))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -657,9 +657,9 @@ extension QueryChainBuilder {
|
||||
- parameter sortKeys: a series of `SortKey`s
|
||||
- returns: a new `QueryChainBuilder` containing the `OrderBy` clause
|
||||
*/
|
||||
public func orderBy(_ sortKeys: [OrderBy<D>.SortKey]) -> QueryChainBuilder<D, R> {
|
||||
public func orderBy(_ sortKeys: [OrderBy<O>.SortKey]) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: OrderBy<D>(sortKeys))
|
||||
return self.queryChain(appending: OrderBy<O>(sortKeys))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -668,7 +668,7 @@ extension QueryChainBuilder {
|
||||
- parameter fetchRequest: the block to customize the `NSFetchRequest`
|
||||
- returns: a new `QueryChainBuilder` containing the `Tweak` clause
|
||||
*/
|
||||
public func tweak(_ fetchRequest: @escaping (NSFetchRequest<NSFetchRequestResult>) -> Void) -> QueryChainBuilder<D, R> {
|
||||
public func tweak(_ fetchRequest: @escaping (NSFetchRequest<NSFetchRequestResult>) -> Void) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: Tweak(fetchRequest))
|
||||
}
|
||||
@@ -679,7 +679,7 @@ extension QueryChainBuilder {
|
||||
- parameter clause: a `GroupBy` clause to add to the query builder
|
||||
- returns: a new `QueryChainBuilder` containing the `GroupBy` clause
|
||||
*/
|
||||
public func groupBy(_ clause: GroupBy<D>) -> QueryChainBuilder<D, R> {
|
||||
public func groupBy(_ clause: GroupBy<O>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: clause)
|
||||
}
|
||||
@@ -691,9 +691,9 @@ extension QueryChainBuilder {
|
||||
- parameter keyPaths: other key paths to group the query results with
|
||||
- returns: a new `QueryChainBuilder` containing the `GroupBy` clause
|
||||
*/
|
||||
public func groupBy(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) -> QueryChainBuilder<D, R> {
|
||||
public func groupBy(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.groupBy(GroupBy<D>([keyPath] + keyPaths))
|
||||
return self.groupBy(GroupBy<O>([keyPath] + keyPaths))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -702,9 +702,9 @@ extension QueryChainBuilder {
|
||||
- parameter keyPaths: a series of key paths to group the query results with
|
||||
- returns: a new `QueryChainBuilder` containing the `GroupBy` clause
|
||||
*/
|
||||
public func groupBy(_ keyPaths: [KeyPathString]) -> QueryChainBuilder<D, R> {
|
||||
public func groupBy(_ keyPaths: [KeyPathString]) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: GroupBy<D>(keyPaths))
|
||||
return self.queryChain(appending: GroupBy<O>(keyPaths))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -713,7 +713,7 @@ extension QueryChainBuilder {
|
||||
- parameter clause: the `QueryClause` to add to the `QueryChainBuilder`
|
||||
- returns: a new `QueryChainBuilder` containing the `QueryClause`
|
||||
*/
|
||||
public func appending(_ clause: QueryClause) -> QueryChainBuilder<D, R> {
|
||||
public func appending(_ clause: QueryClause) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: clause)
|
||||
}
|
||||
@@ -724,7 +724,7 @@ extension QueryChainBuilder {
|
||||
- parameter clauses: the `QueryClause`s to add to the `QueryChainBuilder`
|
||||
- returns: a new `QueryChainBuilder` containing the `QueryClause`s
|
||||
*/
|
||||
public func appending<S: Sequence>(contentsOf clauses: S) -> QueryChainBuilder<D, R> where S.Element == QueryClause {
|
||||
public func appending<S: Sequence>(contentsOf clauses: S) -> QueryChainBuilder<O, R> where S.Element == QueryClause {
|
||||
|
||||
return self.queryChain(appending: clauses)
|
||||
}
|
||||
@@ -732,7 +732,7 @@ extension QueryChainBuilder {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func queryChain(appending clause: QueryClause) -> QueryChainBuilder<D, R> {
|
||||
private func queryChain(appending clause: QueryClause) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return .init(
|
||||
from: self.from,
|
||||
@@ -741,7 +741,7 @@ extension QueryChainBuilder {
|
||||
)
|
||||
}
|
||||
|
||||
private func queryChain<S: Sequence>(appending clauses: S) -> QueryChainBuilder<D, R> where S.Element == QueryClause {
|
||||
private func queryChain<S: Sequence>(appending clauses: S) -> QueryChainBuilder<O, R> where S.Element == QueryClause {
|
||||
|
||||
return .init(
|
||||
from: self.from,
|
||||
@@ -752,9 +752,9 @@ extension QueryChainBuilder {
|
||||
}
|
||||
|
||||
|
||||
// MARK: - QueryChainBuilder where D: NSManagedObject
|
||||
// MARK: - QueryChainBuilder where O: NSManagedObject
|
||||
|
||||
extension QueryChainBuilder where D: NSManagedObject {
|
||||
extension QueryChainBuilder where O: NSManagedObject {
|
||||
|
||||
/**
|
||||
Adds a `GroupBy` clause to the `QueryChainBuilder`
|
||||
@@ -762,16 +762,16 @@ extension QueryChainBuilder where D: NSManagedObject {
|
||||
- parameter keyPath: a key path to group the query results with
|
||||
- returns: a new `QueryChainBuilder` containing the `GroupBy` clause
|
||||
*/
|
||||
public func groupBy<T>(_ keyPath: KeyPath<D, T>) -> QueryChainBuilder<D, R> {
|
||||
public func groupBy<T>(_ keyPath: KeyPath<O, T>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.groupBy(GroupBy<D>(keyPath))
|
||||
return self.groupBy(GroupBy<O>(keyPath))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - QueryChainBuilder where D: CoreStoreObject
|
||||
// MARK: - QueryChainBuilder where O: CoreStoreObject
|
||||
|
||||
extension QueryChainBuilder where D: CoreStoreObject {
|
||||
extension QueryChainBuilder where O: CoreStoreObject {
|
||||
|
||||
/**
|
||||
Adds a `Where` clause to the `QueryChainBuilder`
|
||||
@@ -779,9 +779,9 @@ extension QueryChainBuilder where D: CoreStoreObject {
|
||||
- parameter clause: a `Where` clause to add to the query builder
|
||||
- returns: a new `QueryChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`<T: AnyWhereClause>(_ clause: (D) -> T) -> QueryChainBuilder<D, R> {
|
||||
public func `where`<T: AnyWhereClause>(_ clause: (O) -> T) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.queryChain(appending: clause(D.meta))
|
||||
return self.queryChain(appending: clause(O.meta))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -790,9 +790,9 @@ extension QueryChainBuilder where D: CoreStoreObject {
|
||||
- parameter keyPath: a key path to group the query results with
|
||||
- returns: a new `QueryChainBuilder` containing the `GroupBy` clause
|
||||
*/
|
||||
public func groupBy<T>(_ keyPath: KeyPath<D, ValueContainer<D>.Required<T>>) -> QueryChainBuilder<D, R> {
|
||||
public func groupBy<T>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<T>>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.groupBy(GroupBy<D>(keyPath))
|
||||
return self.groupBy(GroupBy<O>(keyPath))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -801,9 +801,9 @@ extension QueryChainBuilder where D: CoreStoreObject {
|
||||
- parameter keyPath: a key path to group the query results with
|
||||
- returns: a new `QueryChainBuilder` containing the `GroupBy` clause
|
||||
*/
|
||||
public func groupBy<T>(_ keyPath: KeyPath<D, ValueContainer<D>.Optional<T>>) -> QueryChainBuilder<D, R> {
|
||||
public func groupBy<T>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<T>>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.groupBy(GroupBy<D>(keyPath))
|
||||
return self.groupBy(GroupBy<O>(keyPath))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -812,9 +812,9 @@ extension QueryChainBuilder where D: CoreStoreObject {
|
||||
- parameter keyPath: a key path to group the query results with
|
||||
- returns: a new `QueryChainBuilder` containing the `GroupBy` clause
|
||||
*/
|
||||
public func groupBy<T>(_ keyPath: KeyPath<D, TransformableContainer<D>.Required<T>>) -> QueryChainBuilder<D, R> {
|
||||
public func groupBy<T>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<T>>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.groupBy(GroupBy<D>(keyPath))
|
||||
return self.groupBy(GroupBy<O>(keyPath))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -823,9 +823,9 @@ extension QueryChainBuilder where D: CoreStoreObject {
|
||||
- parameter keyPath: a key path to group the query results with
|
||||
- returns: a new `QueryChainBuilder` containing the `GroupBy` clause
|
||||
*/
|
||||
public func groupBy<T>(_ keyPath: KeyPath<D, TransformableContainer<D>.Optional<T>>) -> QueryChainBuilder<D, R> {
|
||||
public func groupBy<T>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<T>>) -> QueryChainBuilder<O, R> {
|
||||
|
||||
return self.groupBy(GroupBy<D>(keyPath))
|
||||
return self.groupBy(GroupBy<O>(keyPath))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -841,7 +841,7 @@ extension SectionMonitorChainBuilder {
|
||||
- parameter clause: a `Where` clause to add to the fetch builder
|
||||
- returns: a new `SectionMonitorChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`(_ clause: Where<D>) -> SectionMonitorChainBuilder<D> {
|
||||
public func `where`(_ clause: Where<O>) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionMonitorChain(appending: clause)
|
||||
}
|
||||
@@ -853,9 +853,9 @@ extension SectionMonitorChainBuilder {
|
||||
- parameter args: the arguments for `format`
|
||||
- returns: a new `SectionMonitorChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`(format: String, _ args: Any...) -> SectionMonitorChainBuilder<D> {
|
||||
public func `where`(format: String, _ args: Any...) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionMonitorChain(appending: Where<D>(format, argumentArray: args))
|
||||
return self.sectionMonitorChain(appending: Where<O>(format, argumentArray: args))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -865,9 +865,9 @@ extension SectionMonitorChainBuilder {
|
||||
- parameter argumentArray: the arguments for `format`
|
||||
- returns: a new `SectionMonitorChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`(format: String, argumentArray: [Any]?) -> SectionMonitorChainBuilder<D> {
|
||||
public func `where`(format: String, argumentArray: [Any]?) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionMonitorChain(appending: Where<D>(format, argumentArray: argumentArray))
|
||||
return self.sectionMonitorChain(appending: Where<O>(format, argumentArray: argumentArray))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -876,7 +876,7 @@ extension SectionMonitorChainBuilder {
|
||||
- parameter clause: the `OrderBy` clause to add
|
||||
- returns: a new `SectionMonitorChainBuilder` containing the `OrderBy` clause
|
||||
*/
|
||||
public func orderBy(_ clause: OrderBy<D>) -> SectionMonitorChainBuilder<D> {
|
||||
public func orderBy(_ clause: OrderBy<O>) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionMonitorChain(appending: clause)
|
||||
}
|
||||
@@ -888,9 +888,9 @@ extension SectionMonitorChainBuilder {
|
||||
- parameter sortKeys: a series of other `SortKey`s
|
||||
- returns: a new `SectionMonitorChainBuilder` containing the `OrderBy` clause
|
||||
*/
|
||||
public func orderBy(_ sortKey: OrderBy<D>.SortKey, _ sortKeys: OrderBy<D>.SortKey...) -> SectionMonitorChainBuilder<D> {
|
||||
public func orderBy(_ sortKey: OrderBy<O>.SortKey, _ sortKeys: OrderBy<O>.SortKey...) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionMonitorChain(appending: OrderBy<D>([sortKey] + sortKeys))
|
||||
return self.sectionMonitorChain(appending: OrderBy<O>([sortKey] + sortKeys))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -899,9 +899,9 @@ extension SectionMonitorChainBuilder {
|
||||
- parameter sortKeys: a series of `SortKey`s
|
||||
- returns: a new `SectionMonitorChainBuilder` containing the `OrderBy` clause
|
||||
*/
|
||||
public func orderBy(_ sortKeys: [OrderBy<D>.SortKey]) -> SectionMonitorChainBuilder<D> {
|
||||
public func orderBy(_ sortKeys: [OrderBy<O>.SortKey]) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionMonitorChain(appending: OrderBy<D>(sortKeys))
|
||||
return self.sectionMonitorChain(appending: OrderBy<O>(sortKeys))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -910,7 +910,7 @@ extension SectionMonitorChainBuilder {
|
||||
- parameter fetchRequest: the block to customize the `NSFetchRequest`
|
||||
- returns: a new `SectionMonitorChainBuilder` containing the `Tweak` clause
|
||||
*/
|
||||
public func tweak(_ fetchRequest: @escaping (NSFetchRequest<NSFetchRequestResult>) -> Void) -> SectionMonitorChainBuilder<D> {
|
||||
public func tweak(_ fetchRequest: @escaping (NSFetchRequest<NSFetchRequestResult>) -> Void) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionMonitorChain(appending: Tweak(fetchRequest))
|
||||
}
|
||||
@@ -921,7 +921,7 @@ extension SectionMonitorChainBuilder {
|
||||
- parameter clause: the `QueryClause` to add to the `SectionMonitorChainBuilder`
|
||||
- returns: a new `SectionMonitorChainBuilder` containing the `QueryClause`
|
||||
*/
|
||||
public func appending(_ clause: FetchClause) -> SectionMonitorChainBuilder<D> {
|
||||
public func appending(_ clause: FetchClause) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionMonitorChain(appending: clause)
|
||||
}
|
||||
@@ -932,7 +932,7 @@ extension SectionMonitorChainBuilder {
|
||||
- parameter clauses: the `QueryClause`s to add to the `SectionMonitorChainBuilder`
|
||||
- returns: a new `SectionMonitorChainBuilder` containing the `QueryClause`s
|
||||
*/
|
||||
public func appending<S: Sequence>(contentsOf clauses: S) -> SectionMonitorChainBuilder<D> where S.Element == FetchClause {
|
||||
public func appending<S: Sequence>(contentsOf clauses: S) -> SectionMonitorChainBuilder<O> where S.Element == FetchClause {
|
||||
|
||||
return self.sectionMonitorChain(appending: clauses)
|
||||
}
|
||||
@@ -940,7 +940,7 @@ extension SectionMonitorChainBuilder {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func sectionMonitorChain(appending clause: FetchClause) -> SectionMonitorChainBuilder<D> {
|
||||
private func sectionMonitorChain(appending clause: FetchClause) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return .init(
|
||||
from: self.from,
|
||||
@@ -949,7 +949,7 @@ extension SectionMonitorChainBuilder {
|
||||
)
|
||||
}
|
||||
|
||||
private func sectionMonitorChain<S: Sequence>(appending clauses: S) -> SectionMonitorChainBuilder<D> where S.Element == FetchClause {
|
||||
private func sectionMonitorChain<S: Sequence>(appending clauses: S) -> SectionMonitorChainBuilder<O> where S.Element == FetchClause {
|
||||
|
||||
return .init(
|
||||
from: self.from,
|
||||
@@ -960,10 +960,10 @@ extension SectionMonitorChainBuilder {
|
||||
}
|
||||
|
||||
|
||||
// MARK: - SectionMonitorChainBuilder where D: CoreStoreObject
|
||||
// MARK: - SectionMonitorChainBuilder where O: CoreStoreObject
|
||||
|
||||
@available(macOS 10.12, *)
|
||||
extension SectionMonitorChainBuilder where D: CoreStoreObject {
|
||||
extension SectionMonitorChainBuilder where O: CoreStoreObject {
|
||||
|
||||
/**
|
||||
Adds a `Where` clause to the `SectionMonitorChainBuilder`
|
||||
@@ -971,8 +971,8 @@ extension SectionMonitorChainBuilder where D: CoreStoreObject {
|
||||
- parameter clause: a `Where` clause to add to the fetch builder
|
||||
- returns: a new `SectionMonitorChainBuilder` containing the `Where` clause
|
||||
*/
|
||||
public func `where`<T: AnyWhereClause>(_ clause: (D) -> T) -> SectionMonitorChainBuilder<D> {
|
||||
public func `where`<T: AnyWhereClause>(_ clause: (O) -> T) -> SectionMonitorChainBuilder<O> {
|
||||
|
||||
return self.sectionMonitorChain(appending: clause(D.meta))
|
||||
return self.sectionMonitorChain(appending: clause(O.meta))
|
||||
}
|
||||
}
|
||||
|
||||
+16
-10
@@ -39,12 +39,12 @@ import CoreData
|
||||
let person = transaction.fetchOne(From<Person>("Configuration1"))
|
||||
```
|
||||
*/
|
||||
public struct From<D: DynamicObject> {
|
||||
public struct From<O: DynamicObject> {
|
||||
|
||||
/**
|
||||
The associated `NSManagedObject` or `CoreStoreObject` entity class
|
||||
*/
|
||||
public let entityClass: D.Type
|
||||
public let entityClass: O.Type
|
||||
|
||||
/**
|
||||
The `NSPersistentStore` configuration names to associate objects from.
|
||||
@@ -60,7 +60,7 @@ public struct From<D: DynamicObject> {
|
||||
*/
|
||||
public init() {
|
||||
|
||||
self.init(entityClass: D.self, configurations: nil)
|
||||
self.init(entityClass: O.self, configurations: nil)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +70,7 @@ public struct From<D: DynamicObject> {
|
||||
```
|
||||
- parameter entity: the associated `NSManagedObject` or `CoreStoreObject` type
|
||||
*/
|
||||
public init(_ entity: D.Type) {
|
||||
public init(_ entity: O.Type) {
|
||||
|
||||
self.init(entityClass: entity, configurations: nil)
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public struct From<D: DynamicObject> {
|
||||
*/
|
||||
public init(_ configuration: ModelConfiguration, _ otherConfigurations: ModelConfiguration...) {
|
||||
|
||||
self.init(entityClass: D.self, configurations: [configuration] + otherConfigurations)
|
||||
self.init(entityClass: O.self, configurations: [configuration] + otherConfigurations)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,7 +97,7 @@ public struct From<D: DynamicObject> {
|
||||
*/
|
||||
public init(_ configurations: [ModelConfiguration]) {
|
||||
|
||||
self.init(entityClass: D.self, configurations: configurations)
|
||||
self.init(entityClass: O.self, configurations: configurations)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +109,7 @@ public struct From<D: DynamicObject> {
|
||||
- parameter configuration: the `NSPersistentStore` configuration name to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject` or `CoreStoreObject`'s entity type. Set to `nil` to use the default configuration.
|
||||
- parameter otherConfigurations: an optional list of other configuration names to associate objects from (see `configuration` parameter)
|
||||
*/
|
||||
public init(_ entity: D.Type, _ configuration: ModelConfiguration, _ otherConfigurations: ModelConfiguration...) {
|
||||
public init(_ entity: O.Type, _ configuration: ModelConfiguration, _ otherConfigurations: ModelConfiguration...) {
|
||||
|
||||
self.init(entityClass: entity, configurations: [configuration] + otherConfigurations)
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public struct From<D: DynamicObject> {
|
||||
- parameter entity: the associated `NSManagedObject` or `CoreStoreObject` type
|
||||
- parameter configurations: a list of `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject` or `CoreStoreObject`'s entity type. Set to `nil` to use the default configuration.
|
||||
*/
|
||||
public init(_ entity: D.Type, _ configurations: [ModelConfiguration]) {
|
||||
public init(_ entity: O.Type, _ configurations: [ModelConfiguration]) {
|
||||
|
||||
self.init(entityClass: entity, configurations: configurations)
|
||||
}
|
||||
@@ -132,7 +132,7 @@ public struct From<D: DynamicObject> {
|
||||
|
||||
internal let findPersistentStores: (_ context: NSManagedObjectContext) -> [NSPersistentStore]?
|
||||
|
||||
internal init(entityClass: D.Type, configurations: [ModelConfiguration]?, findPersistentStores: @escaping (_ context: NSManagedObjectContext) -> [NSPersistentStore]?) {
|
||||
internal init(entityClass: O.Type, configurations: [ModelConfiguration]?, findPersistentStores: @escaping (_ context: NSManagedObjectContext) -> [NSPersistentStore]?) {
|
||||
|
||||
self.entityClass = entityClass
|
||||
self.configurations = configurations
|
||||
@@ -186,7 +186,7 @@ public struct From<D: DynamicObject> {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private init(entityClass: D.Type, configurations: [ModelConfiguration]?) {
|
||||
private init(entityClass: O.Type, configurations: [ModelConfiguration]?) {
|
||||
|
||||
self.entityClass = entityClass
|
||||
self.configurations = configurations
|
||||
@@ -211,4 +211,10 @@ public struct From<D: DynamicObject> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
//
|
||||
// Functions.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
// MARK: Associated Objects
|
||||
|
||||
@inline(__always)
|
||||
internal func cs_getAssociatedObjectForKey<T: AnyObject>(_ key: UnsafeRawPointer, inObject object: Any) -> T? {
|
||||
|
||||
switch objc_getAssociatedObject(object, key) {
|
||||
|
||||
case let associatedObject as T:
|
||||
return associatedObject
|
||||
|
||||
case let associatedObject as WeakObject:
|
||||
return associatedObject.object as? T
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func cs_setAssociatedRetainedObject<T: AnyObject>(_ associatedObject: T?, forKey key: UnsafeRawPointer, inObject object: Any) {
|
||||
|
||||
objc_setAssociatedObject(object, key, associatedObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func cs_setAssociatedCopiedObject<T: AnyObject>(_ associatedObject: T?, forKey key: UnsafeRawPointer, inObject object: Any) {
|
||||
|
||||
objc_setAssociatedObject(object, key, associatedObject, .OBJC_ASSOCIATION_COPY_NONATOMIC)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func cs_setAssociatedWeakObject<T: AnyObject>(_ associatedObject: T?, forKey key: UnsafeRawPointer, inObject object: Any) {
|
||||
|
||||
if let associatedObject = associatedObject {
|
||||
|
||||
objc_setAssociatedObject(object, key, WeakObject(associatedObject), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
else {
|
||||
|
||||
objc_setAssociatedObject(object, key, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Printing Utilities
|
||||
|
||||
@inline(__always)
|
||||
internal func cs_typeName<T>(_ value: T) -> String {
|
||||
|
||||
return "'\(String(reflecting: type(of: value)))'"
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func cs_typeName<T>(_ value: T.Type) -> String {
|
||||
|
||||
return "'\(value)'"
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func cs_typeName(_ value: AnyClass) -> String {
|
||||
|
||||
return "'\(value)'"
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func cs_typeName(_ name: String) -> String {
|
||||
|
||||
return "<\(name)>"
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func cs_typeName(_ name: String?) -> String {
|
||||
|
||||
return "<\(name ?? "unknown")>"
|
||||
}
|
||||
|
||||
|
||||
// MARK: Functional
|
||||
|
||||
@inline(__always)
|
||||
internal func cs_lazy<T>(_ closure: () -> T) -> T {
|
||||
|
||||
return closure()
|
||||
}
|
||||
+19
-13
@@ -32,7 +32,7 @@ import CoreData
|
||||
/**
|
||||
The `GroupBy` clause specifies that the result of a query be grouped accoording to the specified key path.
|
||||
*/
|
||||
public struct GroupBy<D: DynamicObject>: GroupByClause, QueryClause, Hashable {
|
||||
public struct GroupBy<O: DynamicObject>: GroupByClause, QueryClause, Hashable {
|
||||
|
||||
/**
|
||||
Initializes a `GroupBy` clause with an empty list of key path strings
|
||||
@@ -66,7 +66,7 @@ public struct GroupBy<D: DynamicObject>: GroupByClause, QueryClause, Hashable {
|
||||
|
||||
// MARK: GroupByClause
|
||||
|
||||
public typealias ObjectType = D
|
||||
public typealias ObjectType = O
|
||||
|
||||
public let keyPaths: [KeyPathString]
|
||||
|
||||
@@ -101,31 +101,37 @@ public struct GroupBy<D: DynamicObject>: GroupByClause, QueryClause, Hashable {
|
||||
|
||||
hasher.combine(self.keyPaths)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
extension GroupBy where D: NSManagedObject {
|
||||
extension GroupBy where O: NSManagedObject {
|
||||
|
||||
/**
|
||||
Initializes a `GroupBy` clause with a key path
|
||||
|
||||
- parameter keyPath: a key path to group results with
|
||||
*/
|
||||
public init<T>(_ keyPath: KeyPath<D, T>) {
|
||||
public init<T>(_ keyPath: KeyPath<O, T>) {
|
||||
|
||||
self.init([keyPath._kvcKeyPathString!])
|
||||
}
|
||||
}
|
||||
|
||||
extension GroupBy where D: CoreStoreObject {
|
||||
extension GroupBy where O: CoreStoreObject {
|
||||
|
||||
/**
|
||||
Initializes a `GroupBy` clause with a key path
|
||||
|
||||
- parameter keyPath: a key path to group results with
|
||||
*/
|
||||
public init<T>(_ keyPath: KeyPath<D, ValueContainer<D>.Required<T>>) {
|
||||
public init<T>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<T>>) {
|
||||
|
||||
self.init([D.meta[keyPath: keyPath].keyPath])
|
||||
self.init([O.meta[keyPath: keyPath].keyPath])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,9 +139,9 @@ extension GroupBy where D: CoreStoreObject {
|
||||
|
||||
- parameter keyPath: a key path to group results with
|
||||
*/
|
||||
public init<T>(_ keyPath: KeyPath<D, ValueContainer<D>.Optional<T>>) {
|
||||
public init<T>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<T>>) {
|
||||
|
||||
self.init([D.meta[keyPath: keyPath].keyPath])
|
||||
self.init([O.meta[keyPath: keyPath].keyPath])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,9 +149,9 @@ extension GroupBy where D: CoreStoreObject {
|
||||
|
||||
- parameter keyPath: a key path to group results with
|
||||
*/
|
||||
public init<T>(_ keyPath: KeyPath<D, TransformableContainer<D>.Required<T>>) {
|
||||
public init<T>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<T>>) {
|
||||
|
||||
self.init([D.meta[keyPath: keyPath].keyPath])
|
||||
self.init([O.meta[keyPath: keyPath].keyPath])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,9 +159,9 @@ extension GroupBy where D: CoreStoreObject {
|
||||
|
||||
- parameter keyPath: a key path to group results with
|
||||
*/
|
||||
public init<T>(_ keyPath: KeyPath<D, TransformableContainer<D>.Optional<T>>) {
|
||||
public init<T>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<T>>) {
|
||||
|
||||
self.init([D.meta[keyPath: keyPath].keyPath])
|
||||
self.init([O.meta[keyPath: keyPath].keyPath])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ import CoreData
|
||||
// ...
|
||||
}
|
||||
|
||||
CoreStore.perform(
|
||||
dataStack.perform(
|
||||
asynchronous: { (transaction) -> Void in
|
||||
let json: NSDictionary = // ...
|
||||
let person = try transaction.importObject(
|
||||
|
||||
@@ -38,7 +38,7 @@ import CoreData
|
||||
// ...
|
||||
}
|
||||
|
||||
CoreStore.perform(
|
||||
dataStack.perform(
|
||||
asynchronous: { (transaction) -> Void in
|
||||
let json: NSDictionary = // ...
|
||||
let person = try transaction.importUniqueObject(
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>6.3.2</string>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Internals.Closure.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
|
||||
// MARK: - Internals
|
||||
|
||||
extension Internals {
|
||||
|
||||
// MARK: - Closure
|
||||
|
||||
internal final class Closure<T, U> {
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
internal init(_ closure: @escaping (T) -> U) {
|
||||
|
||||
self.closure = closure
|
||||
}
|
||||
|
||||
internal func invoke(with argument: T) -> U {
|
||||
|
||||
return self.closure(argument)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let closure: (T) -> U
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ extension Internals {
|
||||
internal let typedFetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>
|
||||
|
||||
@nonobjc
|
||||
internal convenience init<D>(dataStack: DataStack, fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>, from: From<D>, sectionBy: SectionBy<D>? = nil, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
||||
internal convenience init<O>(dataStack: DataStack, fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>, from: From<O>, sectionBy: SectionBy<O>? = nil, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
||||
|
||||
self.init(
|
||||
context: dataStack.mainContext,
|
||||
@@ -54,7 +54,7 @@ extension Internals {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal init<D>(context: NSManagedObjectContext, fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>, from: From<D>, sectionBy: SectionBy<D>? = nil, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
||||
internal init<O>(context: NSManagedObjectContext, fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>, from: From<O>, sectionBy: SectionBy<O>? = nil, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
||||
|
||||
_ = try? from.applyToFetchRequest(
|
||||
fetchRequest,
|
||||
@@ -82,6 +82,11 @@ extension Internals {
|
||||
|
||||
try self.reapplyAffectedStores(self.typedFetchRequest, self.managedObjectContext)
|
||||
try self.performFetch()
|
||||
|
||||
if case let delegate as FetchedDiffableDataSourceSnapshotDelegate = self.delegate {
|
||||
|
||||
delegate.initialFetch()
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
|
||||
@@ -0,0 +1,641 @@
|
||||
//
|
||||
// Internals.DiffableDataSourceSnapshot.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) || canImport(AppKit)
|
||||
|
||||
import CoreData
|
||||
|
||||
#if canImport(UIKit)
|
||||
import UIKit
|
||||
|
||||
#elseif canImport(AppKit)
|
||||
import AppKit
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - Internals
|
||||
|
||||
extension Internals {
|
||||
|
||||
// MARK: - DiffableDataSourceSnapshot
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DiffableDataSources
|
||||
internal struct DiffableDataSourceSnapshot: DiffableDataSourceSnapshotProtocol {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(sections: [NSFetchedResultsSectionInfo], fetchOffset: Int, fetchLimit: Int) {
|
||||
|
||||
self.structure = .init(
|
||||
sections: sections,
|
||||
fetchOffset: Swift.max(0, fetchOffset),
|
||||
fetchLimit: (fetchLimit > 0) ? fetchLimit : nil
|
||||
)
|
||||
}
|
||||
|
||||
var sections: [Section] {
|
||||
|
||||
get {
|
||||
|
||||
return self.structure.sections
|
||||
}
|
||||
set {
|
||||
|
||||
self.structure.sections = newValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: DiffableDataSourceSnapshotProtocol
|
||||
|
||||
init() {
|
||||
|
||||
self.structure = .init()
|
||||
}
|
||||
|
||||
var numberOfItems: Int {
|
||||
|
||||
return self.structure.allItemIDs.count
|
||||
}
|
||||
|
||||
var numberOfSections: Int {
|
||||
|
||||
return self.structure.allSectionIDs.count
|
||||
}
|
||||
|
||||
var sectionIdentifiers: [String] {
|
||||
|
||||
return self.structure.allSectionIDs
|
||||
}
|
||||
|
||||
var itemIdentifiers: [NSManagedObjectID] {
|
||||
|
||||
return self.structure.allItemIDs
|
||||
}
|
||||
|
||||
var updatedItemIdentifiers: Set<NSManagedObjectID> {
|
||||
|
||||
return self.structure.reloadedItems
|
||||
}
|
||||
|
||||
func numberOfItems(inSection identifier: String) -> Int {
|
||||
|
||||
return self.itemIdentifiers(inSection: identifier).count
|
||||
}
|
||||
|
||||
func itemIdentifiers(inSection identifier: String) -> [NSManagedObjectID] {
|
||||
|
||||
return self.structure.items(in: identifier)
|
||||
}
|
||||
|
||||
func sectionIdentifier(containingItem identifier: NSManagedObjectID) -> String? {
|
||||
|
||||
return self.structure.section(containing: identifier)
|
||||
}
|
||||
|
||||
func indexOfItem(_ identifier: NSManagedObjectID) -> Int? {
|
||||
|
||||
return self.structure.allItemIDs.firstIndex(of: identifier)
|
||||
}
|
||||
|
||||
func indexOfSection(_ identifier: String) -> Int? {
|
||||
|
||||
return self.structure.allSectionIDs.firstIndex(of: identifier)
|
||||
}
|
||||
|
||||
mutating func appendItems<C: Collection>(_ identifiers: C, toSection sectionIdentifier: String?) where C.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.append(itemIDs: identifiers, to: sectionIdentifier)
|
||||
}
|
||||
|
||||
mutating func insertItems<C: Collection>(_ identifiers: C, beforeItem beforeIdentifier: NSManagedObjectID) where C.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.insert(itemIDs: identifiers, before: beforeIdentifier)
|
||||
}
|
||||
|
||||
mutating func insertItems<C: Collection>(_ identifiers: C, afterItem afterIdentifier: NSManagedObjectID) where C.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.insert(itemIDs: identifiers, after: afterIdentifier)
|
||||
}
|
||||
|
||||
mutating func deleteItems<S: Sequence>(_ identifiers: S) where S.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.remove(itemIDs: identifiers)
|
||||
}
|
||||
|
||||
mutating func deleteAllItems() {
|
||||
|
||||
self.structure.removeAllItems()
|
||||
}
|
||||
|
||||
mutating func moveItem(_ identifier: NSManagedObjectID, beforeItem toIdentifier: NSManagedObjectID) {
|
||||
|
||||
self.structure.move(itemID: identifier, before: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func moveItem(_ identifier: NSManagedObjectID, afterItem toIdentifier: NSManagedObjectID) {
|
||||
|
||||
self.structure.move(itemID: identifier, after: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func reloadItems<S: Sequence>(_ identifiers: S) where S.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.update(itemIDs: identifiers)
|
||||
}
|
||||
|
||||
mutating func appendSections<C: Collection>(_ identifiers: C) where C.Element == String {
|
||||
|
||||
self.structure.append(sectionIDs: identifiers)
|
||||
}
|
||||
|
||||
mutating func insertSections<C: Collection>(_ identifiers: C, beforeSection toIdentifier: String) where C.Element == String {
|
||||
|
||||
self.structure.insert(sectionIDs: identifiers, before: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func insertSections<C: Collection>(_ identifiers: C, afterSection toIdentifier: String) where C.Element == String {
|
||||
|
||||
self.structure.insert(sectionIDs: identifiers, after: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func deleteSections<S: Sequence>(_ identifiers: S) where S.Element == String {
|
||||
|
||||
self.structure.remove(sectionIDs: identifiers)
|
||||
}
|
||||
|
||||
mutating func moveSection(_ identifier: String, beforeSection toIdentifier: String) {
|
||||
|
||||
self.structure.move(sectionID: identifier, before: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func moveSection(_ identifier: String, afterSection toIdentifier: String) {
|
||||
|
||||
self.structure.move(sectionID: identifier, after: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func reloadSections<S: Sequence>(_ identifiers: S) where S.Element == String {
|
||||
|
||||
self.structure.update(sectionIDs: identifiers)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var structure: BackingStructure
|
||||
|
||||
|
||||
// MARK: - Section
|
||||
|
||||
internal struct Section: DifferentiableSection, Equatable {
|
||||
|
||||
var isReloaded: Bool
|
||||
|
||||
init(differenceIdentifier: String, items: [Item] = [], isReloaded: Bool = false) {
|
||||
|
||||
self.differenceIdentifier = differenceIdentifier
|
||||
self.elements = items
|
||||
self.isReloaded = isReloaded
|
||||
}
|
||||
|
||||
// MARK: Differentiable
|
||||
|
||||
let differenceIdentifier: String
|
||||
|
||||
func isContentEqual(to source: Section) -> Bool {
|
||||
|
||||
return !self.isReloaded
|
||||
&& self.differenceIdentifier == source.differenceIdentifier
|
||||
}
|
||||
|
||||
|
||||
// MARK: DifferentiableSection
|
||||
|
||||
var elements: [Item] = []
|
||||
|
||||
init<S: Sequence>(source: Section, elements: S) where S.Element == Item {
|
||||
|
||||
self.init(
|
||||
differenceIdentifier: source.differenceIdentifier,
|
||||
items: Array(elements),
|
||||
isReloaded: source.isReloaded
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Item
|
||||
|
||||
internal struct Item: Differentiable, Equatable {
|
||||
|
||||
var isReloaded: Bool
|
||||
|
||||
init(differenceIdentifier: NSManagedObjectID, isReloaded: Bool = false) {
|
||||
|
||||
self.differenceIdentifier = differenceIdentifier
|
||||
self.isReloaded = isReloaded
|
||||
}
|
||||
|
||||
// MARK: Differentiable
|
||||
|
||||
let differenceIdentifier: NSManagedObjectID
|
||||
|
||||
func isContentEqual(to source: Item) -> Bool {
|
||||
|
||||
return !self.isReloaded
|
||||
&& self.differenceIdentifier == source.differenceIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - BackingStructure
|
||||
|
||||
fileprivate struct BackingStructure {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
var sections: [Section]
|
||||
private(set) var reloadedItems: Set<NSManagedObjectID>
|
||||
|
||||
init() {
|
||||
|
||||
self.sections = []
|
||||
self.reloadedItems = []
|
||||
}
|
||||
|
||||
init(sections: [NSFetchedResultsSectionInfo], fetchOffset: Int, fetchLimit: Int?) {
|
||||
|
||||
let sliceItems: (_ array: [Any], _ offset: Int) -> Array<Any>.SubSequence
|
||||
if let fetchLimit = fetchLimit {
|
||||
|
||||
var remainingCount = fetchLimit
|
||||
sliceItems = {
|
||||
|
||||
let slice = $0[$1...].prefix(remainingCount)
|
||||
remainingCount -= slice.count
|
||||
return slice
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
sliceItems = { $0[$1...] }
|
||||
}
|
||||
var newSections: [Internals.DiffableDataSourceSnapshot.Section] = []
|
||||
var ignoreCount = fetchOffset
|
||||
for section in sections {
|
||||
|
||||
let objects = section.objects ?? []
|
||||
guard objects.indices.contains(ignoreCount) else {
|
||||
|
||||
ignoreCount -= objects.count
|
||||
continue
|
||||
}
|
||||
let items = sliceItems(objects, ignoreCount)
|
||||
.map({ Item(differenceIdentifier: ($0 as! NSManagedObject).objectID) })
|
||||
ignoreCount = 0
|
||||
guard !items.isEmpty else {
|
||||
|
||||
continue
|
||||
}
|
||||
newSections.append(
|
||||
Section(differenceIdentifier: section.name, items: items)
|
||||
)
|
||||
}
|
||||
self.sections = newSections
|
||||
self.reloadedItems = []
|
||||
}
|
||||
|
||||
var allSectionIDs: [String] {
|
||||
|
||||
return self.sections.map({ $0.differenceIdentifier })
|
||||
}
|
||||
|
||||
var allItemIDs: [NSManagedObjectID] {
|
||||
|
||||
return self.sections.lazy.flatMap({ $0.elements }).map({ $0.differenceIdentifier })
|
||||
}
|
||||
|
||||
func items(in sectionID: String) -> [NSManagedObjectID] {
|
||||
|
||||
guard let sectionIndex = self.sectionIndex(of: sectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(sectionID)\" does not exist")
|
||||
}
|
||||
return self.sections[sectionIndex].elements.map({ $0.differenceIdentifier })
|
||||
}
|
||||
|
||||
func section(containing itemID: NSManagedObjectID) -> String? {
|
||||
|
||||
return self.itemPositionMap(itemID)?.section.differenceIdentifier
|
||||
}
|
||||
|
||||
mutating func append<C: Collection>(itemIDs: C, to sectionID: String?) where C.Element == NSManagedObjectID {
|
||||
|
||||
let index: Array<Section>.Index
|
||||
if let sectionID = sectionID {
|
||||
|
||||
guard let sectionIndex = self.sectionIndex(of: sectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(sectionID)\" does not exist")
|
||||
}
|
||||
index = sectionIndex
|
||||
}
|
||||
else {
|
||||
|
||||
let section = self.sections
|
||||
guard !section.isEmpty else {
|
||||
|
||||
Internals.abort("No sections exist")
|
||||
}
|
||||
index = section.index(before: section.endIndex)
|
||||
}
|
||||
let items = itemIDs.lazy.map({ Item(differenceIdentifier: $0) })
|
||||
self.sections[index].elements.append(contentsOf: items)
|
||||
}
|
||||
|
||||
mutating func insert<C: Collection>(itemIDs: C, before beforeItemID: NSManagedObjectID) where C.Element == NSManagedObjectID {
|
||||
|
||||
guard let itemPosition = self.itemPositionMap(beforeItemID) else {
|
||||
|
||||
Internals.abort("Item \(beforeItemID) does not exist")
|
||||
}
|
||||
let items = itemIDs.lazy.map({ Item(differenceIdentifier: $0) })
|
||||
self.sections[itemPosition.sectionIndex].elements
|
||||
.insert(contentsOf: items, at: itemPosition.itemRelativeIndex)
|
||||
}
|
||||
|
||||
mutating func insert<C: Collection>(itemIDs: C, after afterItemID: NSManagedObjectID) where C.Element == NSManagedObjectID {
|
||||
|
||||
guard let itemPosition = self.itemPositionMap(afterItemID) else {
|
||||
|
||||
Internals.abort("Item \(afterItemID) does not exist")
|
||||
}
|
||||
let itemIndex = self.sections[itemPosition.sectionIndex].elements
|
||||
.index(after: itemPosition.itemRelativeIndex)
|
||||
let items = itemIDs.lazy.map({ Item(differenceIdentifier: $0) })
|
||||
self.sections[itemPosition.sectionIndex].elements
|
||||
.insert(contentsOf: items, at: itemIndex)
|
||||
}
|
||||
|
||||
mutating func remove<S: Sequence>(itemIDs: S) where S.Element == NSManagedObjectID {
|
||||
|
||||
let itemPositionMap = self.itemPositionMap()
|
||||
var removeIndexSetMap: [Int: IndexSet] = [:]
|
||||
|
||||
for itemID in itemIDs {
|
||||
|
||||
guard let itemPosition = itemPositionMap[itemID] else {
|
||||
|
||||
continue
|
||||
}
|
||||
removeIndexSetMap[itemPosition.sectionIndex, default: []]
|
||||
.insert(itemPosition.itemRelativeIndex)
|
||||
}
|
||||
for (sectionIndex, removeIndexSet) in removeIndexSetMap {
|
||||
|
||||
for range in removeIndexSet.rangeView.reversed() {
|
||||
|
||||
self.sections[sectionIndex].elements.removeSubrange(range)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutating func removeAllItems() {
|
||||
|
||||
for sectionIndex in self.sections.indices {
|
||||
|
||||
self.sections[sectionIndex].elements.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
mutating func removeAllEmptySections() {
|
||||
|
||||
self.sections.removeAll(where: { $0.elements.isEmpty })
|
||||
}
|
||||
|
||||
mutating func move(itemID: NSManagedObjectID, before beforeItemID: NSManagedObjectID) {
|
||||
|
||||
guard let removed = self.remove(itemID: itemID) else {
|
||||
|
||||
Internals.abort("Item \(itemID) does not exist")
|
||||
}
|
||||
guard let itemPosition = self.itemPositionMap(beforeItemID) else {
|
||||
|
||||
Internals.abort("Item \(beforeItemID) does not exist")
|
||||
}
|
||||
self.sections[itemPosition.sectionIndex].elements
|
||||
.insert(removed, at: itemPosition.itemRelativeIndex)
|
||||
}
|
||||
|
||||
mutating func move(itemID: NSManagedObjectID, after afterItemID: NSManagedObjectID) {
|
||||
|
||||
guard let removed = self.remove(itemID: itemID) else {
|
||||
|
||||
Internals.abort("Item \(itemID) does not exist")
|
||||
}
|
||||
guard let itemPosition = self.itemPositionMap(afterItemID) else {
|
||||
|
||||
Internals.abort("Item \(afterItemID) does not exist")
|
||||
}
|
||||
let itemIndex = self.sections[itemPosition.sectionIndex].elements
|
||||
.index(after: itemPosition.itemRelativeIndex)
|
||||
self.sections[itemPosition.sectionIndex].elements
|
||||
.insert(removed, at: itemIndex)
|
||||
}
|
||||
|
||||
mutating func update<S: Sequence>(itemIDs: S) where S.Element == NSManagedObjectID {
|
||||
|
||||
let itemPositionMap = self.itemPositionMap()
|
||||
var newItemIDs: Set<NSManagedObjectID> = []
|
||||
for itemID in itemIDs {
|
||||
|
||||
guard let itemPosition = itemPositionMap[itemID] else {
|
||||
|
||||
continue
|
||||
}
|
||||
self.sections[itemPosition.sectionIndex]
|
||||
.elements[itemPosition.itemRelativeIndex].isReloaded = true
|
||||
newItemIDs.insert(itemID)
|
||||
}
|
||||
self.reloadedItems.formUnion(newItemIDs)
|
||||
}
|
||||
|
||||
mutating func append<C: Collection>(sectionIDs: C) where C.Element == String {
|
||||
|
||||
let newSections = sectionIDs.lazy.map({ Section(differenceIdentifier: $0) })
|
||||
self.sections.append(contentsOf: newSections)
|
||||
}
|
||||
|
||||
mutating func insert<C: Collection>(sectionIDs: C, before beforeSectionID: String) where C.Element == String {
|
||||
|
||||
guard let sectionIndex = self.sectionIndex(of: beforeSectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(beforeSectionID)\" does not exist")
|
||||
}
|
||||
let newSections = sectionIDs.lazy.map({ Section(differenceIdentifier: $0) })
|
||||
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||
}
|
||||
|
||||
mutating func insert<C: Collection>(sectionIDs: C, after afterSectionID: String) where C.Element == String {
|
||||
|
||||
guard let beforeIndex = self.sectionIndex(of: afterSectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(afterSectionID)\" does not exist")
|
||||
}
|
||||
let sectionIndex = self.sections.index(after: beforeIndex)
|
||||
let newSections = sectionIDs.lazy.map({ Section(differenceIdentifier: $0) })
|
||||
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||
}
|
||||
|
||||
mutating func remove<S: Sequence>(sectionIDs: S) where S.Element == String {
|
||||
|
||||
for sectionID in sectionIDs {
|
||||
|
||||
self.remove(sectionID: sectionID)
|
||||
}
|
||||
}
|
||||
|
||||
mutating func move(sectionID: String, before beforeSectionID: String) {
|
||||
|
||||
guard let removed = self.remove(sectionID: sectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(sectionID)\" does not exist")
|
||||
}
|
||||
guard let sectionIndex = self.sectionIndex(of: beforeSectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(beforeSectionID)\" does not exist")
|
||||
}
|
||||
self.sections.insert(removed, at: sectionIndex)
|
||||
}
|
||||
|
||||
mutating func move(sectionID: String, after afterSectionID: String) {
|
||||
|
||||
guard let removed = self.remove(sectionID: sectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(sectionID)\" does not exist")
|
||||
}
|
||||
guard let beforeIndex = self.sectionIndex(of: afterSectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(afterSectionID)\" does not exist")
|
||||
}
|
||||
let sectionIndex = self.sections.index(after: beforeIndex)
|
||||
self.sections.insert(removed, at: sectionIndex)
|
||||
}
|
||||
|
||||
mutating func update<S: Sequence>(sectionIDs: S) where S.Element == String {
|
||||
|
||||
for sectionID in sectionIDs {
|
||||
|
||||
guard let sectionIndex = self.sectionIndex(of: sectionID) else {
|
||||
|
||||
continue
|
||||
}
|
||||
self.sections[sectionIndex].isReloaded = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func sectionIndex(of sectionID: String) -> Array<Section>.Index? {
|
||||
|
||||
return self.sections.firstIndex(where: { $0.differenceIdentifier == sectionID })
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
private mutating func remove(itemID: NSManagedObjectID) -> Item? {
|
||||
|
||||
guard let itemPosition = self.itemPositionMap(itemID) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return self.sections[itemPosition.sectionIndex].elements
|
||||
.remove(at: itemPosition.itemRelativeIndex)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
private mutating func remove(sectionID: String) -> Section? {
|
||||
|
||||
guard let sectionIndex = self.sectionIndex(of: sectionID) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return self.sections.remove(at: sectionIndex)
|
||||
}
|
||||
|
||||
private func itemPositionMap(_ itemID: NSManagedObjectID) -> ItemPosition? {
|
||||
|
||||
let sections = self.sections
|
||||
for (sectionIndex, section) in sections.enumerated() {
|
||||
|
||||
for (itemRelativeIndex, item) in section.elements.enumerated() {
|
||||
|
||||
guard item.differenceIdentifier == itemID else {
|
||||
|
||||
continue
|
||||
}
|
||||
return ItemPosition(
|
||||
item: item,
|
||||
itemRelativeIndex: itemRelativeIndex,
|
||||
section: section,
|
||||
sectionIndex: sectionIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func itemPositionMap() -> [NSManagedObjectID: ItemPosition] {
|
||||
|
||||
return self.sections.enumerated().reduce(into: [:]) { result, section in
|
||||
|
||||
for (itemRelativeIndex, item) in section.element.elements.enumerated() {
|
||||
|
||||
result[item.differenceIdentifier] = ItemPosition(
|
||||
item: item,
|
||||
itemRelativeIndex: itemRelativeIndex,
|
||||
section: section.element,
|
||||
sectionIndex: section.offset
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ItemPosition
|
||||
|
||||
fileprivate struct ItemPosition {
|
||||
|
||||
let item: Item
|
||||
let itemRelativeIndex: Int
|
||||
let section: Section
|
||||
let sectionIndex: Int
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// Internals.DiffableDataUIDispatcher.Changeset.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) || canImport(AppKit)
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - Internals.DiffableDataUIDispatcher
|
||||
|
||||
extension Internals.DiffableDataUIDispatcher {
|
||||
|
||||
// MARK: - ChangeSet
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DifferenceKit
|
||||
internal struct Changeset<C: Collection>: Equatable where C: Equatable {
|
||||
|
||||
var data: C
|
||||
var sectionDeleted: [Int]
|
||||
var sectionInserted: [Int]
|
||||
var sectionUpdated: [Int]
|
||||
var sectionMoved: [(source: Int, target: Int)]
|
||||
|
||||
var elementDeleted: [ElementPath]
|
||||
var elementInserted: [ElementPath]
|
||||
var elementUpdated: [ElementPath]
|
||||
var elementMoved: [(source: ElementPath, target: ElementPath)]
|
||||
|
||||
@inlinable
|
||||
init(
|
||||
data: C,
|
||||
sectionDeleted: [Int] = [],
|
||||
sectionInserted: [Int] = [],
|
||||
sectionUpdated: [Int] = [],
|
||||
sectionMoved: [(source: Int, target: Int)] = [],
|
||||
elementDeleted: [ElementPath] = [],
|
||||
elementInserted: [ElementPath] = [],
|
||||
elementUpdated: [ElementPath] = [],
|
||||
elementMoved: [(source: ElementPath, target: ElementPath)] = []
|
||||
) {
|
||||
self.data = data
|
||||
self.sectionDeleted = sectionDeleted
|
||||
self.sectionInserted = sectionInserted
|
||||
self.sectionUpdated = sectionUpdated
|
||||
self.sectionMoved = sectionMoved
|
||||
self.elementDeleted = elementDeleted
|
||||
self.elementInserted = elementInserted
|
||||
self.elementUpdated = elementUpdated
|
||||
self.elementMoved = elementMoved
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var sectionChangeCount: Int {
|
||||
|
||||
return self.sectionDeleted.count
|
||||
+ self.sectionInserted.count
|
||||
+ self.sectionUpdated.count
|
||||
+ self.sectionMoved.count
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var elementChangeCount: Int {
|
||||
|
||||
return self.elementDeleted.count
|
||||
+ self.elementInserted.count
|
||||
+ self.elementUpdated.count
|
||||
+ self.elementMoved.count
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var changeCount: Int {
|
||||
|
||||
return self.sectionChangeCount + self.elementChangeCount
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var hasSectionChanges: Bool {
|
||||
|
||||
return self.sectionChangeCount > 0
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var hasElementChanges: Bool {
|
||||
|
||||
return self.elementChangeCount > 0
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var hasChanges: Bool {
|
||||
|
||||
return self.changeCount > 0
|
||||
}
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
static func == (lhs: Changeset, rhs: Changeset) -> Bool {
|
||||
return lhs.data == rhs.data
|
||||
&& Set(lhs.sectionDeleted) == Set(rhs.sectionDeleted)
|
||||
&& Set(lhs.sectionInserted) == Set(rhs.sectionInserted)
|
||||
&& Set(lhs.sectionUpdated) == Set(rhs.sectionUpdated)
|
||||
&& Set(lhs.sectionMoved.map(HashablePair.init)) == Set(rhs.sectionMoved.map(HashablePair.init))
|
||||
&& Set(lhs.elementDeleted) == Set(rhs.elementDeleted)
|
||||
&& Set(lhs.elementInserted) == Set(rhs.elementInserted)
|
||||
&& Set(lhs.elementUpdated) == Set(rhs.elementUpdated)
|
||||
&& Set(lhs.elementMoved.map(HashablePair.init)) == Set(rhs.elementMoved.map(HashablePair.init))
|
||||
}
|
||||
|
||||
|
||||
// MARK: - HashablePair
|
||||
|
||||
private struct HashablePair<H: Hashable>: Hashable {
|
||||
|
||||
let first: H
|
||||
let second: H
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,325 @@
|
||||
//
|
||||
// Internals.DiffableDataUIDispatcher.DiffResult.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) || canImport(AppKit)
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - Internals.DiffableDataUIDispatcher
|
||||
|
||||
extension Internals.DiffableDataUIDispatcher {
|
||||
|
||||
// MARK: - DiffResult
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DifferenceKit
|
||||
@usableFromInline
|
||||
internal struct DiffResult<Index> {
|
||||
|
||||
@usableFromInline
|
||||
internal let deleted: [Index]
|
||||
@usableFromInline
|
||||
internal let inserted: [Index]
|
||||
@usableFromInline
|
||||
internal let updated: [Index]
|
||||
@usableFromInline
|
||||
internal let moved: [(source: Index, target: Index)]
|
||||
@usableFromInline
|
||||
internal let sourceTraces: ContiguousArray<Trace<Int>>
|
||||
@usableFromInline
|
||||
internal let targetReferences: ContiguousArray<Int?>
|
||||
|
||||
@inlinable
|
||||
@discardableResult
|
||||
static func diff<E: Differentiable>(
|
||||
source: ContiguousArray<E>,
|
||||
target: ContiguousArray<E>,
|
||||
useTargetIndexForUpdated: Bool,
|
||||
mapIndex: (Int) -> Index,
|
||||
updatedElementsPointer: UnsafeMutablePointer<ContiguousArray<E>>? = nil,
|
||||
notDeletedElementsPointer: UnsafeMutablePointer<ContiguousArray<E>>? = nil
|
||||
) -> DiffResult<Index> {
|
||||
|
||||
var deleted = [Index]()
|
||||
var inserted = [Index]()
|
||||
var updated = [Index]()
|
||||
var moved = [(source: Index, target: Index)]()
|
||||
|
||||
var sourceTraces = ContiguousArray<Trace<Int>>()
|
||||
var sourceIdentifiers = ContiguousArray<E.DifferenceIdentifier>()
|
||||
var targetReferences = ContiguousArray<Int?>(repeating: nil, count: target.count)
|
||||
|
||||
sourceTraces.reserveCapacity(source.count)
|
||||
sourceIdentifiers.reserveCapacity(source.count)
|
||||
|
||||
for sourceElement in source {
|
||||
|
||||
sourceTraces.append(Trace())
|
||||
sourceIdentifiers.append(sourceElement.differenceIdentifier)
|
||||
}
|
||||
sourceIdentifiers.withUnsafeBufferPointer { bufferPointer in
|
||||
|
||||
var sourceOccurrencesTable = [TableKey<E.DifferenceIdentifier>: Occurrence](minimumCapacity: source.count)
|
||||
|
||||
for sourceIndex in sourceIdentifiers.indices {
|
||||
|
||||
let pointer = bufferPointer.baseAddress!.advanced(by: sourceIndex)
|
||||
let key = TableKey(pointer: pointer)
|
||||
|
||||
switch sourceOccurrencesTable[key] {
|
||||
case .none:
|
||||
sourceOccurrencesTable[key] = .unique(index: sourceIndex)
|
||||
|
||||
case .unique(let otherIndex)?:
|
||||
let reference = IndicesReference([otherIndex, sourceIndex])
|
||||
sourceOccurrencesTable[key] = .duplicate(reference: reference)
|
||||
|
||||
case .duplicate(let reference)?:
|
||||
reference.push(sourceIndex)
|
||||
}
|
||||
}
|
||||
for targetIndex in target.indices {
|
||||
|
||||
var targetIdentifier = target[targetIndex].differenceIdentifier
|
||||
let key = TableKey(pointer: &targetIdentifier)
|
||||
|
||||
switch sourceOccurrencesTable[key] {
|
||||
|
||||
case .none:
|
||||
break
|
||||
|
||||
case .unique(let sourceIndex)?:
|
||||
if case .none = sourceTraces[sourceIndex].reference {
|
||||
|
||||
targetReferences[targetIndex] = sourceIndex
|
||||
sourceTraces[sourceIndex].reference = targetIndex
|
||||
}
|
||||
|
||||
case .duplicate(let reference)?:
|
||||
if let sourceIndex = reference.next() {
|
||||
|
||||
targetReferences[targetIndex] = sourceIndex
|
||||
sourceTraces[sourceIndex].reference = targetIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var offsetByDelete = 0
|
||||
var untrackedSourceIndex: Int? = 0
|
||||
|
||||
for sourceIndex in source.indices {
|
||||
|
||||
sourceTraces[sourceIndex].deleteOffset = offsetByDelete
|
||||
|
||||
if let targetIndex = sourceTraces[sourceIndex].reference {
|
||||
|
||||
let targetElement = target[targetIndex]
|
||||
updatedElementsPointer?.pointee.append(targetElement)
|
||||
notDeletedElementsPointer?.pointee.append(targetElement)
|
||||
}
|
||||
else {
|
||||
|
||||
let sourceElement = source[sourceIndex]
|
||||
deleted.append(mapIndex(sourceIndex))
|
||||
sourceTraces[sourceIndex].isTracked = true
|
||||
offsetByDelete += 1
|
||||
updatedElementsPointer?.pointee.append(sourceElement)
|
||||
}
|
||||
}
|
||||
for targetIndex in target.indices {
|
||||
|
||||
untrackedSourceIndex = untrackedSourceIndex.flatMap { index in
|
||||
|
||||
sourceTraces.suffix(from: index).firstIndex { !$0.isTracked }
|
||||
}
|
||||
if let sourceIndex = targetReferences[targetIndex] {
|
||||
|
||||
sourceTraces[sourceIndex].isTracked = true
|
||||
|
||||
let sourceElement = source[sourceIndex]
|
||||
let targetElement = target[targetIndex]
|
||||
|
||||
if !targetElement.isContentEqual(to: sourceElement) {
|
||||
|
||||
updated.append(mapIndex(useTargetIndexForUpdated ? targetIndex : sourceIndex))
|
||||
}
|
||||
if sourceIndex != untrackedSourceIndex {
|
||||
|
||||
let deleteOffset = sourceTraces[sourceIndex].deleteOffset
|
||||
moved.append((source: mapIndex(sourceIndex - deleteOffset), target: mapIndex(targetIndex)))
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
inserted.append(mapIndex(targetIndex))
|
||||
}
|
||||
}
|
||||
return DiffResult(
|
||||
deleted: deleted,
|
||||
inserted: inserted,
|
||||
updated: updated,
|
||||
moved: moved,
|
||||
sourceTraces: sourceTraces,
|
||||
targetReferences: targetReferences
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@inlinable
|
||||
internal init(
|
||||
deleted: [Index] = [],
|
||||
inserted: [Index] = [],
|
||||
updated: [Index] = [],
|
||||
moved: [(source: Index, target: Index)] = [],
|
||||
sourceTraces: ContiguousArray<Trace<Int>>,
|
||||
targetReferences: ContiguousArray<Int?>
|
||||
) {
|
||||
|
||||
self.deleted = deleted
|
||||
self.inserted = inserted
|
||||
self.updated = updated
|
||||
self.moved = moved
|
||||
self.sourceTraces = sourceTraces
|
||||
self.targetReferences = targetReferences
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Trace
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DifferenceKit
|
||||
@usableFromInline
|
||||
internal struct Trace<Index> {
|
||||
|
||||
@usableFromInline
|
||||
internal var reference: Index?
|
||||
|
||||
@usableFromInline
|
||||
internal var deleteOffset = 0
|
||||
|
||||
@usableFromInline
|
||||
internal var isTracked = false
|
||||
|
||||
@inlinable
|
||||
init() {}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Occurrence
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DifferenceKit
|
||||
@usableFromInline
|
||||
internal enum Occurrence {
|
||||
|
||||
case unique(index: Int)
|
||||
case duplicate(reference: IndicesReference)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - IndicesReference
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DifferenceKit
|
||||
@usableFromInline
|
||||
internal final class IndicesReference {
|
||||
|
||||
@usableFromInline
|
||||
internal var indices: ContiguousArray<Int>
|
||||
|
||||
@usableFromInline
|
||||
internal var position = 0
|
||||
|
||||
@inlinable
|
||||
internal init(_ indices: ContiguousArray<Int>) {
|
||||
|
||||
self.indices = indices
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func push(_ index: Int) {
|
||||
|
||||
self.indices.append(index)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func next() -> Int? {
|
||||
|
||||
guard self.position < self.indices.endIndex else {
|
||||
|
||||
return nil
|
||||
}
|
||||
defer {
|
||||
|
||||
self.position += 1
|
||||
}
|
||||
return self.indices[self.position]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - TableKey
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DifferenceKit
|
||||
@usableFromInline
|
||||
internal struct TableKey<T: Hashable>: Hashable {
|
||||
|
||||
@usableFromInline
|
||||
internal let pointeeHashValue: Int
|
||||
|
||||
@usableFromInline
|
||||
internal let pointer: UnsafePointer<T>
|
||||
|
||||
@inlinable
|
||||
internal init(pointer: UnsafePointer<T>) {
|
||||
|
||||
self.pointeeHashValue = pointer.pointee.hashValue
|
||||
self.pointer = pointer
|
||||
}
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
@inlinable
|
||||
internal static func == (lhs: TableKey, rhs: TableKey) -> Bool {
|
||||
|
||||
return lhs.pointeeHashValue == rhs.pointeeHashValue
|
||||
&& (lhs.pointer.distance(to: rhs.pointer) == 0
|
||||
|| lhs.pointer.pointee == rhs.pointer.pointee)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
@inlinable
|
||||
internal func hash(into hasher: inout Hasher) {
|
||||
|
||||
hasher.combine(pointeeHashValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,517 @@
|
||||
//
|
||||
// Internals.DiffableDataUIDispatcher.StagedChangeset.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) || canImport(AppKit)
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - Internals.DiffableDataUIDispatcher
|
||||
|
||||
extension Internals.DiffableDataUIDispatcher {
|
||||
|
||||
// MARK: - StagedChangeset
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DifferenceKit
|
||||
internal struct StagedChangeset<C: Collection>: ExpressibleByArrayLiteral, Equatable, RandomAccessCollection, RangeReplaceableCollection where C: Equatable {
|
||||
|
||||
@usableFromInline
|
||||
var changesets: ContiguousArray<Changeset<C>>
|
||||
|
||||
@inlinable
|
||||
init<S: Sequence>(_ changesets: S) where S.Element == Changeset<C> {
|
||||
|
||||
self.changesets = ContiguousArray(changesets)
|
||||
}
|
||||
|
||||
|
||||
// MARK: ExpressibleByArrayLiteral
|
||||
|
||||
@inlinable
|
||||
init(arrayLiteral elements: Changeset<C>...) {
|
||||
|
||||
self.init(elements)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
@inlinable
|
||||
static func == (lhs: StagedChangeset, rhs: StagedChangeset) -> Bool {
|
||||
|
||||
return lhs.changesets == rhs.changesets
|
||||
}
|
||||
|
||||
|
||||
// MARK: Sequence
|
||||
|
||||
typealias Element = Changeset<C>
|
||||
|
||||
|
||||
// MARK: RandomAccessCollection
|
||||
|
||||
@inlinable
|
||||
var startIndex: Int {
|
||||
|
||||
return self.changesets.startIndex
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var endIndex: Int {
|
||||
|
||||
return self.changesets.endIndex
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func index(after i: Int) -> Int {
|
||||
|
||||
return self.changesets.index(after: i)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
subscript(position: Int) -> Changeset<C> {
|
||||
|
||||
get { return self.changesets[position] }
|
||||
set { self.changesets[position] = newValue }
|
||||
}
|
||||
|
||||
|
||||
// MARK: RangeReplaceableCollection
|
||||
|
||||
@inlinable
|
||||
init() {
|
||||
|
||||
self.init([])
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func replaceSubrange<C2: Collection, R: RangeExpression>(_ subrange: R, with newElements: C2) where C2.Element == Changeset<C>, R.Bound == Int {
|
||||
|
||||
self.changesets.replaceSubrange(subrange, with: newElements)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Internals.DiffableDataUIDispatcher.StagedChangeset where C: RangeReplaceableCollection, C.Element: Differentiable
|
||||
|
||||
extension Internals.DiffableDataUIDispatcher.StagedChangeset where C: RangeReplaceableCollection, C.Element: Differentiable {
|
||||
|
||||
@inlinable
|
||||
internal init(source: C, target: C) {
|
||||
|
||||
self.init(source: source, target: target, section: 0)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal init(source: C, target: C, section: Int) {
|
||||
|
||||
typealias Changeset = Internals.DiffableDataUIDispatcher<O>.Changeset
|
||||
typealias ElementPath = Internals.DiffableDataUIDispatcher<O>.ElementPath
|
||||
typealias DiffResult = Internals.DiffableDataUIDispatcher<O>.DiffResult
|
||||
|
||||
let sourceElements = ContiguousArray(source)
|
||||
let targetElements = ContiguousArray(target)
|
||||
if sourceElements.isEmpty && targetElements.isEmpty {
|
||||
|
||||
self.init()
|
||||
return
|
||||
}
|
||||
if !sourceElements.isEmpty && targetElements.isEmpty {
|
||||
|
||||
self.init(
|
||||
[
|
||||
Changeset(
|
||||
data: target,
|
||||
elementDeleted: sourceElements.indices.map {
|
||||
ElementPath(
|
||||
element: $0,
|
||||
section: section
|
||||
)
|
||||
}
|
||||
)
|
||||
]
|
||||
)
|
||||
return
|
||||
}
|
||||
if sourceElements.isEmpty && !targetElements.isEmpty {
|
||||
|
||||
self.init(
|
||||
[
|
||||
Changeset(
|
||||
data: target,
|
||||
elementInserted: targetElements.indices.map {
|
||||
ElementPath(
|
||||
element: $0,
|
||||
section: section
|
||||
)
|
||||
}
|
||||
)
|
||||
]
|
||||
)
|
||||
return
|
||||
}
|
||||
var firstStageElements = ContiguousArray<C.Element>()
|
||||
var secondStageElements = ContiguousArray<C.Element>()
|
||||
let result = DiffResult.diff(
|
||||
source: sourceElements,
|
||||
target: targetElements,
|
||||
useTargetIndexForUpdated: false,
|
||||
mapIndex: { ElementPath(element: $0, section: section) },
|
||||
updatedElementsPointer: &firstStageElements,
|
||||
notDeletedElementsPointer: &secondStageElements
|
||||
)
|
||||
|
||||
var changesets = ContiguousArray<Changeset<C>>()
|
||||
if !result.updated.isEmpty {
|
||||
|
||||
changesets.append(
|
||||
Changeset(
|
||||
data: C(firstStageElements),
|
||||
elementUpdated: result.updated
|
||||
)
|
||||
)
|
||||
}
|
||||
if !result.deleted.isEmpty {
|
||||
|
||||
changesets.append(
|
||||
Changeset(
|
||||
data: C(secondStageElements),
|
||||
elementDeleted: result.deleted
|
||||
)
|
||||
)
|
||||
}
|
||||
if !result.inserted.isEmpty || !result.moved.isEmpty {
|
||||
|
||||
changesets.append(
|
||||
Changeset(
|
||||
data: target,
|
||||
elementInserted: result.inserted,
|
||||
elementMoved: result.moved
|
||||
)
|
||||
)
|
||||
}
|
||||
if !changesets.isEmpty {
|
||||
|
||||
let index = changesets.index(before: changesets.endIndex)
|
||||
changesets[index].data = target
|
||||
}
|
||||
self.init(changesets)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Internals.DiffableDataUIDispatcher.StagedChangeset where C: RangeReplaceableCollection, C.Element: DifferentiableSection
|
||||
|
||||
extension Internals.DiffableDataUIDispatcher.StagedChangeset where C: RangeReplaceableCollection, C.Element: DifferentiableSection {
|
||||
|
||||
@inlinable
|
||||
internal init(source: C, target: C) {
|
||||
|
||||
typealias Section = C.Element
|
||||
typealias SectionIdentifier = C.Element.DifferenceIdentifier
|
||||
typealias Element = C.Element.Collection.Element
|
||||
typealias ElementIdentifier = C.Element.Collection.Element.DifferenceIdentifier
|
||||
|
||||
typealias Changeset = Internals.DiffableDataUIDispatcher<O>.Changeset
|
||||
typealias ElementPath = Internals.DiffableDataUIDispatcher<O>.ElementPath
|
||||
typealias DiffResult = Internals.DiffableDataUIDispatcher<O>.DiffResult
|
||||
|
||||
typealias Trace = Internals.DiffableDataUIDispatcher<O>.DiffResult<Section>.Trace
|
||||
typealias TableKey = Internals.DiffableDataUIDispatcher<O>.DiffResult<Section>.TableKey
|
||||
typealias Occurrence = Internals.DiffableDataUIDispatcher<O>.DiffResult<Section>.Occurrence
|
||||
typealias IndicesReference = Internals.DiffableDataUIDispatcher<O>.DiffResult<Section>.IndicesReference
|
||||
|
||||
let sourceSections = ContiguousArray(source)
|
||||
let targetSections = ContiguousArray(target)
|
||||
|
||||
let contiguousSourceSections = ContiguousArray(sourceSections.map { ContiguousArray($0.elements) })
|
||||
let contiguousTargetSections = ContiguousArray(targetSections.map { ContiguousArray($0.elements) })
|
||||
|
||||
var firstStageSections = sourceSections
|
||||
var secondStageSections = ContiguousArray<Section>()
|
||||
var thirdStageSections = ContiguousArray<Section>()
|
||||
var fourthStageSections = ContiguousArray<Section>()
|
||||
|
||||
var sourceElementTraces = contiguousSourceSections.map { section in
|
||||
|
||||
ContiguousArray(repeating: Trace<ElementPath>(), count: section.count)
|
||||
}
|
||||
var targetElementReferences = contiguousTargetSections.map { section in
|
||||
|
||||
ContiguousArray<ElementPath?>(repeating: nil, count: section.count)
|
||||
}
|
||||
|
||||
let flattenSourceCount = contiguousSourceSections.reduce(into: 0) { $0 += $1.count }
|
||||
var flattenSourceIdentifiers = ContiguousArray<ElementIdentifier>()
|
||||
var flattenSourceElementPaths = ContiguousArray<ElementPath>()
|
||||
|
||||
thirdStageSections.reserveCapacity(contiguousTargetSections.count)
|
||||
fourthStageSections.reserveCapacity(contiguousTargetSections.count)
|
||||
|
||||
flattenSourceIdentifiers.reserveCapacity(flattenSourceCount)
|
||||
flattenSourceElementPaths.reserveCapacity(flattenSourceCount)
|
||||
|
||||
let sectionResult = DiffResult.diff(
|
||||
source: sourceSections,
|
||||
target: targetSections,
|
||||
useTargetIndexForUpdated: true,
|
||||
mapIndex: { $0 }
|
||||
)
|
||||
|
||||
var elementDeleted = [ElementPath]()
|
||||
var elementInserted = [ElementPath]()
|
||||
var elementUpdated = [ElementPath]()
|
||||
var elementMoved = [(source: ElementPath, target: ElementPath)]()
|
||||
|
||||
for sourceSectionIndex in contiguousSourceSections.indices {
|
||||
|
||||
for sourceElementIndex in contiguousSourceSections[sourceSectionIndex].indices {
|
||||
|
||||
let sourceElementPath = ElementPath(element: sourceElementIndex, section: sourceSectionIndex)
|
||||
let sourceElement = contiguousSourceSections[sourceElementPath]
|
||||
flattenSourceIdentifiers.append(sourceElement.differenceIdentifier)
|
||||
flattenSourceElementPaths.append(sourceElementPath)
|
||||
}
|
||||
}
|
||||
flattenSourceIdentifiers.withUnsafeBufferPointer { bufferPointer in
|
||||
|
||||
var sourceOccurrencesTable = [TableKey<ElementIdentifier>: Occurrence](minimumCapacity: flattenSourceCount)
|
||||
|
||||
for flattenSourceIndex in flattenSourceIdentifiers.indices {
|
||||
let pointer = bufferPointer.baseAddress!.advanced(by: flattenSourceIndex)
|
||||
let key = TableKey(pointer: pointer)
|
||||
|
||||
switch sourceOccurrencesTable[key] {
|
||||
|
||||
case .none:
|
||||
sourceOccurrencesTable[key] = .unique(index: flattenSourceIndex)
|
||||
|
||||
case .unique(let otherIndex)?:
|
||||
let reference = IndicesReference([otherIndex, flattenSourceIndex])
|
||||
sourceOccurrencesTable[key] = .duplicate(reference: reference)
|
||||
|
||||
case .duplicate(let reference)?:
|
||||
reference.push(flattenSourceIndex)
|
||||
}
|
||||
}
|
||||
|
||||
for targetSectionIndex in contiguousTargetSections.indices {
|
||||
|
||||
let targetElements = contiguousTargetSections[targetSectionIndex]
|
||||
for targetElementIndex in targetElements.indices {
|
||||
|
||||
var targetIdentifier = targetElements[targetElementIndex].differenceIdentifier
|
||||
let key = TableKey(pointer: &targetIdentifier)
|
||||
|
||||
switch sourceOccurrencesTable[key] {
|
||||
|
||||
case .none:
|
||||
break
|
||||
|
||||
case .unique(let flattenSourceIndex)?:
|
||||
let sourceElementPath = flattenSourceElementPaths[flattenSourceIndex]
|
||||
let targetElementPath = ElementPath(element: targetElementIndex, section: targetSectionIndex)
|
||||
if case .none = sourceElementTraces[sourceElementPath].reference {
|
||||
|
||||
targetElementReferences[targetElementPath] = sourceElementPath
|
||||
sourceElementTraces[sourceElementPath].reference = targetElementPath
|
||||
}
|
||||
|
||||
case .duplicate(let reference)?:
|
||||
if let flattenSourceIndex = reference.next() {
|
||||
|
||||
let sourceElementPath = flattenSourceElementPaths[flattenSourceIndex]
|
||||
let targetElementPath = ElementPath(element: targetElementIndex, section: targetSectionIndex)
|
||||
targetElementReferences[targetElementPath] = sourceElementPath
|
||||
sourceElementTraces[sourceElementPath].reference = targetElementPath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for sourceSectionIndex in contiguousSourceSections.indices {
|
||||
|
||||
let sourceSection = sourceSections[sourceSectionIndex]
|
||||
let sourceElements = contiguousSourceSections[sourceSectionIndex]
|
||||
var firstStageElements = sourceElements
|
||||
|
||||
if case .some = sectionResult.sourceTraces[sourceSectionIndex].reference {
|
||||
|
||||
var offsetByDelete = 0
|
||||
var secondStageElements = ContiguousArray<Element>()
|
||||
for sourceElementIndex in sourceElements.indices {
|
||||
|
||||
let sourceElementPath = ElementPath(element: sourceElementIndex, section: sourceSectionIndex)
|
||||
sourceElementTraces[sourceElementPath].deleteOffset = offsetByDelete
|
||||
|
||||
if let targetElementPath = sourceElementTraces[sourceElementPath].reference,
|
||||
case .some = sectionResult.targetReferences[targetElementPath.section] {
|
||||
|
||||
let targetElement = contiguousTargetSections[targetElementPath]
|
||||
firstStageElements[sourceElementIndex] = targetElement
|
||||
secondStageElements.append(targetElement)
|
||||
continue
|
||||
}
|
||||
elementDeleted.append(sourceElementPath)
|
||||
sourceElementTraces[sourceElementPath].isTracked = true
|
||||
offsetByDelete += 1
|
||||
}
|
||||
|
||||
let secondStageSection = Section(source: sourceSection, elements: secondStageElements)
|
||||
secondStageSections.append(secondStageSection)
|
||||
}
|
||||
|
||||
let firstStageSection = Section(source: sourceSection, elements: firstStageElements)
|
||||
firstStageSections[sourceSectionIndex] = firstStageSection
|
||||
}
|
||||
for targetSectionIndex in contiguousTargetSections.indices {
|
||||
|
||||
guard let sourceSectionIndex = sectionResult.targetReferences[targetSectionIndex] else {
|
||||
|
||||
thirdStageSections.append(targetSections[targetSectionIndex])
|
||||
fourthStageSections.append(targetSections[targetSectionIndex])
|
||||
continue
|
||||
}
|
||||
|
||||
var untrackedSourceIndex: Int? = 0
|
||||
let targetElements = contiguousTargetSections[targetSectionIndex]
|
||||
let sectionDeleteOffset = sectionResult.sourceTraces[sourceSectionIndex].deleteOffset
|
||||
let thirdStageSection = secondStageSections[sourceSectionIndex - sectionDeleteOffset]
|
||||
thirdStageSections.append(thirdStageSection)
|
||||
|
||||
var fourthStageElements = ContiguousArray<Element>()
|
||||
fourthStageElements.reserveCapacity(targetElements.count)
|
||||
|
||||
for targetElementIndex in targetElements.indices {
|
||||
|
||||
untrackedSourceIndex = untrackedSourceIndex.flatMap { index in
|
||||
|
||||
sourceElementTraces[sourceSectionIndex].suffix(from: index).firstIndex { !$0.isTracked }
|
||||
}
|
||||
let targetElementPath = ElementPath(element: targetElementIndex, section: targetSectionIndex)
|
||||
let targetElement = contiguousTargetSections[targetElementPath]
|
||||
|
||||
guard
|
||||
let sourceElementPath = targetElementReferences[targetElementPath],
|
||||
let movedSourceSectionIndex = sectionResult.sourceTraces[sourceElementPath.section].reference
|
||||
else {
|
||||
|
||||
fourthStageElements.append(targetElement)
|
||||
elementInserted.append(targetElementPath)
|
||||
continue
|
||||
}
|
||||
sourceElementTraces[sourceElementPath].isTracked = true
|
||||
|
||||
let sourceElement = contiguousSourceSections[sourceElementPath]
|
||||
fourthStageElements.append(targetElement)
|
||||
|
||||
if !targetElement.isContentEqual(to: sourceElement) {
|
||||
|
||||
elementUpdated.append(sourceElementPath)
|
||||
}
|
||||
if sourceElementPath.section != sourceSectionIndex || sourceElementPath.element != untrackedSourceIndex {
|
||||
|
||||
let deleteOffset = sourceElementTraces[sourceElementPath].deleteOffset
|
||||
let moveSourceElementPath = ElementPath(element: sourceElementPath.element - deleteOffset, section: movedSourceSectionIndex)
|
||||
elementMoved.append((source: moveSourceElementPath, target: targetElementPath))
|
||||
}
|
||||
}
|
||||
let fourthStageSection = Section(source: thirdStageSection, elements: fourthStageElements)
|
||||
fourthStageSections.append(fourthStageSection)
|
||||
}
|
||||
|
||||
var changesets = ContiguousArray<Changeset<C>>()
|
||||
if !elementUpdated.isEmpty {
|
||||
|
||||
changesets.append(
|
||||
Changeset(
|
||||
data: C(firstStageSections),
|
||||
elementUpdated: elementUpdated
|
||||
)
|
||||
)
|
||||
}
|
||||
if !sectionResult.deleted.isEmpty || !elementDeleted.isEmpty {
|
||||
|
||||
changesets.append(
|
||||
Changeset(
|
||||
data: C(secondStageSections),
|
||||
sectionDeleted: sectionResult.deleted,
|
||||
elementDeleted: elementDeleted
|
||||
)
|
||||
)
|
||||
}
|
||||
if !sectionResult.inserted.isEmpty || !sectionResult.moved.isEmpty {
|
||||
|
||||
changesets.append(
|
||||
Changeset(
|
||||
data: C(thirdStageSections),
|
||||
sectionInserted: sectionResult.inserted,
|
||||
sectionMoved: sectionResult.moved
|
||||
)
|
||||
)
|
||||
}
|
||||
if !elementInserted.isEmpty || !elementMoved.isEmpty {
|
||||
|
||||
changesets.append(
|
||||
Changeset(
|
||||
data: C(fourthStageSections),
|
||||
elementInserted: elementInserted,
|
||||
elementMoved: elementMoved
|
||||
)
|
||||
)
|
||||
}
|
||||
if !sectionResult.updated.isEmpty {
|
||||
|
||||
changesets.append(
|
||||
Changeset(
|
||||
data: target,
|
||||
sectionUpdated: sectionResult.updated
|
||||
)
|
||||
)
|
||||
}
|
||||
if !changesets.isEmpty {
|
||||
|
||||
let index = changesets.index(before: changesets.endIndex)
|
||||
changesets[index].data = target
|
||||
}
|
||||
self.init(changesets)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - MutableCollection
|
||||
|
||||
extension MutableCollection where Element: MutableCollection, Index == Int, Element.Index == Int {
|
||||
|
||||
@inlinable
|
||||
internal subscript<O>(path: Internals.DiffableDataUIDispatcher<O>.ElementPath) -> Element.Element {
|
||||
|
||||
get { return self[path.section][path.element] }
|
||||
set { self[path.section][path.element] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,299 @@
|
||||
//
|
||||
// Internals.DiffableDataUIDispatcher.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) || canImport(AppKit)
|
||||
|
||||
import CoreData
|
||||
|
||||
#if canImport(QuartzCore)
|
||||
import QuartzCore
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - Internals
|
||||
|
||||
extension Internals {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DiffableDataSources
|
||||
@usableFromInline
|
||||
internal final class DiffableDataUIDispatcher<O: DynamicObject> {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
typealias ObjectType = O
|
||||
|
||||
init(dataStack: DataStack) {
|
||||
|
||||
self.dataStack = dataStack
|
||||
}
|
||||
|
||||
func purge<Target: DiffableDataSource.Target>(
|
||||
target: Target?,
|
||||
animatingDifferences: Bool,
|
||||
performUpdates: @escaping (
|
||||
Target,
|
||||
StagedChangeset<[Internals.DiffableDataSourceSnapshot.Section]>,
|
||||
@escaping ([Internals.DiffableDataSourceSnapshot.Section]) -> Void
|
||||
) -> Void,
|
||||
completion: @escaping () -> Void
|
||||
) {
|
||||
|
||||
self.apply(
|
||||
.init(),
|
||||
target: target,
|
||||
animatingDifferences: animatingDifferences,
|
||||
performUpdates: performUpdates,
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
|
||||
func apply<Target: DiffableDataSource.Target>(
|
||||
_ snapshot: DiffableDataSourceSnapshot,
|
||||
target: Target?,
|
||||
animatingDifferences: Bool,
|
||||
performUpdates: @escaping (
|
||||
Target,
|
||||
StagedChangeset<[Internals.DiffableDataSourceSnapshot.Section]>,
|
||||
@escaping ([Internals.DiffableDataSourceSnapshot.Section]) -> Void
|
||||
) -> Void,
|
||||
completion: @escaping () -> Void
|
||||
) {
|
||||
|
||||
self.dispatcher.dispatch { [weak self] in
|
||||
|
||||
guard let self = self else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
self.currentSnapshot = snapshot
|
||||
|
||||
let newSections = snapshot.sections
|
||||
guard let target = target else {
|
||||
|
||||
self.sections = newSections
|
||||
return
|
||||
}
|
||||
|
||||
let performDiffingUpdates: () -> Void = {
|
||||
|
||||
let changeset = StagedChangeset(source: self.sections, target: newSections)
|
||||
performUpdates(target, changeset) { sections in
|
||||
|
||||
self.sections = sections
|
||||
}
|
||||
}
|
||||
|
||||
#if canImport(QuartzCore)
|
||||
|
||||
CATransaction.begin()
|
||||
CATransaction.setCompletionBlock(completion)
|
||||
|
||||
if !animatingDifferences {
|
||||
|
||||
CATransaction.setDisableActions(true)
|
||||
}
|
||||
performDiffingUpdates()
|
||||
|
||||
CATransaction.commit()
|
||||
|
||||
|
||||
#else
|
||||
|
||||
performDiffingUpdates()
|
||||
completion()
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
func snapshot() -> DiffableDataSourceSnapshot {
|
||||
|
||||
var snapshot: DiffableDataSourceSnapshot = .init()
|
||||
snapshot.sections = self.currentSnapshot.sections
|
||||
return snapshot
|
||||
}
|
||||
|
||||
func sectionIdentifier(inSection section: Int) -> String? {
|
||||
|
||||
guard self.sections.indices.contains(section) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return self.sections[section].differenceIdentifier
|
||||
}
|
||||
|
||||
func itemIdentifier(for indexPath: IndexPath) -> O.ObjectID? {
|
||||
|
||||
guard self.sections.indices.contains(indexPath.section) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let items = self.sections[indexPath.section].elements
|
||||
guard items.indices.contains(indexPath.item) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return items[indexPath.item].differenceIdentifier
|
||||
}
|
||||
|
||||
func indexPath(for itemIdentifier: O.ObjectID) -> IndexPath? {
|
||||
|
||||
let indexPathMap: [O.ObjectID: IndexPath] = self.sections.enumerated().reduce(into: [:]) { result, section in
|
||||
|
||||
for (itemIndex, item) in section.element.elements.enumerated() {
|
||||
|
||||
result[item.differenceIdentifier] = IndexPath(
|
||||
item: itemIndex,
|
||||
section: section.offset
|
||||
)
|
||||
}
|
||||
}
|
||||
return indexPathMap[itemIdentifier]
|
||||
}
|
||||
|
||||
func numberOfSections() -> Int {
|
||||
|
||||
return self.sections.count
|
||||
}
|
||||
|
||||
func numberOfItems(inSection section: Int) -> Int? {
|
||||
|
||||
guard self.sections.indices.contains(section) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return self.sections[section].elements.count
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let dispatcher: MainThreadSerialDispatcher = .init()
|
||||
private let dataStack: DataStack
|
||||
|
||||
private var currentSnapshot: Internals.DiffableDataSourceSnapshot = .init()
|
||||
private var sections: [Internals.DiffableDataSourceSnapshot.Section] = []
|
||||
|
||||
|
||||
// MARK: - ElementPath
|
||||
|
||||
@usableFromInline
|
||||
internal struct ElementPath: Hashable {
|
||||
|
||||
@usableFromInline
|
||||
var element: Int
|
||||
|
||||
@usableFromInline
|
||||
var section: Int
|
||||
|
||||
@inlinable
|
||||
init(element: Int, section: Int) {
|
||||
|
||||
self.element = element
|
||||
self.section = section
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - MainThreadSerialDispatcher
|
||||
|
||||
fileprivate final class MainThreadSerialDispatcher {
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate init() {}
|
||||
|
||||
fileprivate func dispatch(_ action: @escaping () -> Void) {
|
||||
|
||||
let count = self.executingCount.incrementAndGet()
|
||||
if Thread.isMainThread && count == 1 {
|
||||
|
||||
action()
|
||||
self.executingCount.decrement()
|
||||
}
|
||||
else {
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
|
||||
guard let self = self else {
|
||||
|
||||
return
|
||||
}
|
||||
action()
|
||||
self.executingCount.decrement()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let executingCount: AtomicInt = .init()
|
||||
|
||||
|
||||
// MARK: - AtomicInt
|
||||
|
||||
fileprivate class AtomicInt {
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate func incrementAndGet() -> Int {
|
||||
|
||||
self.lock.wait()
|
||||
defer {
|
||||
|
||||
self.lock.signal()
|
||||
}
|
||||
self.value += 1
|
||||
return self.value
|
||||
}
|
||||
|
||||
fileprivate func decrement() {
|
||||
|
||||
self.lock.wait()
|
||||
defer {
|
||||
|
||||
self.lock.signal()
|
||||
}
|
||||
self.value -= 1
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let lock = DispatchSemaphore(value: 1)
|
||||
private var value = 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// Internals.FetchedDiffableDataSourceSnapshotDelegate.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
#if canImport(UIKit)
|
||||
|
||||
import UIKit
|
||||
|
||||
#elseif canImport(AppKit)
|
||||
|
||||
import AppKit
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - FetchedDiffableDataSourceSnapshot
|
||||
|
||||
internal protocol FetchedDiffableDataSourceSnapshotHandler: AnyObject {
|
||||
|
||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: Internals.DiffableDataSourceSnapshot)
|
||||
|
||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, sectionIndexTitleForSectionName sectionName: String?) -> String?
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
extension Internals {
|
||||
|
||||
// MARK: - FetchedDiffableDataSourceSnapshotDelegate
|
||||
|
||||
internal final class FetchedDiffableDataSourceSnapshotDelegate: NSObject, NSFetchedResultsControllerDelegate {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
@nonobjc
|
||||
internal weak var handler: FetchedDiffableDataSourceSnapshotHandler?
|
||||
|
||||
@nonobjc
|
||||
internal weak var fetchedResultsController: Internals.CoreStoreFetchedResultsController? {
|
||||
|
||||
didSet {
|
||||
|
||||
oldValue?.delegate = nil
|
||||
self.fetchedResultsController?.delegate = self
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
self.fetchedResultsController?.delegate = nil
|
||||
}
|
||||
|
||||
internal func initialFetch() {
|
||||
|
||||
guard let fetchedResultsController = self.fetchedResultsController else {
|
||||
|
||||
return
|
||||
}
|
||||
self.controllerDidChangeContent(fetchedResultsController.dynamicCast())
|
||||
}
|
||||
|
||||
|
||||
// MARK: NSFetchedResultsControllerDelegate
|
||||
|
||||
@objc
|
||||
dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||
|
||||
var snapshot = Internals.DiffableDataSourceSnapshot(
|
||||
sections: controller.sections ?? [],
|
||||
fetchOffset: controller.fetchRequest.fetchOffset,
|
||||
fetchLimit: controller.fetchRequest.fetchLimit
|
||||
)
|
||||
snapshot.reloadSections(self.reloadedSectionIDs)
|
||||
snapshot.reloadItems(self.reloadedItemIDs)
|
||||
|
||||
self.handler?.controller(
|
||||
controller,
|
||||
didChangeContentWith: snapshot
|
||||
)
|
||||
self.reloadedItemIDs.removeAll()
|
||||
self.reloadedSectionIDs.removeAll()
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, sectionIndexTitleForSectionName sectionName: String) -> String? {
|
||||
|
||||
return self.handler?.controller(
|
||||
controller,
|
||||
sectionIndexTitleForSectionName: sectionName
|
||||
)
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
|
||||
|
||||
let object = anObject as! NSManagedObject
|
||||
self.reloadedItemIDs.append(object.objectID)
|
||||
}
|
||||
|
||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
|
||||
|
||||
self.reloadedSectionIDs.append(sectionInfo.name)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var reloadedItemIDs: [NSManagedObjectID] = []
|
||||
private var reloadedSectionIDs: [String] = []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// Internals.LazyNonmutating.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
|
||||
// MARK: - Internals
|
||||
|
||||
extension Internals {
|
||||
|
||||
// MARK: - LazyNonmutating
|
||||
|
||||
@propertyWrapper
|
||||
internal final class LazyNonmutating<Value> {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(_ initializer: @escaping () -> Value) {
|
||||
|
||||
self.initializer = initializer
|
||||
}
|
||||
|
||||
init(uninitialized: Void) {
|
||||
|
||||
self.initializer = { fatalError() }
|
||||
}
|
||||
|
||||
func initialize(_ initializer: @escaping () -> Value) {
|
||||
|
||||
self.initializer = initializer
|
||||
}
|
||||
|
||||
func reset(_ initializer: @escaping () -> Value) {
|
||||
|
||||
self.initializer = initializer
|
||||
self.initializedValue = nil
|
||||
}
|
||||
|
||||
|
||||
// MARK: @propertyWrapper
|
||||
|
||||
var wrappedValue: Value {
|
||||
|
||||
get {
|
||||
|
||||
if let initializedValue = self.initializedValue {
|
||||
|
||||
return initializedValue
|
||||
}
|
||||
let initializedValue = self.initializer()
|
||||
self.initializedValue = initializedValue
|
||||
return initializedValue
|
||||
}
|
||||
set {
|
||||
|
||||
self.initializer = { newValue }
|
||||
self.initializedValue = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var projectedValue: Internals.LazyNonmutating<Value> {
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var initializer: () -> Value
|
||||
private var initializedValue: Value? = nil
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ extension Internals {
|
||||
|
||||
internal final class NotificationObserver {
|
||||
|
||||
// MARK: Public
|
||||
// MARK: Internal
|
||||
|
||||
let observer: NSObjectProtocol
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// Internals.SharedNotificationObserver.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
extension Internals {
|
||||
|
||||
// MARK: - SharedNotificationObserver
|
||||
|
||||
internal final class SharedNotificationObserver<T> {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(notificationName: Notification.Name, object: Any?, queue: OperationQueue? = nil, sharedValue: @escaping (_ note: Notification) -> T) {
|
||||
|
||||
self.observer = NotificationCenter.default.addObserver(
|
||||
forName: notificationName,
|
||||
object: object,
|
||||
queue: queue,
|
||||
using: { [weak self] (notification) in
|
||||
|
||||
guard let self = self else {
|
||||
|
||||
return
|
||||
}
|
||||
let value = sharedValue(notification)
|
||||
self.notifyObservers(value)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
self.observer.map(NotificationCenter.default.removeObserver(_:))
|
||||
self.observers.removeAllObjects()
|
||||
}
|
||||
|
||||
internal func addObserver<U: AnyObject>(_ observer: U, closure: @escaping (T) -> Void) {
|
||||
|
||||
self.observers.setObject(Closure<T, Void>(closure), forKey: observer)
|
||||
}
|
||||
|
||||
internal func removeObserver<U: AnyObject>(_ observer: U) {
|
||||
|
||||
self.observers.removeObject(forKey: observer)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var observer: NSObjectProtocol!
|
||||
private let observers: NSMapTable<AnyObject, Closure<T, Void>> = .weakToStrongObjects()
|
||||
|
||||
private func notifyObservers(_ sharedValue: T) {
|
||||
|
||||
guard let enumerator = self.observers.objectEnumerator() else {
|
||||
|
||||
return
|
||||
}
|
||||
for closure in enumerator {
|
||||
|
||||
(closure as! Closure<T, Void>).invoke(with: sharedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import Foundation
|
||||
|
||||
// MARK: - Internals
|
||||
|
||||
@usableFromInline
|
||||
internal enum Internals {
|
||||
|
||||
// MARK: Associated Objects
|
||||
|
||||
+13
-7
@@ -39,12 +39,12 @@ import CoreData
|
||||
let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
|
||||
```
|
||||
*/
|
||||
public struct Into<D: DynamicObject>: Hashable {
|
||||
public struct Into<O: DynamicObject>: Hashable {
|
||||
|
||||
/**
|
||||
The associated `NSManagedObject` or `CoreStoreObject` entity class
|
||||
*/
|
||||
public let entityClass: D.Type
|
||||
public let entityClass: O.Type
|
||||
|
||||
/**
|
||||
The `NSPersistentStore` configuration name to associate objects from.
|
||||
@@ -60,7 +60,7 @@ public struct Into<D: DynamicObject>: Hashable {
|
||||
*/
|
||||
public init() {
|
||||
|
||||
self.init(entityClass: D.self, configuration: nil, inferStoreIfPossible: true)
|
||||
self.init(entityClass: O.self, configuration: nil, inferStoreIfPossible: true)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +70,7 @@ public struct Into<D: DynamicObject>: Hashable {
|
||||
```
|
||||
- parameter entity: the `NSManagedObject` or `CoreStoreObject` type to be created
|
||||
*/
|
||||
public init(_ entity: D.Type) {
|
||||
public init(_ entity: O.Type) {
|
||||
|
||||
self.init(entityClass: entity, configuration: nil, inferStoreIfPossible: true)
|
||||
}
|
||||
@@ -84,7 +84,7 @@ public struct Into<D: DynamicObject>: Hashable {
|
||||
*/
|
||||
public init(_ configuration: ModelConfiguration) {
|
||||
|
||||
self.init(entityClass: D.self, configuration: configuration, inferStoreIfPossible: false)
|
||||
self.init(entityClass: O.self, configuration: configuration, inferStoreIfPossible: false)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +95,7 @@ public struct Into<D: DynamicObject>: Hashable {
|
||||
- parameter entity: the `NSManagedObject` or `CoreStoreObject` type to be created
|
||||
- parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s or `CoreStoreObject`'s entity type. Set to `nil` to use the default configuration.
|
||||
*/
|
||||
public init(_ entity: D.Type, _ configuration: ModelConfiguration) {
|
||||
public init(_ entity: O.Type, _ configuration: ModelConfiguration) {
|
||||
|
||||
self.init(entityClass: entity, configuration: configuration, inferStoreIfPossible: false)
|
||||
}
|
||||
@@ -125,10 +125,16 @@ public struct Into<D: DynamicObject>: Hashable {
|
||||
|
||||
internal let inferStoreIfPossible: Bool
|
||||
|
||||
internal init(entityClass: D.Type, configuration: ModelConfiguration, inferStoreIfPossible: Bool) {
|
||||
internal init(entityClass: O.Type, configuration: ModelConfiguration, inferStoreIfPossible: Bool) {
|
||||
|
||||
self.entityClass = entityClass
|
||||
self.configuration = configuration
|
||||
self.inferStoreIfPossible = inferStoreIfPossible
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import Foundation
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.nickname == "John"))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.nickname == "John"))
|
||||
```
|
||||
*/
|
||||
public func == <O: NSManagedObject, V: QueryableAttributeType & Equatable>(_ keyPath: KeyPath<O, V>, _ value: V) -> Where<O> {
|
||||
@@ -43,7 +43,7 @@ public func == <O: NSManagedObject, V: QueryableAttributeType & Equatable>(_ key
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.nickname != "John"))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.nickname != "John"))
|
||||
```
|
||||
*/
|
||||
public func != <O: NSManagedObject, V: QueryableAttributeType & Equatable>(_ keyPath: KeyPath<O, V>, _ value: V) -> Where<O> {
|
||||
@@ -54,7 +54,7 @@ public func != <O: NSManagedObject, V: QueryableAttributeType & Equatable>(_ key
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains the value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname))
|
||||
```
|
||||
*/
|
||||
public func ~= <O: NSManagedObject, V: QueryableAttributeType & Equatable, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, V>) -> Where<O> where S.Iterator.Element == V {
|
||||
@@ -68,7 +68,7 @@ public func ~= <O: NSManagedObject, V: QueryableAttributeType & Equatable, S: Se
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.nickname == "John"))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.nickname == "John"))
|
||||
```
|
||||
*/
|
||||
public func == <O: NSManagedObject, V: QueryableAttributeType & Equatable>(_ keyPath: KeyPath<O, Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -79,7 +79,7 @@ public func == <O: NSManagedObject, V: QueryableAttributeType & Equatable>(_ key
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.nickname != "John"))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.nickname != "John"))
|
||||
```
|
||||
*/
|
||||
public func != <O: NSManagedObject, V: QueryableAttributeType & Equatable>(_ keyPath: KeyPath<O, Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -90,7 +90,7 @@ public func != <O: NSManagedObject, V: QueryableAttributeType & Equatable>(_ key
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains the value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname))
|
||||
```
|
||||
*/
|
||||
public func ~= <O: NSManagedObject, V: QueryableAttributeType & Equatable, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, Optional<V>>) -> Where<O> where S.Iterator.Element == V {
|
||||
@@ -104,7 +104,7 @@ public func ~= <O: NSManagedObject, V: QueryableAttributeType & Equatable, S: Se
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age < 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age < 20))
|
||||
```
|
||||
*/
|
||||
public func < <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ keyPath: KeyPath<O, V>, _ value: V) -> Where<O> {
|
||||
@@ -115,7 +115,7 @@ public func < <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ key
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age > 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age > 20))
|
||||
```
|
||||
*/
|
||||
public func > <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ keyPath: KeyPath<O, V>, _ value: V) -> Where<O> {
|
||||
@@ -126,7 +126,7 @@ public func > <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ key
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age <= 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age <= 20))
|
||||
```
|
||||
*/
|
||||
public func <= <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ keyPath: KeyPath<O, V>, _ value: V) -> Where<O> {
|
||||
@@ -137,7 +137,7 @@ public func <= <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ ke
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age >= 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age >= 20))
|
||||
```
|
||||
*/
|
||||
public func >= <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ keyPath: KeyPath<O, V>, _ value: V) -> Where<O> {
|
||||
@@ -151,7 +151,7 @@ public func >= <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ ke
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age < 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age < 20))
|
||||
```
|
||||
*/
|
||||
public func < <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ keyPath: KeyPath<O, Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -169,7 +169,7 @@ public func < <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ key
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age > 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age > 20))
|
||||
```
|
||||
*/
|
||||
public func > <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ keyPath: KeyPath<O, Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -187,7 +187,7 @@ public func > <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ key
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age <= 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age <= 20))
|
||||
```
|
||||
*/
|
||||
public func <= <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ keyPath: KeyPath<O, Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -205,7 +205,7 @@ public func <= <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ ke
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age >= 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age >= 20))
|
||||
```
|
||||
*/
|
||||
public func >= <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ keyPath: KeyPath<O, Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -226,7 +226,7 @@ public func >= <O: NSManagedObject, V: QueryableAttributeType & Comparable>(_ ke
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master == john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master == john))
|
||||
```
|
||||
*/
|
||||
public func == <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, D>, _ object: D) -> Where<O> {
|
||||
@@ -237,7 +237,7 @@ public func == <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, D>
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master != john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master != john))
|
||||
```
|
||||
*/
|
||||
public func != <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, D>, _ object: D) -> Where<O> {
|
||||
@@ -248,7 +248,7 @@ public func != <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, D>
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains a value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where([john, bob, joe] ~= \.master))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where([john, bob, joe] ~= \.master))
|
||||
```
|
||||
*/
|
||||
public func ~= <O: NSManagedObject, D: NSManagedObject, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, D>) -> Where<O> where S.Iterator.Element == D {
|
||||
@@ -259,7 +259,7 @@ public func ~= <O: NSManagedObject, D: NSManagedObject, S: Sequence>(_ sequence:
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master == john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master == john))
|
||||
```
|
||||
*/
|
||||
public func == <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, D>, _ objectID: NSManagedObjectID) -> Where<O> {
|
||||
@@ -270,7 +270,7 @@ public func == <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, D>
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master != john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master != john))
|
||||
```
|
||||
*/
|
||||
public func != <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, D>, _ objectID: NSManagedObjectID) -> Where<O> {
|
||||
@@ -281,7 +281,7 @@ public func != <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, D>
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains a value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where([john, bob, joe] ~= \.master))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where([john, bob, joe] ~= \.master))
|
||||
```
|
||||
*/
|
||||
public func ~= <O: NSManagedObject, D: NSManagedObject, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, D>) -> Where<O> where S.Iterator.Element == NSManagedObjectID {
|
||||
@@ -295,7 +295,7 @@ public func ~= <O: NSManagedObject, D: NSManagedObject, S: Sequence>(_ sequence:
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master == john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master == john))
|
||||
```
|
||||
*/
|
||||
public func == <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, Optional<D>>, _ object: D?) -> Where<O> {
|
||||
@@ -306,7 +306,7 @@ public func == <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, Op
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master != john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master != john))
|
||||
```
|
||||
*/
|
||||
public func != <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, Optional<D>>, _ object: D?) -> Where<O> {
|
||||
@@ -317,7 +317,7 @@ public func != <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, Op
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains a value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where([john, bob, joe] ~= \.master))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where([john, bob, joe] ~= \.master))
|
||||
```
|
||||
*/
|
||||
public func ~= <O: NSManagedObject, D: NSManagedObject, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, Optional<D>>) -> Where<O> where S.Iterator.Element == D {
|
||||
@@ -328,7 +328,7 @@ public func ~= <O: NSManagedObject, D: NSManagedObject, S: Sequence>(_ sequence:
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master == john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master == john))
|
||||
```
|
||||
*/
|
||||
public func == <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, Optional<D>>, _ objectID: NSManagedObjectID) -> Where<O> {
|
||||
@@ -339,7 +339,7 @@ public func == <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, Op
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master != john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master != john))
|
||||
```
|
||||
*/
|
||||
public func != <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, Optional<D>>, _ objectID: NSManagedObjectID) -> Where<O> {
|
||||
@@ -350,7 +350,7 @@ public func != <O: NSManagedObject, D: NSManagedObject>(_ keyPath: KeyPath<O, Op
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains a value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where([john, bob, joe] ~= \.master))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where([john, bob, joe] ~= \.master))
|
||||
```
|
||||
*/
|
||||
public func ~= <O: NSManagedObject, D: NSManagedObject, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, Optional<D>>) -> Where<O> where S.Iterator.Element == NSManagedObjectID {
|
||||
@@ -364,7 +364,7 @@ public func ~= <O: NSManagedObject, D: NSManagedObject, S: Sequence>(_ sequence:
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.nickname == "John"))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.nickname == "John"))
|
||||
```
|
||||
*/
|
||||
public func == <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, _ value: V) -> Where<O> {
|
||||
@@ -375,7 +375,7 @@ public func == <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, _ va
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.nickname != "John"))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.nickname != "John"))
|
||||
```
|
||||
*/
|
||||
public func != <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, _ value: V) -> Where<O> {
|
||||
@@ -386,7 +386,7 @@ public func != <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, _ va
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains the value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname))
|
||||
```
|
||||
*/
|
||||
public func ~= <O, V, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>) -> Where<O> where S.Iterator.Element == V {
|
||||
@@ -400,7 +400,7 @@ public func ~= <O, V, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, ValueCon
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.nickname == "John"))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.nickname == "John"))
|
||||
```
|
||||
*/
|
||||
public func == <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -411,7 +411,7 @@ public func == <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ va
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.nickname != "John"))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.nickname != "John"))
|
||||
```
|
||||
*/
|
||||
public func != <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -422,7 +422,7 @@ public func != <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ va
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains the value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(["Pluto", "Snoopy", "Scooby"] ~= \.nickname))
|
||||
```
|
||||
*/
|
||||
public func ~= <O, V, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>) -> Where<O> where S.Iterator.Element == V {
|
||||
@@ -436,7 +436,7 @@ public func ~= <O, V, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, ValueCon
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age < 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age < 20))
|
||||
```
|
||||
*/
|
||||
public func < <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, _ value: V) -> Where<O> {
|
||||
@@ -447,7 +447,7 @@ public func < <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Require
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age > 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age > 20))
|
||||
```
|
||||
*/
|
||||
public func > <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, _ value: V) -> Where<O> {
|
||||
@@ -458,7 +458,7 @@ public func > <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Require
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age <= 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age <= 20))
|
||||
```
|
||||
*/
|
||||
public func <= <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, _ value: V) -> Where<O> {
|
||||
@@ -469,7 +469,7 @@ public func <= <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Requir
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age >= 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age >= 20))
|
||||
```
|
||||
*/
|
||||
public func >= <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, _ value: V) -> Where<O> {
|
||||
@@ -483,7 +483,7 @@ public func >= <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Requir
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age < 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age < 20))
|
||||
```
|
||||
*/
|
||||
public func < <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -501,7 +501,7 @@ public func < <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ val
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age > 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age > 20))
|
||||
```
|
||||
*/
|
||||
public func > <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -519,7 +519,7 @@ public func > <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ val
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age <= 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age <= 20))
|
||||
```
|
||||
*/
|
||||
public func <= <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -537,7 +537,7 @@ public func <= <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ va
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than or equal to a value
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>().where(\.age >= 20))
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.age >= 20))
|
||||
```
|
||||
*/
|
||||
public func >= <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ value: V?) -> Where<O> {
|
||||
@@ -558,7 +558,7 @@ public func >= <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ va
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master == john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master == john))
|
||||
```
|
||||
*/
|
||||
public func == <O, D>(_ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>, _ object: D) -> Where<O> {
|
||||
@@ -569,7 +569,7 @@ public func == <O, D>(_ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>,
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master == john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master == john))
|
||||
```
|
||||
*/
|
||||
public func == <O, D>(_ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>, _ object: D?) -> Where<O> {
|
||||
@@ -580,7 +580,7 @@ public func == <O, D>(_ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>,
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master != john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master != john))
|
||||
```
|
||||
*/
|
||||
public func != <O, D>(_ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>, _ object: D) -> Where<O> {
|
||||
@@ -591,7 +591,7 @@ public func != <O, D>(_ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>,
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where(\.master != john))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.master != john))
|
||||
```
|
||||
*/
|
||||
public func != <O, D>(_ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>, _ object: D?) -> Where<O> {
|
||||
@@ -602,7 +602,7 @@ public func != <O, D>(_ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>,
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains a value of a property
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>().where([john, bob, joe] ~= \.master))
|
||||
let dog = dataStack.fetchOne(From<Dog>().where([john, bob, joe] ~= \.master))
|
||||
```
|
||||
*/
|
||||
public func ~= <O, D, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>) -> Where<O> where S.Iterator.Element == D {
|
||||
|
||||
+60
-120
@@ -32,7 +32,7 @@ import CoreData
|
||||
/**
|
||||
The `ListMonitor` monitors changes to a list of `DynamicObject` instances. Observers that implement the `ListObserver` protocol may then register themselves to the `ListMonitor`'s `addObserver(_:)` method:
|
||||
```
|
||||
let monitor = CoreStore.monitorList(
|
||||
let monitor = dataStack.monitorList(
|
||||
From<Person>(),
|
||||
Where("title", isEqualTo: "Engineer"),
|
||||
OrderBy(.ascending("lastName"))
|
||||
@@ -50,7 +50,7 @@ import CoreData
|
||||
|
||||
Creating a sectioned-list is also possible with the `monitorSectionedList(...)` method:
|
||||
```
|
||||
let monitor = CoreStore.monitorSectionedList(
|
||||
let monitor = dataStack.monitorSectionedList(
|
||||
From<Person>(),
|
||||
SectionBy("age") { "Age \($0)" },
|
||||
Where("title", isEqualTo: "Engineer"),
|
||||
@@ -67,14 +67,14 @@ import CoreData
|
||||
In the example above, both `person1` and `person2` will contain the object at section=2, index=3.
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
public final class ListMonitor<O: DynamicObject>: Hashable {
|
||||
|
||||
// MARK: Public (Accessors)
|
||||
|
||||
/**
|
||||
The type for the objects contained bye the `ListMonitor`
|
||||
*/
|
||||
public typealias ObjectType = D
|
||||
public typealias ObjectType = O
|
||||
|
||||
/**
|
||||
Returns the object at the given index within the first section. This subscript indexer is typically used for `ListMonitor`s created with `monitorList(_:)`.
|
||||
@@ -82,7 +82,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
- parameter index: the index of the object. Using an index above the valid range will raise an exception.
|
||||
- returns: the `DynamicObject` at the specified index
|
||||
*/
|
||||
public subscript(index: Int) -> ObjectType {
|
||||
public subscript(index: Int) -> O {
|
||||
|
||||
Internals.assert(
|
||||
!self.isPendingRefetch || Thread.isMainThread,
|
||||
@@ -90,7 +90,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
)
|
||||
if self.isSectioned {
|
||||
|
||||
return ObjectType.cs_fromRaw(object: (self.fetchedResultsController.fetchedObjects as NSArray?)![index] as! NSManagedObject)
|
||||
return O.cs_fromRaw(object: (self.fetchedResultsController.fetchedObjects as NSArray?)![index] as! NSManagedObject)
|
||||
}
|
||||
return self[0, index]
|
||||
}
|
||||
@@ -101,14 +101,14 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
- parameter index: the index for the object. Using an index above the valid range will return `nil`.
|
||||
- returns: the `DynamicObject` at the specified index, or `nil` if out of bounds
|
||||
*/
|
||||
public subscript(safeIndex index: Int) -> ObjectType? {
|
||||
public subscript(safeIndex index: Int) -> O? {
|
||||
|
||||
if self.isSectioned {
|
||||
|
||||
let fetchedObjects = (self.fetchedResultsController.fetchedObjects as NSArray?)!
|
||||
if index < fetchedObjects.count && index >= 0 {
|
||||
|
||||
return ObjectType.cs_fromRaw(object: fetchedObjects[index] as! NSManagedObject)
|
||||
return O.cs_fromRaw(object: fetchedObjects[index] as! NSManagedObject)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
- parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will raise an exception.
|
||||
- returns: the `DynamicObject` at the specified section and item index
|
||||
*/
|
||||
public subscript(sectionIndex: Int, itemIndex: Int) -> ObjectType {
|
||||
public subscript(sectionIndex: Int, itemIndex: Int) -> O {
|
||||
|
||||
return self[IndexPath(indexes: [sectionIndex, itemIndex])]
|
||||
}
|
||||
@@ -134,7 +134,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
- parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will return `nil`.
|
||||
- returns: the `DynamicObject` at the specified section and item index, or `nil` if out of bounds
|
||||
*/
|
||||
public subscript(safeSectionIndex sectionIndex: Int, safeItemIndex itemIndex: Int) -> ObjectType? {
|
||||
public subscript(safeSectionIndex sectionIndex: Int, safeItemIndex itemIndex: Int) -> O? {
|
||||
|
||||
guard let section = self.sectionInfo(safelyAt: sectionIndex) else {
|
||||
|
||||
@@ -153,13 +153,13 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
- parameter indexPath: the `IndexPath` for the object. Using an `indexPath` with an invalid range will raise an exception.
|
||||
- returns: the `DynamicObject` at the specified index path
|
||||
*/
|
||||
public subscript(indexPath: IndexPath) -> ObjectType {
|
||||
public subscript(indexPath: IndexPath) -> O {
|
||||
|
||||
Internals.assert(
|
||||
!self.isPendingRefetch || Thread.isMainThread,
|
||||
"Attempted to access a \(Internals.typeName(self)) outside the main thread while a refetch is in progress."
|
||||
)
|
||||
return ObjectType.cs_fromRaw(object: self.fetchedResultsController.object(at: indexPath))
|
||||
return O.cs_fromRaw(object: self.fetchedResultsController.object(at: indexPath))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +168,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
- parameter indexPath: the `IndexPath` for the object. Using an `indexPath` with an invalid range will return `nil`.
|
||||
- returns: the `DynamicObject` at the specified index path, or `nil` if out of bounds
|
||||
*/
|
||||
public subscript(safeIndexPath indexPath: IndexPath) -> ObjectType? {
|
||||
public subscript(safeIndexPath indexPath: IndexPath) -> O? {
|
||||
|
||||
return self[
|
||||
safeSectionIndex: indexPath[0],
|
||||
@@ -345,7 +345,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
- parameter object: the `DynamicObject` to search the index of
|
||||
- returns: the index of the `DynamicObject` if it exists in the `ListMonitor`'s fetched objects, or `nil` if not found.
|
||||
*/
|
||||
public func index(of object: ObjectType) -> Int? {
|
||||
public func index(of object: O) -> Int? {
|
||||
|
||||
Internals.assert(
|
||||
!self.isPendingRefetch || Thread.isMainThread,
|
||||
@@ -364,7 +364,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
- parameter object: the `DynamicObject` to search the index of
|
||||
- returns: the `IndexPath` of the `DynamicObject` if it exists in the `ListMonitor`'s fetched objects, or `nil` if not found.
|
||||
*/
|
||||
public func indexPath(of object: ObjectType) -> IndexPath? {
|
||||
public func indexPath(of object: O) -> IndexPath? {
|
||||
|
||||
Internals.assert(
|
||||
!self.isPendingRefetch || Thread.isMainThread,
|
||||
@@ -387,7 +387,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
|
||||
- parameter observer: a `ListObserver` to send change notifications to
|
||||
*/
|
||||
public func addObserver<U: ListObserver>(_ observer: U) where U.ListEntityType == ObjectType {
|
||||
public func addObserver<U: ListObserver>(_ observer: U) where U.ListEntityType == O {
|
||||
|
||||
self.unregisterObserver(observer)
|
||||
self.registerObserver(
|
||||
@@ -422,7 +422,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
|
||||
- parameter observer: a `ListObjectObserver` to send change notifications to
|
||||
*/
|
||||
public func addObserver<U: ListObjectObserver>(_ observer: U) where U.ListEntityType == ObjectType {
|
||||
public func addObserver<U: ListObjectObserver>(_ observer: U) where U.ListEntityType == O {
|
||||
|
||||
self.unregisterObserver(observer)
|
||||
self.registerObserver(
|
||||
@@ -476,7 +476,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
|
||||
- parameter observer: a `ListSectionObserver` to send change notifications to
|
||||
*/
|
||||
public func addObserver<U: ListSectionObserver>(_ observer: U) where U.ListEntityType == ObjectType {
|
||||
public func addObserver<U: ListSectionObserver>(_ observer: U) where U.ListEntityType == O {
|
||||
|
||||
self.unregisterObserver(observer)
|
||||
self.registerObserver(
|
||||
@@ -537,7 +537,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
|
||||
- parameter observer: a `ListObserver` to unregister notifications to
|
||||
*/
|
||||
public func removeObserver<U: ListObserver>(_ observer: U) where U.ListEntityType == ObjectType {
|
||||
public func removeObserver<U: ListObserver>(_ observer: U) where U.ListEntityType == O {
|
||||
|
||||
self.unregisterObserver(observer)
|
||||
}
|
||||
@@ -597,7 +597,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (lhs: ListMonitor<ObjectType>, rhs: ListMonitor<ObjectType>) -> Bool {
|
||||
public static func == (lhs: ListMonitor<O>, rhs: ListMonitor<O>) -> Bool {
|
||||
|
||||
return lhs.fetchedResultsController === rhs.fetchedResultsController
|
||||
}
|
||||
@@ -607,7 +607,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
return lhs.fetchedResultsController === rhs.fetchedResultsController
|
||||
}
|
||||
|
||||
public static func ~= (lhs: ListMonitor<ObjectType>, rhs: ListMonitor<ObjectType>) -> Bool {
|
||||
public static func ~= (lhs: ListMonitor<O>, rhs: ListMonitor<O>) -> Bool {
|
||||
|
||||
return lhs.fetchedResultsController === rhs.fetchedResultsController
|
||||
}
|
||||
@@ -628,7 +628,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal convenience init(dataStack: DataStack, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
||||
internal convenience init(dataStack: DataStack, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
||||
|
||||
self.init(
|
||||
context: dataStack.mainContext,
|
||||
@@ -640,7 +640,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
)
|
||||
}
|
||||
|
||||
internal convenience init(dataStack: DataStack, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: @escaping (ListMonitor<ObjectType>) -> Void) {
|
||||
internal convenience init(dataStack: DataStack, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: @escaping (ListMonitor<O>) -> Void) {
|
||||
|
||||
self.init(
|
||||
context: dataStack.mainContext,
|
||||
@@ -652,7 +652,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
)
|
||||
}
|
||||
|
||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
||||
|
||||
self.init(
|
||||
context: unsafeTransaction.context,
|
||||
@@ -664,7 +664,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
)
|
||||
}
|
||||
|
||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: @escaping (ListMonitor<ObjectType>) -> Void) {
|
||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: @escaping (ListMonitor<O>) -> Void) {
|
||||
|
||||
self.init(
|
||||
context: unsafeTransaction.context,
|
||||
@@ -676,7 +676,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
)
|
||||
}
|
||||
|
||||
internal func registerChangeNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor<ObjectType>) -> Void) {
|
||||
internal func registerChangeNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor<O>) -> Void) {
|
||||
|
||||
Internals.setAssociatedRetainedObject(
|
||||
Internals.NotificationObserver(
|
||||
@@ -696,7 +696,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
)
|
||||
}
|
||||
|
||||
internal func registerObjectNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor<ObjectType>, _ object: ObjectType, _ indexPath: IndexPath?, _ newIndexPath: IndexPath?) -> Void) {
|
||||
internal func registerObjectNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor<O>, _ object: O, _ indexPath: IndexPath?, _ newIndexPath: IndexPath?) -> Void) {
|
||||
|
||||
Internals.setAssociatedRetainedObject(
|
||||
Internals.NotificationObserver(
|
||||
@@ -712,7 +712,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
}
|
||||
callback(
|
||||
self,
|
||||
ObjectType.cs_fromRaw(object: rawObject),
|
||||
O.cs_fromRaw(object: rawObject),
|
||||
userInfo[String(describing: IndexPath.self)] as? IndexPath,
|
||||
userInfo["\(String(describing: IndexPath.self)).New"] as? IndexPath
|
||||
)
|
||||
@@ -723,7 +723,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
)
|
||||
}
|
||||
|
||||
internal func registerSectionNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor<ObjectType>, _ sectionInfo: NSFetchedResultsSectionInfo, _ sectionIndex: Int) -> Void) {
|
||||
internal func registerSectionNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor<O>, _ sectionInfo: NSFetchedResultsSectionInfo, _ sectionIndex: Int) -> Void) {
|
||||
|
||||
Internals.setAssociatedRetainedObject(
|
||||
Internals.NotificationObserver(
|
||||
@@ -746,7 +746,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
)
|
||||
}
|
||||
|
||||
internal func registerObserver<U: AnyObject>(_ observer: U, willChange: @escaping (_ observer: U, _ monitor: ListMonitor<ObjectType>) -> Void, didChange: @escaping (_ observer: U, _ monitor: ListMonitor<ObjectType>) -> Void, willRefetch: @escaping (_ observer: U, _ monitor: ListMonitor<ObjectType>) -> Void, didRefetch: @escaping (_ observer: U, _ monitor: ListMonitor<ObjectType>) -> Void) {
|
||||
internal func registerObserver<U: AnyObject>(_ observer: U, willChange: @escaping (_ observer: U, _ monitor: ListMonitor<O>) -> Void, didChange: @escaping (_ observer: U, _ monitor: ListMonitor<O>) -> Void, willRefetch: @escaping (_ observer: U, _ monitor: ListMonitor<O>) -> Void, didRefetch: @escaping (_ observer: U, _ monitor: ListMonitor<O>) -> Void) {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -806,7 +806,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
)
|
||||
}
|
||||
|
||||
internal func registerObserver<U: AnyObject>(_ observer: U, didInsertObject: @escaping (_ observer: U, _ monitor: ListMonitor<ObjectType>, _ object: ObjectType, _ toIndexPath: IndexPath) -> Void, didDeleteObject: @escaping (_ observer: U, _ monitor: ListMonitor<ObjectType>, _ object: ObjectType, _ fromIndexPath: IndexPath) -> Void, didUpdateObject: @escaping (_ observer: U, _ monitor: ListMonitor<ObjectType>, _ object: ObjectType, _ atIndexPath: IndexPath) -> Void, didMoveObject: @escaping (_ observer: U, _ monitor: ListMonitor<ObjectType>, _ object: ObjectType, _ fromIndexPath: IndexPath, _ toIndexPath: IndexPath) -> Void) {
|
||||
internal func registerObserver<U: AnyObject>(_ observer: U, didInsertObject: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ object: O, _ toIndexPath: IndexPath) -> Void, didDeleteObject: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ object: O, _ fromIndexPath: IndexPath) -> Void, didUpdateObject: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ object: O, _ atIndexPath: IndexPath) -> Void, didMoveObject: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ object: O, _ fromIndexPath: IndexPath, _ toIndexPath: IndexPath) -> Void) {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -867,7 +867,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
)
|
||||
}
|
||||
|
||||
internal func registerObserver<U: AnyObject>(_ observer: U, didInsertSection: @escaping (_ observer: U, _ monitor: ListMonitor<ObjectType>, _ sectionInfo: NSFetchedResultsSectionInfo, _ toIndex: Int) -> Void, didDeleteSection: @escaping (_ observer: U, _ monitor: ListMonitor<ObjectType>, _ sectionInfo: NSFetchedResultsSectionInfo, _ fromIndex: Int) -> Void) {
|
||||
internal func registerObserver<U: AnyObject>(_ observer: U, didInsertSection: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ sectionInfo: NSFetchedResultsSectionInfo, _ toIndex: Int) -> Void, didDeleteSection: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ sectionInfo: NSFetchedResultsSectionInfo, _ fromIndex: Int) -> Void) {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -948,7 +948,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
return
|
||||
}
|
||||
|
||||
let (newFetchedResultsController, newFetchedResultsControllerDelegate) = ListMonitor.recreateFetchedResultsController(
|
||||
let (newFetchedResultsController, newFetchedResultsControllerDelegate) = Self.recreateFetchedResultsController(
|
||||
context: self.fetchedResultsController.managedObjectContext,
|
||||
from: self.from,
|
||||
sectionBy: self.sectionBy,
|
||||
@@ -1052,7 +1052,7 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
private static func recreateFetchedResultsController(context: NSManagedObjectContext, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) -> (controller: Internals.CoreStoreFetchedResultsController, delegate: Internals.FetchedResultsControllerDelegate) {
|
||||
private static func recreateFetchedResultsController(context: NSManagedObjectContext, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) -> (controller: Internals.CoreStoreFetchedResultsController, delegate: Internals.FetchedResultsControllerDelegate) {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||
fetchRequest.fetchLimit = 0
|
||||
@@ -1075,15 +1075,15 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
return (fetchedResultsController, fetchedResultsControllerDelegate)
|
||||
}
|
||||
|
||||
private let from: From<ObjectType>
|
||||
private let sectionBy: SectionBy<ObjectType>?
|
||||
private let from: From<O>
|
||||
private let sectionBy: SectionBy<O>?
|
||||
|
||||
private init(context: NSManagedObjectContext, transactionQueue: DispatchQueue, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: ((ListMonitor<ObjectType>) -> Void)?) {
|
||||
private init(context: NSManagedObjectContext, transactionQueue: DispatchQueue, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: ((ListMonitor<O>) -> Void)?) {
|
||||
|
||||
self.isSectioned = (sectionBy != nil)
|
||||
self.from = from
|
||||
self.sectionBy = sectionBy
|
||||
(self.fetchedResultsController, self.fetchedResultsControllerDelegate) = ListMonitor.recreateFetchedResultsController(
|
||||
(self.fetchedResultsController, self.fetchedResultsControllerDelegate) = Self.recreateFetchedResultsController(
|
||||
context: context,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
@@ -1173,77 +1173,26 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "hasObjects(in:)")
|
||||
public func hasObjectsInSection(_ section: Int) -> Bool {
|
||||
|
||||
return self.hasObjects(in: section)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "numberOfObjects(in:)")
|
||||
public func numberOfObjectsInSection(_ section: Int) -> Int {
|
||||
|
||||
return self.numberOfObjects(in: section)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "numberOfObjects(safelyIn:)")
|
||||
public func numberOfObjectsInSection(safeSectionIndex section: Int) -> Int? {
|
||||
|
||||
return self.numberOfObjects(safelyIn: section)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "sectionInfo(at:)")
|
||||
public func sectionInfoAtIndex(_ section: Int) -> NSFetchedResultsSectionInfo {
|
||||
|
||||
return self.sectionInfo(at: section)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "sectionInfo(safelyAt:)")
|
||||
public func sectionInfoAtIndex(safeSectionIndex section: Int) -> NSFetchedResultsSectionInfo? {
|
||||
|
||||
return self.sectionInfo(safelyAt: section)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "targetSection(forSectionIndexTitle:at:)")
|
||||
public func targetSectionForSectionIndex(title: String, index: Int) -> Int {
|
||||
|
||||
return self.targetSection(forSectionIndexTitle: title, at: index)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "index(of:)")
|
||||
public func indexOf(_ object: ObjectType) -> Int? {
|
||||
|
||||
return self.index(of: object)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "indexPath(of:)")
|
||||
public func indexPathOf(_ object: ObjectType) -> IndexPath? {
|
||||
|
||||
return self.indexPath(of: object)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ListMonitor where ListMonitor.ObjectType: NSManagedObject
|
||||
// MARK: - ListMonitor where O: NSManagedObject
|
||||
|
||||
@available(macOS 10.12, *)
|
||||
extension ListMonitor where ListMonitor.ObjectType: NSManagedObject {
|
||||
extension ListMonitor where O: NSManagedObject {
|
||||
|
||||
/**
|
||||
Returns all objects in all sections
|
||||
|
||||
- returns: all objects in all sections
|
||||
*/
|
||||
public func objectsInAllSections() -> [ObjectType] {
|
||||
public func objectsInAllSections() -> [O] {
|
||||
|
||||
Internals.assert(
|
||||
!self.isPendingRefetch || Thread.isMainThread,
|
||||
"Attempted to access a \(Internals.typeName(self)) outside the main thread while a refetch is in progress."
|
||||
)
|
||||
return (self.fetchedResultsController.dynamicCast() as NSFetchedResultsController<ObjectType>).fetchedObjects ?? []
|
||||
return (self.fetchedResultsController.dynamicCast() as NSFetchedResultsController<O>).fetchedObjects ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1252,9 +1201,9 @@ extension ListMonitor where ListMonitor.ObjectType: NSManagedObject {
|
||||
- parameter section: the section index. Using an index outside the valid range will raise an exception.
|
||||
- returns: all objects in the specified section
|
||||
*/
|
||||
public func objects(in section: Int) -> [ObjectType] {
|
||||
public func objects(in section: Int) -> [O] {
|
||||
|
||||
return (self.sectionInfo(at: section).objects as! [ObjectType]?) ?? []
|
||||
return (self.sectionInfo(at: section).objects as! [O]?) ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1263,46 +1212,46 @@ extension ListMonitor where ListMonitor.ObjectType: NSManagedObject {
|
||||
- parameter section: the section index. Using an index outside the valid range will return `nil`.
|
||||
- returns: all objects in the specified section
|
||||
*/
|
||||
public func objects(safelyIn section: Int) -> [ObjectType]? {
|
||||
public func objects(safelyIn section: Int) -> [O]? {
|
||||
|
||||
return self.sectionInfo(safelyAt: section)?.objects as! [ObjectType]?
|
||||
return self.sectionInfo(safelyAt: section)?.objects as! [O]?
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "objects(in:)")
|
||||
public func objectsInSection(_ section: Int) -> [ObjectType] {
|
||||
public func objectsInSection(_ section: Int) -> [O] {
|
||||
|
||||
return self.objects(in: section)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "objects(safelyIn:)")
|
||||
public func objectsInSection(safeSectionIndex section: Int) -> [ObjectType]? {
|
||||
public func objectsInSection(safeSectionIndex section: Int) -> [O]? {
|
||||
|
||||
return self.objects(safelyIn: section)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ListMonitor where ListMonitor.ObjectType: CoreStoreObject
|
||||
// MARK: - ListMonitor where O: CoreStoreObject
|
||||
|
||||
@available(macOS 10.12, *)
|
||||
extension ListMonitor where ListMonitor.ObjectType: CoreStoreObject {
|
||||
extension ListMonitor where O: CoreStoreObject {
|
||||
|
||||
/**
|
||||
Returns all objects in all sections
|
||||
|
||||
- returns: all objects in all sections
|
||||
*/
|
||||
public func objectsInAllSections() -> [ObjectType] {
|
||||
public func objectsInAllSections() -> [O] {
|
||||
|
||||
Internals.assert(
|
||||
!self.isPendingRefetch || Thread.isMainThread,
|
||||
"Attempted to access a \(Internals.typeName(self)) outside the main thread while a refetch is in progress."
|
||||
)
|
||||
return (self.fetchedResultsController.fetchedObjects ?? [])
|
||||
.map(ObjectType.cs_fromRaw)
|
||||
.map(O.cs_fromRaw)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1311,10 +1260,10 @@ extension ListMonitor where ListMonitor.ObjectType: CoreStoreObject {
|
||||
- parameter section: the section index. Using an index outside the valid range will raise an exception.
|
||||
- returns: all objects in the specified section
|
||||
*/
|
||||
public func objects(in section: Int) -> [ObjectType] {
|
||||
public func objects(in section: Int) -> [O] {
|
||||
|
||||
return (self.sectionInfo(at: section).objects ?? [])
|
||||
.map({ ObjectType.cs_fromRaw(object: $0 as! NSManagedObject) })
|
||||
.map({ O.cs_fromRaw(object: $0 as! NSManagedObject) })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1323,26 +1272,17 @@ extension ListMonitor where ListMonitor.ObjectType: CoreStoreObject {
|
||||
- parameter section: the section index. Using an index outside the valid range will return `nil`.
|
||||
- returns: all objects in the specified section
|
||||
*/
|
||||
public func objects(safelyIn section: Int) -> [ObjectType]? {
|
||||
public func objects(safelyIn section: Int) -> [O]? {
|
||||
|
||||
return (self.sectionInfo(safelyAt: section)?.objects)?
|
||||
.map({ ObjectType.cs_fromRaw(object: $0 as! NSManagedObject) })
|
||||
.map({ O.cs_fromRaw(object: $0 as! NSManagedObject) })
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
// MARK: - Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "objects(in:)")
|
||||
public func objectsInSection(_ section: Int) -> [ObjectType] {
|
||||
|
||||
return self.objects(in: section)
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "objects(safelyIn:)")
|
||||
public func objectsInSection(safeSectionIndex section: Int) -> [ObjectType]? {
|
||||
|
||||
return self.objects(safelyIn: section)
|
||||
}
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
|
||||
@@ -1453,7 +1393,7 @@ extension ListMonitor: FetchedResultsControllerHandler {
|
||||
)
|
||||
}
|
||||
|
||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, sectionIndexTitleForSectionName sectionName: String?) -> String? {
|
||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, sectionIndexTitleForSectionName sectionName: String?) -> String? {
|
||||
|
||||
return self.sectionIndexTransformer(sectionName)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import CoreData
|
||||
/**
|
||||
Implement the `ListObserver` protocol to observe changes to a list of `NSManagedObject`s. `ListObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method:
|
||||
```
|
||||
let monitor = CoreStore.monitorList(
|
||||
let monitor = dataStack.monitorList(
|
||||
From<Person>(),
|
||||
OrderBy(.ascending("lastName"))
|
||||
)
|
||||
@@ -96,7 +96,7 @@ extension ListObserver {
|
||||
/**
|
||||
Implement the `ListObjectObserver` protocol to observe detailed changes to a list's object. `ListObjectObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method:
|
||||
```
|
||||
let monitor = CoreStore.monitorList(
|
||||
let monitor = dataStack.monitorList(
|
||||
From<MyPersonEntity>(),
|
||||
OrderBy(.ascending("lastName"))
|
||||
)
|
||||
@@ -169,7 +169,7 @@ extension ListObjectObserver {
|
||||
/**
|
||||
Implement the `ListSectionObserver` protocol to observe changes to a list's section info. `ListSectionObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method:
|
||||
```
|
||||
let monitor = CoreStore.monitorSectionedList(
|
||||
let monitor = dataStack.monitorSectionedList(
|
||||
From<MyPersonEntity>(),
|
||||
SectionBy("age") { "Age \($0)" },
|
||||
OrderBy(.ascending("lastName"))
|
||||
|
||||
@@ -0,0 +1,465 @@
|
||||
//
|
||||
// ListPublisher.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
#endif
|
||||
|
||||
#if canImport(SwiftUI)
|
||||
import SwiftUI
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - ListPublisher
|
||||
|
||||
/**
|
||||
`ListPublisher` tracks a diffable list of `DynamicObject` instances. Unlike `ListMonitor`s, `ListPublisher` are more lightweight and access objects lazily. Objects that need to be notified of `ListPublisher` changes may register themselves to its `addObserver(_:_:)` method:
|
||||
```
|
||||
let listPublisher = CoreStoreDefaults.dataStack.listPublisher(
|
||||
From<Person>()
|
||||
.where(\.title == "Engineer")
|
||||
.orderBy(.ascending(\.lastName))
|
||||
)
|
||||
listPublisher.addObserver(self) { (listPublisher) in
|
||||
// Handle changes
|
||||
}
|
||||
```
|
||||
The `ListPublisher` instance needs to be held on (retained) for as long as the list needs to be observed.
|
||||
Observers registered via `addObserver(_:_:)` are not retained. `ListPublisher` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles.
|
||||
|
||||
`ListPublisher`s may optionally be created with sections:
|
||||
```
|
||||
let listPublisher = CoreStoreDefaults.dataStack.listPublisher(
|
||||
From<Person>()
|
||||
.sectionBy(\.age") { "Age \($0)" }
|
||||
.where(\.title == "Engineer")
|
||||
.orderBy(.ascending(\.lastName))
|
||||
)
|
||||
```
|
||||
All access to the `ListPublisher` items should be done via its `snapshot` value, which is a `struct` of type `ListSnapshot<O>`. `ListSnapshot`s are also designed to work well with `DiffableDataSource.TableViewAdapter`s and `DiffableDataSource.CollectionViewAdapter`s. For detailed examples, refer to the documentation for `DiffableDataSource.TableViewAdapter` and `DiffableDataSource.CollectionViewAdapter`.
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public final class ListPublisher<O: DynamicObject>: Hashable {
|
||||
|
||||
// MARK: Public (Accessors)
|
||||
|
||||
/**
|
||||
The `DynamicObject` type associated with this list
|
||||
*/
|
||||
public typealias ObjectType = O
|
||||
|
||||
/**
|
||||
The type for the section IDs
|
||||
*/
|
||||
public typealias SectionID = ListSnapshot<O>.SectionID
|
||||
|
||||
/**
|
||||
The type for the item IDs
|
||||
*/
|
||||
public typealias ItemID = ListSnapshot<O>.ItemID
|
||||
|
||||
/**
|
||||
A snapshot of the latest state of this list
|
||||
*/
|
||||
public fileprivate(set) var snapshot: ListSnapshot<O> = .init() {
|
||||
|
||||
willSet {
|
||||
|
||||
self.willChange()
|
||||
}
|
||||
didSet {
|
||||
|
||||
self.didChange()
|
||||
self.notifyObservers()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Public (Observers)
|
||||
|
||||
/**
|
||||
Registers an object as an observer to be notified when changes to the `ListPublisher`'s snapshot occur.
|
||||
|
||||
To prevent retain-cycles, `ListPublisher` only keeps `weak` references to its observers.
|
||||
|
||||
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
|
||||
|
||||
Calling `addObserver(_:_:)` multiple times on the same observer is safe.
|
||||
|
||||
- parameter observer: an object to become owner of the specified `callback`
|
||||
- parameter callback: the closure to execute when changes occur
|
||||
*/
|
||||
public func addObserver<T: AnyObject>(_ observer: T, _ callback: @escaping (ListPublisher<O>) -> Void) {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
"Attempted to add an observer of type \(Internals.typeName(observer)) outside the main thread."
|
||||
)
|
||||
self.observers.setObject(
|
||||
Internals.Closure(callback),
|
||||
forKey: observer
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Unregisters an object from receiving notifications for changes to the `ListPublisher`'s snapshot.
|
||||
|
||||
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
|
||||
|
||||
- parameter observer: the object whose notifications will be unregistered
|
||||
*/
|
||||
public func removeObserver<T: AnyObject>(_ observer: T) {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
"Attempted to remove an observer of type \(Internals.typeName(observer)) outside the main thread."
|
||||
)
|
||||
self.observers.removeObject(forKey: observer)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Public (Refetching)
|
||||
|
||||
/**
|
||||
Asks the `ListPublisher` to refetch its objects using the specified `FetchChainableBuilderType`. Unlike `ListMonitor`s, a `ListPublisher`'s `refetch(...)` executes immediately.
|
||||
```
|
||||
try listPublisher.refetch(
|
||||
From<MyPersonEntity>()
|
||||
.where(\.age > 18)
|
||||
.orderBy(.ascending(\.age))
|
||||
)
|
||||
```
|
||||
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
|
||||
*/
|
||||
public func refetch<B: FetchChainableBuilderType>(_ clauseChain: B) throws where B.ObjectType == O {
|
||||
|
||||
try self.refetch(
|
||||
from: clauseChain.from,
|
||||
sectionBy: nil,
|
||||
applyFetchClauses: { (fetchRequest) in
|
||||
|
||||
clauseChain.fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Asks the `ListPublisher` to refetch its objects using the specified `SectionMonitorBuilderType`. Unlike `ListMonitor`s, a `ListPublisher`'s `refetch(...)` executes immediately.
|
||||
```
|
||||
try listPublisher.refetch(
|
||||
From<MyPersonEntity>()
|
||||
.sectionBy(\.age, { "\($0!) years old" })
|
||||
.where(\.age > 18)
|
||||
.orderBy(.ascending(\.age))
|
||||
)
|
||||
```
|
||||
- parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses
|
||||
*/
|
||||
public func refetch<B: SectionMonitorBuilderType>(_ clauseChain: B) throws where B.ObjectType == O {
|
||||
|
||||
try self.refetch(
|
||||
from: clauseChain.from,
|
||||
sectionBy: clauseChain.sectionBy,
|
||||
applyFetchClauses: { (fetchRequest) in
|
||||
|
||||
clauseChain.fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Public (3rd Party Utilities)
|
||||
|
||||
/**
|
||||
Allow external libraries to store custom data in the `ListPublisher`. App code should rarely have a need for this.
|
||||
```
|
||||
enum Static {
|
||||
static var myDataKey: Void?
|
||||
}
|
||||
monitor.userInfo[&Static.myDataKey] = myObject
|
||||
```
|
||||
- Important: Do not use this method to store thread-sensitive data.
|
||||
*/
|
||||
public let userInfo = UserInfo()
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (_ lhs: ListPublisher, _ rhs: ListPublisher) -> Bool {
|
||||
|
||||
return lhs === rhs
|
||||
}
|
||||
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
|
||||
hasher.combine(ObjectIdentifier(self))
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal convenience init(dataStack: DataStack, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
||||
|
||||
self.init(
|
||||
context: dataStack.mainContext,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
applyFetchClauses: applyFetchClauses,
|
||||
createAsynchronously: nil
|
||||
)
|
||||
}
|
||||
|
||||
internal convenience init(dataStack: DataStack, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: @escaping (ListPublisher<ObjectType>) -> Void) {
|
||||
|
||||
self.init(
|
||||
context: dataStack.mainContext,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
applyFetchClauses: applyFetchClauses,
|
||||
createAsynchronously: createAsynchronously
|
||||
)
|
||||
}
|
||||
|
||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
||||
|
||||
self.init(
|
||||
context: unsafeTransaction.context,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
applyFetchClauses: applyFetchClauses,
|
||||
createAsynchronously: nil
|
||||
)
|
||||
}
|
||||
|
||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: @escaping (ListPublisher<ObjectType>) -> Void) {
|
||||
|
||||
self.init(
|
||||
context: unsafeTransaction.context,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
applyFetchClauses: applyFetchClauses,
|
||||
createAsynchronously: createAsynchronously
|
||||
)
|
||||
}
|
||||
|
||||
internal func refetch(from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) throws {
|
||||
|
||||
let (newFetchedResultsController, newFetchedResultsControllerDelegate) = Self.recreateFetchedResultsController(
|
||||
context: self.fetchedResultsController.managedObjectContext,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
applyFetchClauses: applyFetchClauses
|
||||
)
|
||||
self.query = (
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
sectionIndexTransformer: sectionBy?.sectionIndexTransformer ?? { $0 },
|
||||
applyFetchClauses: applyFetchClauses
|
||||
)
|
||||
(self.fetchedResultsController, self.fetchedResultsControllerDelegate) = (newFetchedResultsController, newFetchedResultsControllerDelegate)
|
||||
|
||||
newFetchedResultsControllerDelegate.handler = self
|
||||
try newFetchedResultsController.performFetchFromSpecifiedStores()
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
self.fetchedResultsControllerDelegate.fetchedResultsController = nil
|
||||
self.observers.removeAllObjects()
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate let rawObjectWillChange: Any?
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var query: (
|
||||
from: From<O>,
|
||||
sectionBy: SectionBy<O>?,
|
||||
sectionIndexTransformer: (_ sectionName: KeyPathString?) -> String?,
|
||||
applyFetchClauses: (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void
|
||||
)
|
||||
|
||||
private var fetchedResultsController: Internals.CoreStoreFetchedResultsController
|
||||
private var fetchedResultsControllerDelegate: Internals.FetchedDiffableDataSourceSnapshotDelegate
|
||||
private var observerForWillChangePersistentStore: Internals.NotificationObserver!
|
||||
private var observerForDidChangePersistentStore: Internals.NotificationObserver!
|
||||
|
||||
private lazy var observers: NSMapTable<AnyObject, Internals.Closure<ListPublisher<O>, Void>> = .weakToStrongObjects()
|
||||
|
||||
private lazy var context: NSManagedObjectContext = self.fetchedResultsController.managedObjectContext
|
||||
|
||||
private static func recreateFetchedResultsController(context: NSManagedObjectContext, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) -> (controller: Internals.CoreStoreFetchedResultsController, delegate: Internals.FetchedDiffableDataSourceSnapshotDelegate) {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||
fetchRequest.fetchLimit = 0
|
||||
fetchRequest.resultType = .managedObjectResultType
|
||||
fetchRequest.includesPendingChanges = false
|
||||
fetchRequest.shouldRefreshRefetchedObjects = true
|
||||
|
||||
let fetchedResultsController = Internals.CoreStoreFetchedResultsController(
|
||||
context: context,
|
||||
fetchRequest: fetchRequest,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
applyFetchClauses: applyFetchClauses
|
||||
)
|
||||
|
||||
let fetchedResultsControllerDelegate = Internals.FetchedDiffableDataSourceSnapshotDelegate()
|
||||
fetchedResultsControllerDelegate.fetchedResultsController = fetchedResultsController
|
||||
|
||||
return (fetchedResultsController, fetchedResultsControllerDelegate)
|
||||
}
|
||||
|
||||
private init(context: NSManagedObjectContext, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: ((ListPublisher<ObjectType>) -> Void)?) {
|
||||
|
||||
self.query = (
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
sectionIndexTransformer: sectionBy?.sectionIndexTransformer ?? { $0 },
|
||||
applyFetchClauses: applyFetchClauses
|
||||
)
|
||||
(self.fetchedResultsController, self.fetchedResultsControllerDelegate) = Self.recreateFetchedResultsController(
|
||||
context: context,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
applyFetchClauses: applyFetchClauses
|
||||
)
|
||||
|
||||
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||
|
||||
#if canImport(Combine)
|
||||
self.rawObjectWillChange = ObservableObjectPublisher()
|
||||
|
||||
#else
|
||||
self.rawObjectWillChange = nil
|
||||
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
||||
self.rawObjectWillChange = nil
|
||||
}
|
||||
|
||||
self.fetchedResultsControllerDelegate.handler = self
|
||||
|
||||
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
}
|
||||
|
||||
private func notifyObservers() {
|
||||
|
||||
guard let enumerator = self.observers.objectEnumerator() else {
|
||||
|
||||
return
|
||||
}
|
||||
for closure in enumerator {
|
||||
|
||||
(closure as! Internals.Closure<ListPublisher<O>, Void>).invoke(with: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ListPublisher: FetchedDiffableDataSourceSnapshotHandler
|
||||
|
||||
extension ListPublisher: FetchedDiffableDataSourceSnapshotHandler {
|
||||
|
||||
// MARK: FetchedDiffableDataSourceSnapshotHandler
|
||||
|
||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: Internals.DiffableDataSourceSnapshot) {
|
||||
|
||||
self.snapshot = .init(
|
||||
diffableSnapshot: snapshot,
|
||||
context: controller.managedObjectContext
|
||||
)
|
||||
}
|
||||
|
||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, sectionIndexTitleForSectionName sectionName: String?) -> String? {
|
||||
|
||||
return self.query.sectionIndexTransformer(sectionName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
extension ListPublisher: ObservableObject {}
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: - ListPublisher
|
||||
|
||||
extension ListPublisher {
|
||||
|
||||
// MARK: ObservableObject
|
||||
|
||||
#if canImport(Combine)
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
public var objectWillChange: ObservableObjectPublisher {
|
||||
|
||||
return self.rawObjectWillChange! as! ObservableObjectPublisher
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
fileprivate func willChange() {
|
||||
|
||||
guard #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) else {
|
||||
|
||||
return
|
||||
}
|
||||
#if canImport(Combine)
|
||||
|
||||
#if canImport(SwiftUI)
|
||||
withAnimation {
|
||||
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
self.objectWillChange.send()
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
fileprivate func didChange() {
|
||||
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,660 @@
|
||||
//
|
||||
// ListSnapshot.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
#if canImport(UIKit)
|
||||
import UIKit
|
||||
|
||||
#elseif canImport(AppKit)
|
||||
import AppKit
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - ListSnapshot
|
||||
|
||||
/**
|
||||
A `ListSnapshot` holds a stable list of `DynamicObject` identifiers. This is typically created by a `ListPublisher` and are designed to work well with `DiffableDataSource.TableViewAdapter`s and `DiffableDataSource.CollectionViewAdapter`s. For detailed examples, see the documentation on `DiffableDataSource.TableViewAdapter` and `DiffableDataSource.CollectionViewAdapter`.
|
||||
|
||||
While the `ListSnapshot` stores only object identifiers, all accessors to its items return `ObjectPublisher`s, which are lazily created. For more details, see the documentation on `ListObject`.
|
||||
|
||||
Since `ListSnapshot` is a value type, you can freely modify its items.
|
||||
*/
|
||||
public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
|
||||
// MARK: Public (Accessors)
|
||||
|
||||
/**
|
||||
The `DynamicObject` type associated with this list
|
||||
*/
|
||||
public typealias ObjectType = O
|
||||
|
||||
/**
|
||||
The type for the section IDs
|
||||
*/
|
||||
public typealias SectionID = String
|
||||
|
||||
/**
|
||||
The type for the item IDs
|
||||
*/
|
||||
public typealias ItemID = O.ObjectID
|
||||
|
||||
/**
|
||||
Returns the object at the given index.
|
||||
|
||||
- parameter index: the index of the object. Using an index above the valid range will raise an exception.
|
||||
- returns: the `ObjectPublisher<O>` interfacing the object at the specified index
|
||||
*/
|
||||
public subscript(index: Index) -> ObjectPublisher<O> {
|
||||
|
||||
let context = self.context!
|
||||
let itemID = self.diffableSnapshot.itemIdentifiers[index]
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the object at the given index, or `nil` if out of bounds.
|
||||
|
||||
- parameter index: the index for the object. Using an index above the valid range will return `nil`.
|
||||
- returns: the `ObjectPublisher<O>` interfacing the object at the specified index, or `nil` if out of bounds
|
||||
*/
|
||||
public subscript(safeIndex index: Index) -> ObjectPublisher<O>? {
|
||||
|
||||
guard let context = self.context else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers
|
||||
guard itemIDs.indices.contains(index) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let itemID = itemIDs[index]
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the object at the given `sectionIndex` and `itemIndex`.
|
||||
|
||||
- parameter sectionIndex: the section index for the object. Using a `sectionIndex` with an invalid range will raise an exception.
|
||||
- parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will raise an exception.
|
||||
- returns: the `ObjectPublisher<O>` interfacing the object at the specified section and item index
|
||||
*/
|
||||
public subscript(sectionIndex: Int, itemIndex: Int) -> ObjectPublisher<O> {
|
||||
|
||||
let context = self.context!
|
||||
let snapshot = self.diffableSnapshot
|
||||
let sectionID = snapshot.sectionIdentifiers[sectionIndex]
|
||||
let itemID = snapshot.itemIdentifiers(inSection: sectionID)[itemIndex]
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the object at the given section and item index, or `nil` if out of bounds.
|
||||
|
||||
- parameter sectionIndex: the section index for the object. Using a `sectionIndex` with an invalid range will return `nil`.
|
||||
- parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will return `nil`.
|
||||
- returns: the `ObjectPublisher<O>` interfacing the object at the specified section and item index, or `nil` if out of bounds
|
||||
*/
|
||||
public subscript(safeSectionIndex sectionIndex: Int, safeItemIndex itemIndex: Int) -> ObjectPublisher<O>? {
|
||||
|
||||
guard let context = self.context else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let snapshot = self.diffableSnapshot
|
||||
let sectionIDs = snapshot.sectionIdentifiers
|
||||
guard sectionIDs.indices.contains(sectionIndex) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let sectionID = sectionIDs[sectionIndex]
|
||||
let itemIDs = snapshot.itemIdentifiers(inSection: sectionID)
|
||||
guard itemIDs.indices.contains(itemIndex) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let itemID = itemIDs[itemIndex]
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the object at the given `IndexPath`.
|
||||
|
||||
- parameter indexPath: the `IndexPath` for the object. Using an `indexPath` with an invalid range will raise an exception.
|
||||
- returns: the `ObjectPublisher<O>` interfacing the object at the specified index path
|
||||
*/
|
||||
public subscript(indexPath: IndexPath) -> ObjectPublisher<O> {
|
||||
|
||||
return self[indexPath[0], indexPath[1]]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the object at the given `IndexPath`, or `nil` if out of bounds.
|
||||
|
||||
- parameter indexPath: the `IndexPath` for the object. Using an `indexPath` with an invalid range will return `nil`.
|
||||
- returns: the `ObjectPublisher<O>` interfacing the object at the specified index path, or `nil` if out of bounds
|
||||
*/
|
||||
public subscript(safeIndexPath indexPath: IndexPath) -> ObjectPublisher<O>? {
|
||||
|
||||
return self[
|
||||
safeSectionIndex: indexPath[0],
|
||||
safeItemIndex: indexPath[1]
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
Checks if the `ListSnapshot` has at least one section
|
||||
|
||||
- returns: `true` if at least one section exists, `false` otherwise
|
||||
*/
|
||||
public func hasSections() -> Bool {
|
||||
|
||||
return self.diffableSnapshot.numberOfSections > 0
|
||||
}
|
||||
|
||||
/**
|
||||
Checks if the `ListSnapshot` has at least one object in any section.
|
||||
|
||||
- returns: `true` if at least one object in any section exists, `false` otherwise
|
||||
*/
|
||||
public func hasItems() -> Bool {
|
||||
|
||||
return self.diffableSnapshot.numberOfItems > 0
|
||||
}
|
||||
|
||||
/**
|
||||
Checks if the `ListSnapshot` has at least one object in the specified section.
|
||||
|
||||
- parameter sectionIndex: the section index. Using an index outside the valid range will return `false`.
|
||||
- returns: `true` if at least one object in the specified section exists, `false` otherwise
|
||||
*/
|
||||
public func hasItems(inSectionIndex sectionIndex: Int) -> Bool {
|
||||
|
||||
let snapshot = self.diffableSnapshot
|
||||
let sectionIDs = snapshot.sectionIdentifiers
|
||||
guard sectionIDs.indices.contains(sectionIndex) else {
|
||||
|
||||
return false
|
||||
}
|
||||
let sectionID = sectionIDs[sectionIndex]
|
||||
return snapshot.numberOfItems(inSection: sectionID) > 0
|
||||
}
|
||||
|
||||
/**
|
||||
Checks if the `ListSnapshot` has at least one object the specified section.
|
||||
|
||||
- parameter sectionID: the section identifier. Using an index outside the valid range will return `false`.
|
||||
- returns: `true` if at least one object in the specified section exists, `false` otherwise
|
||||
*/
|
||||
public func hasItems(inSectionWithID sectionID: SectionID) -> Bool {
|
||||
|
||||
let snapshot = self.diffableSnapshot
|
||||
guard snapshot.sectionIdentifiers.contains(sectionID) else {
|
||||
|
||||
return false
|
||||
}
|
||||
return snapshot.numberOfItems(inSection: sectionID) > 0
|
||||
}
|
||||
|
||||
/**
|
||||
Returns item identifiers for updated objects. This is mainly useful for Data Source adapters such as `UICollectionViewDiffableDataSource` or `UITableViewDiffableDataSource` which work on collection diffs when reloading. Since objects with same IDs resolve as "equal" in their old and new states, adapters may need extra heuristics to determine which row items need reloading. If your row items are all observing changes from each corresponding `ObjectPublisher`, or if you are using CoreStore's built-in `DiffableDataSource`s, there is no need to inspect this property.
|
||||
*/
|
||||
public var updatedItemIdentifiers: Set<NSManagedObjectID> {
|
||||
|
||||
return self.diffableSnapshot.updatedItemIdentifiers
|
||||
}
|
||||
|
||||
/**
|
||||
The number of items in all sections in the `ListSnapshot`
|
||||
*/
|
||||
public var numberOfItems: Int {
|
||||
|
||||
return self.diffableSnapshot.numberOfItems
|
||||
}
|
||||
|
||||
/**
|
||||
The number of sections in the `ListSnapshot`
|
||||
*/
|
||||
public var numberOfSections: Int {
|
||||
|
||||
return self.diffableSnapshot.numberOfSections
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the number of items for the specified `SectionID`.
|
||||
|
||||
- parameter sectionID: the `SectionID`. Specifying an invalid value will raise an exception.
|
||||
- returns: The number of items in the given `SectionID`
|
||||
*/
|
||||
public func numberOfItems(inSectionWithID sectionID: SectionID) -> Int {
|
||||
|
||||
return self.diffableSnapshot.numberOfItems(inSection: sectionID)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the number of items at the specified section index.
|
||||
|
||||
- parameter sectionIndex: the index of the section. Specifying an invalid value will raise an exception.
|
||||
- returns: The number of items in the given `SectionID`
|
||||
*/
|
||||
public func numberOfItems(inSectionIndex sectionIndex: Int) -> Int {
|
||||
|
||||
let snapshot = self.diffableSnapshot
|
||||
let sectionID = snapshot.sectionIdentifiers[sectionIndex]
|
||||
return snapshot.numberOfItems(inSection: sectionID)
|
||||
}
|
||||
|
||||
/**
|
||||
All section identifiers in the `ListSnapshot`
|
||||
*/
|
||||
public var sectionIDs: [SectionID] {
|
||||
|
||||
return self.diffableSnapshot.sectionIdentifiers
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `SectionID` that the specified `ItemID` belongs to, or `nil` if it is not in the list.
|
||||
|
||||
- parameter itemID: the `ItemID`
|
||||
- returns: the `SectionID` that the specified `ItemID` belongs to, or `nil` if it is not in the list
|
||||
*/
|
||||
public func sectionID(containingItemWithID itemID: ItemID) -> SectionID? {
|
||||
|
||||
return self.diffableSnapshot.sectionIdentifier(containingItem: itemID)
|
||||
}
|
||||
|
||||
/**
|
||||
All object identifiers in the `ListSnapshot`
|
||||
*/
|
||||
public var itemIDs: [ItemID] {
|
||||
|
||||
return self.diffableSnapshot.itemIdentifiers
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the item identifiers belonging to the specified `SectionID`.
|
||||
|
||||
- parameter sectionID: the `SectionID`. Specifying an invalid value will raise an exception.
|
||||
- returns: the `ItemID` array belonging to the given `SectionID`
|
||||
*/
|
||||
public func itemIDs(inSectionWithID sectionID: SectionID) -> [ItemID] {
|
||||
|
||||
return self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the item identifiers belonging to the specified `SectionID` and a `Sequence` of item indices.
|
||||
|
||||
- parameter sectionID: the `SectionID`. Specifying an invalid value will raise an exception.
|
||||
- parameter indices: the positions of the itemIDs to return. Specifying an invalid value will raise an exception.
|
||||
- returns: the `ItemID` array belonging to the given `SectionID` at the specified indices
|
||||
*/
|
||||
public func itemIDs<S: Sequence>(inSectionWithID sectionID: SectionID, atIndices indices: S) -> [ItemID] where S.Element == Int {
|
||||
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||
return indices.map({ itemIDs[$0] })
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the index of the specified `ItemID` in the whole list, or `nil` if it is not in the list.
|
||||
|
||||
- parameter itemID: the `ItemID`
|
||||
- returns: the index of the specified `ItemID`, or `nil` if it is not in the list
|
||||
*/
|
||||
public func indexOfItem(withID itemID: ItemID) -> Index? {
|
||||
|
||||
return self.diffableSnapshot.indexOfItem(itemID)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the index of the specified `SectionID`, or `nil` if it is not in the list.
|
||||
|
||||
- parameter sectionID: the `SectionID`
|
||||
- returns: the index of the specified `SectionID`, or `nil` if it is not in the list
|
||||
*/
|
||||
public func indexOfSection(withID sectionID: SectionID) -> Int? {
|
||||
|
||||
return self.diffableSnapshot.indexOfSection(sectionID)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an array of `ObjectPublisher`s for the items at the specified indices
|
||||
|
||||
- parameter indices: the positions of items. Specifying an invalid value will raise an exception.
|
||||
- returns: an array of `ObjectPublisher`s for the items at the specified indices
|
||||
*/
|
||||
public func items<S: Sequence>(atIndices indices: S) -> [ObjectPublisher<O>] where S.Element == Index {
|
||||
|
||||
let context = self.context!
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers
|
||||
return indices.map { position in
|
||||
|
||||
let itemID = itemIDs[position]
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an array of `ObjectPublisher`s for the items in the specified `SectionID`
|
||||
|
||||
- parameter sectionID: the `SectionID`. Specifying an invalid value will raise an exception.
|
||||
- returns: an array of `ObjectPublisher`s for the items in the specified `SectionID`
|
||||
*/
|
||||
public func items(inSectionWithID sectionID: SectionID) -> [ObjectPublisher<O>] {
|
||||
|
||||
let context = self.context!
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||
return itemIDs.map(context.objectPublisher(objectID:))
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an array of `ObjectPublisher`s for the items in the specified `SectionID` and indices
|
||||
|
||||
- parameter sectionID: the `SectionID`. Specifying an invalid value will raise an exception.
|
||||
- parameter itemIndices: the positions of items within the section. Specifying an invalid value will raise an exception.
|
||||
- returns: an array of `ObjectPublisher`s for the items in the specified `SectionID` and indices
|
||||
*/
|
||||
public func items<S: Sequence>(inSectionWithID sectionID: SectionID, atIndices itemIndices: S) -> [ObjectPublisher<O>] where S.Element == Int {
|
||||
|
||||
let context = self.context!
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||
return itemIndices.map { position in
|
||||
|
||||
let itemID = itemIDs[position]
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a lazy sequence of `ObjectPublisher`s for the items at the specified indices
|
||||
|
||||
- parameter indices: the positions of items. Specifying an invalid value will raise an exception.
|
||||
- returns: a lazy sequence of `ObjectPublisher`s for the items at the specified indices
|
||||
*/
|
||||
public func lazy<S: Sequence>(atIndices indices: S) -> LazyMapSequence<S, ObjectPublisher<O>> where S.Element == Index {
|
||||
|
||||
let context = self.context!
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers
|
||||
return indices.lazy.map { position in
|
||||
|
||||
let itemID = itemIDs[position]
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a lazy sequence of `ObjectPublisher`s for the items in the specified `SectionID`
|
||||
|
||||
- parameter sectionID: the `SectionID`. Specifying an invalid value will raise an exception.
|
||||
- returns: a lazy sequence of `ObjectPublisher`s for the items in the specified `SectionID`
|
||||
*/
|
||||
public func lazy(inSectionWithID sectionID: SectionID) -> LazyMapSequence<[NSManagedObjectID], ObjectPublisher<O>> {
|
||||
|
||||
let context = self.context!
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||
return itemIDs.lazy.map(context.objectPublisher(objectID:))
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a lazy sequence of `ObjectPublisher`s for the items in the specified `SectionID` and indices
|
||||
|
||||
- parameter sectionID: the `SectionID`. Specifying an invalid value will raise an exception.
|
||||
- parameter itemIndices: the positions of items within the section. Specifying an invalid value will raise an exception.
|
||||
- returns: a lazy sequence of `ObjectPublisher`s for the items in the specified `SectionID` and indices
|
||||
*/
|
||||
public func lazy<S: Sequence>(inSectionWithID sectionID: SectionID, atIndices itemIndices: S) -> LazyMapSequence<S, ObjectPublisher<O>> where S.Element == Int {
|
||||
|
||||
let context = self.context!
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||
return itemIndices.lazy.map { position in
|
||||
|
||||
let itemID = itemIDs[position]
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Public (Mutators)
|
||||
|
||||
/**
|
||||
Appends extra items to the specified section
|
||||
|
||||
- parameter itemIDs: the object identifiers for the objects to append
|
||||
- parameter sectionID: the section to append the items to
|
||||
*/
|
||||
public mutating func appendItems<C: Collection>(withIDs itemIDs: C, toSectionWithID sectionID: SectionID? = nil) where C.Element == ItemID {
|
||||
|
||||
self.diffableSnapshot.appendItems(itemIDs, toSection: sectionID)
|
||||
}
|
||||
|
||||
/**
|
||||
Inserts extra items before a specified item
|
||||
|
||||
- parameter itemIDs: the object identifiers for the objects to insert
|
||||
- parameter beforeItemID: an existing identifier to insert items before of. Specifying an invalid value will raise an exception.
|
||||
*/
|
||||
public mutating func insertItems<C: Collection>(withIDs itemIDs: C, beforeItemID: ItemID) where C.Element == ItemID {
|
||||
|
||||
self.diffableSnapshot.insertItems(itemIDs, beforeItem: beforeItemID)
|
||||
}
|
||||
|
||||
/**
|
||||
Inserts extra items after a specified item
|
||||
|
||||
- parameter itemIDs: the object identifiers for the objects to insert
|
||||
- parameter beforeItemID: an existing identifier to insert items after of. Specifying an invalid value will raise an exception.
|
||||
*/
|
||||
public mutating func insertItems<C: Collection>(withIDs itemIDs: C, afterItemID: ItemID) where C.Element == ItemID {
|
||||
|
||||
self.diffableSnapshot.insertItems(itemIDs, afterItem: afterItemID)
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes the specified items
|
||||
|
||||
- parameter itemIDs: the object identifiers for the objects to delete
|
||||
*/
|
||||
public mutating func deleteItems<S: Sequence>(withIDs itemIDs: S) where S.Element == ItemID {
|
||||
|
||||
self.diffableSnapshot.deleteItems(itemIDs)
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes all items
|
||||
*/
|
||||
public mutating func deleteAllItems() {
|
||||
|
||||
self.diffableSnapshot.deleteAllItems()
|
||||
}
|
||||
|
||||
/**
|
||||
Moves an item before another specified item
|
||||
|
||||
- parameter itemID: an object identifier in the list to move. Specifying an invalid value will raise an exception.
|
||||
- parameter beforeItemID: another identifier to move the item before of. Specifying an invalid value will raise an exception.
|
||||
*/
|
||||
public mutating func moveItem(withID itemID: ItemID, beforeItemID: ItemID) {
|
||||
|
||||
self.diffableSnapshot.moveItem(itemID, beforeItem: beforeItemID)
|
||||
}
|
||||
|
||||
/**
|
||||
Moves an item after another specified item
|
||||
|
||||
- parameter itemID: an object identifier in the list to move. Specifying an invalid value will raise an exception.
|
||||
- parameter beforeItemID: another identifier to move the item after of. Specifying an invalid value will raise an exception.
|
||||
*/
|
||||
public mutating func moveItem(withID itemID: ItemID, afterItemID: ItemID) {
|
||||
|
||||
self.diffableSnapshot.moveItem(itemID, afterItem: afterItemID)
|
||||
}
|
||||
|
||||
/**
|
||||
Marks the specified items as reloaded
|
||||
|
||||
- parameter itemIDs: the object identifiers to reload
|
||||
*/
|
||||
public mutating func reloadItems<S: Sequence>(withIDs itemIDs: S) where S.Element == ItemID {
|
||||
|
||||
self.diffableSnapshot.reloadItems(itemIDs)
|
||||
}
|
||||
|
||||
/**
|
||||
Appends new section identifiers to the end of the list
|
||||
|
||||
- parameter sectionIDs: the sections to append
|
||||
*/
|
||||
public mutating func appendSections<C: Collection>(withIDs sectionIDs: C) where C.Element == SectionID {
|
||||
|
||||
self.diffableSnapshot.appendSections(sectionIDs)
|
||||
}
|
||||
|
||||
/**
|
||||
Inserts new sections before an existing section
|
||||
|
||||
- parameter sectionIDs: the section identifiers for the sections to insert
|
||||
- parameter beforeSectionID: an existing identifier to insert items before of. Specifying an invalid value will raise an exception.
|
||||
*/
|
||||
public mutating func insertSections<C: Collection>(withIDs sectionIDs: C, beforeSectionID: SectionID) where C.Element == SectionID {
|
||||
|
||||
self.diffableSnapshot.insertSections(sectionIDs, beforeSection: beforeSectionID)
|
||||
}
|
||||
|
||||
/**
|
||||
Inserts new sections after an existing section
|
||||
|
||||
- parameter sectionIDs: the section identifiers for the sections to insert
|
||||
- parameter beforeSectionID: an existing identifier to insert items after of. Specifying an invalid value will raise an exception.
|
||||
*/
|
||||
public mutating func insertSections<C: Collection>(withIDs sectionIDs: C, afterSectionID: SectionID) where C.Element == SectionID {
|
||||
|
||||
self.diffableSnapshot.insertSections(sectionIDs, afterSection: afterSectionID)
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes the specified sections
|
||||
|
||||
- parameter sectionIDs: the section identifiers for the sections to delete
|
||||
*/
|
||||
public mutating func deleteSections<S: Sequence>(withIDs sectionIDs: S) where S.Element == SectionID {
|
||||
|
||||
self.diffableSnapshot.deleteSections(sectionIDs)
|
||||
}
|
||||
|
||||
/**
|
||||
Moves a section before another specified section
|
||||
|
||||
- parameter sectionID: a section identifier in the list to move. Specifying an invalid value will raise an exception.
|
||||
- parameter beforeSectionID: another identifier to move the section before of. Specifying an invalid value will raise an exception.
|
||||
*/
|
||||
public mutating func moveSection(withID sectionID: SectionID, beforeSectionID: SectionID) {
|
||||
|
||||
self.diffableSnapshot.moveSection(sectionID, beforeSection: beforeSectionID)
|
||||
}
|
||||
|
||||
/**
|
||||
Moves a section after another specified section
|
||||
|
||||
- parameter sectionID: a section identifier in the list to move. Specifying an invalid value will raise an exception.
|
||||
- parameter afterSectionID: another identifier to move the section after of. Specifying an invalid value will raise an exception.
|
||||
*/
|
||||
public mutating func moveSection(withID sectionID: SectionID, afterSectionID: SectionID) {
|
||||
|
||||
self.diffableSnapshot.moveSection(sectionID, afterSection: afterSectionID)
|
||||
}
|
||||
|
||||
/**
|
||||
Marks the specified sections as reloaded
|
||||
|
||||
- parameter sectionIDs: the section identifiers to reload
|
||||
*/
|
||||
public mutating func reloadSections<S: Sequence>(withIDs sectionIDs: S) where S.Element == SectionID {
|
||||
|
||||
self.diffableSnapshot.reloadSections(sectionIDs)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: RandomAccessCollection
|
||||
|
||||
public var startIndex: Index {
|
||||
|
||||
return self.diffableSnapshot.itemIdentifiers.startIndex
|
||||
}
|
||||
|
||||
public var endIndex: Index {
|
||||
|
||||
return self.diffableSnapshot.itemIdentifiers.endIndex
|
||||
}
|
||||
|
||||
|
||||
// MARK: Sequence
|
||||
|
||||
public typealias Element = ObjectPublisher<O>
|
||||
|
||||
public typealias Index = Int
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (_ lhs: Self, _ rhs: Self) -> Bool {
|
||||
|
||||
return lhs.id == rhs.id
|
||||
}
|
||||
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
|
||||
hasher.combine(self.id)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal private(set) var diffableSnapshot: Internals.DiffableDataSourceSnapshot
|
||||
|
||||
internal init() {
|
||||
|
||||
self.diffableSnapshot = Internals.DiffableDataSourceSnapshot()
|
||||
self.context = nil
|
||||
}
|
||||
|
||||
internal init(diffableSnapshot: Internals.DiffableDataSourceSnapshot, context: NSManagedObjectContext) {
|
||||
|
||||
self.diffableSnapshot = diffableSnapshot
|
||||
self.context = context
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let id: UUID = .init()
|
||||
private let context: NSManagedObjectContext?
|
||||
|
||||
}
|
||||
@@ -33,7 +33,7 @@ import Foundation
|
||||
`MigrationResult.success` indicates either the migration succeeded, or there were no migrations needed. The associated value is an array of `MigrationType`s reflecting the migration steps completed.
|
||||
`MigrationResult.failure` indicates that the migration failed. The associated object for this value is the a `CoreStoreError` enum value.
|
||||
```
|
||||
CoreStore.upgradeStorageIfNeeded(SQLiteStorage(fileName: "data.sqlite")) { (result) in
|
||||
dataStack.upgradeStorageIfNeeded(SQLiteStorage(fileName: "data.sqlite")) { (result) in
|
||||
switch result {
|
||||
case .success(let migrationSteps):
|
||||
// ...
|
||||
|
||||
@@ -42,7 +42,7 @@ extension DataStack {
|
||||
- returns: an `NSFetchedResultsController` that observes the `DataStack`
|
||||
*/
|
||||
@nonobjc
|
||||
public func createFetchedResultsController<D: NSManagedObject>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController<D> {
|
||||
public func createFetchedResultsController<O: NSManagedObject>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController<O> {
|
||||
|
||||
return Internals.createFRC(
|
||||
fromContext: self.mainContext,
|
||||
@@ -62,7 +62,7 @@ extension DataStack {
|
||||
- returns: an `NSFetchedResultsController` that observes the `DataStack`
|
||||
*/
|
||||
@nonobjc
|
||||
public func createFetchedResultsController<D: NSManagedObject>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController<D> {
|
||||
public func createFetchedResultsController<O: NSManagedObject>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController<O> {
|
||||
|
||||
return Internals.createFRC(
|
||||
fromContext: self.mainContext,
|
||||
@@ -81,7 +81,7 @@ extension DataStack {
|
||||
- returns: an `NSFetchedResultsController` that observes the `DataStack`
|
||||
*/
|
||||
@nonobjc
|
||||
public func createFetchedResultsController<D: NSManagedObject>(_ from: From<D>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController<D> {
|
||||
public func createFetchedResultsController<O: NSManagedObject>(_ from: From<O>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController<O> {
|
||||
|
||||
return Internals.createFRC(
|
||||
fromContext: self.mainContext,
|
||||
@@ -100,7 +100,7 @@ extension DataStack {
|
||||
- returns: an `NSFetchedResultsController` that observes the `DataStack`
|
||||
*/
|
||||
@nonobjc
|
||||
public func createFetchedResultsController<D: NSManagedObject>(forDataStack dataStack: DataStack, _ from: From<D>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController<D> {
|
||||
public func createFetchedResultsController<O: NSManagedObject>(forDataStack dataStack: DataStack, _ from: From<O>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController<O> {
|
||||
|
||||
return Internals.createFRC(
|
||||
fromContext: self.mainContext,
|
||||
@@ -127,7 +127,7 @@ extension UnsafeDataTransaction {
|
||||
- returns: an `NSFetchedResultsController` that observes the `UnsafeDataTransaction`
|
||||
*/
|
||||
@nonobjc
|
||||
public func createFetchedResultsController<D: NSManagedObject>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController<D> {
|
||||
public func createFetchedResultsController<O: NSManagedObject>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController<O> {
|
||||
|
||||
return Internals.createFRC(
|
||||
fromContext: self.context,
|
||||
@@ -147,7 +147,7 @@ extension UnsafeDataTransaction {
|
||||
- returns: an `NSFetchedResultsController` that observes the `UnsafeDataTransaction`
|
||||
*/
|
||||
@nonobjc
|
||||
public func createFetchedResultsController<D: NSManagedObject>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController<D> {
|
||||
public func createFetchedResultsController<O: NSManagedObject>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController<O> {
|
||||
|
||||
return Internals.createFRC(
|
||||
fromContext: self.context,
|
||||
@@ -166,7 +166,7 @@ extension UnsafeDataTransaction {
|
||||
- returns: an `NSFetchedResultsController` that observes the `UnsafeDataTransaction`
|
||||
*/
|
||||
@nonobjc
|
||||
public func createFetchedResultsController<D: NSManagedObject>(_ from: From<D>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController<D> {
|
||||
public func createFetchedResultsController<O: NSManagedObject>(_ from: From<O>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController<O> {
|
||||
|
||||
return Internals.createFRC(
|
||||
fromContext: self.context,
|
||||
@@ -185,7 +185,7 @@ extension UnsafeDataTransaction {
|
||||
- returns: an `NSFetchedResultsController` that observes the `UnsafeDataTransaction`
|
||||
*/
|
||||
@nonobjc
|
||||
public func createFetchedResultsController<D: NSManagedObject>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController<D> {
|
||||
public func createFetchedResultsController<O: NSManagedObject>(_ from: From<O>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController<O> {
|
||||
|
||||
return Internals.createFRC(
|
||||
fromContext: self.context,
|
||||
@@ -205,7 +205,7 @@ extension Internals {
|
||||
// MARK: FilePrivate
|
||||
|
||||
@available(macOS 10.12, *)
|
||||
fileprivate static func createFRC<D: NSManagedObject>(fromContext context: NSManagedObjectContext, from: From<D>, sectionBy: SectionBy<D>? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController<D> {
|
||||
fileprivate static func createFRC<O: NSManagedObject>(fromContext context: NSManagedObjectContext, from: From<O>, sectionBy: SectionBy<O>? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController<O> {
|
||||
|
||||
let controller = Internals.CoreStoreFetchedResultsController(
|
||||
context: context,
|
||||
@@ -218,7 +218,7 @@ extension Internals {
|
||||
|
||||
Internals.assert(
|
||||
fetchRequest.sortDescriptors?.isEmpty == false,
|
||||
"An \(Internals.typeName(NSFetchedResultsController<D>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<D>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
"An \(Internals.typeName(NSFetchedResultsController<O>.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy<O>.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor."
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -38,15 +38,7 @@ extension NSManagedObject {
|
||||
|
||||
return nil
|
||||
}
|
||||
if context.isTransactionContext {
|
||||
|
||||
return context.parentTransaction?.isRunningInAllowedQueue()
|
||||
}
|
||||
if context.isDataStackContext {
|
||||
|
||||
return Thread.isMainThread
|
||||
}
|
||||
return nil
|
||||
return context.isRunningInAllowedQueue()
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
@@ -56,14 +48,6 @@ extension NSManagedObject {
|
||||
|
||||
return nil
|
||||
}
|
||||
if context.isTransactionContext {
|
||||
|
||||
return true
|
||||
}
|
||||
if context.isDataStackContext {
|
||||
|
||||
return false
|
||||
}
|
||||
return nil
|
||||
return context.isEditableInContext()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,16 +85,76 @@ extension NSManagedObjectContext {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func objectPublisher<O: DynamicObject>(objectID: NSManagedObjectID) -> ObjectPublisher<O> {
|
||||
|
||||
let cache: NSMapTable<NSManagedObjectID, ObjectPublisher<O>> = self.userInfo(for: .objectPublishersCache(O.self)) {
|
||||
|
||||
return .strongToWeakObjects()
|
||||
}
|
||||
return Internals.with {
|
||||
|
||||
if let objectPublisher = cache.object(forKey: objectID) {
|
||||
|
||||
return objectPublisher
|
||||
}
|
||||
let objectPublisher = ObjectPublisher<O>.createUncached(objectID: objectID, context: self)
|
||||
cache.setObject(objectPublisher, forKey: objectID)
|
||||
return objectPublisher
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func objectsDidChangeObserver<U: AnyObject>(for observer: U) -> Internals.SharedNotificationObserver<(updated: Set<NSManagedObjectID>, deleted: Set<NSManagedObjectID>)> {
|
||||
|
||||
return self.userInfo(for: .objectsChangeObserver(U.self)) { [unowned self] in
|
||||
|
||||
return .init(
|
||||
notificationName: .NSManagedObjectContextObjectsDidChange,
|
||||
object: self,
|
||||
queue: .main,
|
||||
sharedValue: { (notification) -> (updated: Set<NSManagedObjectID>, deleted: Set<NSManagedObjectID>) in
|
||||
|
||||
guard let userInfo = notification.userInfo else {
|
||||
|
||||
return (updated: [], deleted: [])
|
||||
}
|
||||
if userInfo[NSInvalidatedAllObjectsKey] != nil {
|
||||
|
||||
let context = notification.object as! NSManagedObjectContext
|
||||
return (updated: Set(context.registeredObjects.map({ $0.objectID })), deleted: [])
|
||||
}
|
||||
|
||||
var updatedObjectIDs: Set<NSManagedObjectID> = []
|
||||
if let updatedObjects = userInfo[NSUpdatedObjectsKey] as? Set<NSManagedObject> {
|
||||
|
||||
updatedObjectIDs.formUnion(updatedObjects.map({ $0.objectID }))
|
||||
}
|
||||
if let mergedObjects = userInfo[NSRefreshedObjectsKey] as? Set<NSManagedObject> {
|
||||
|
||||
updatedObjectIDs.formUnion(mergedObjects.map({ $0.objectID }))
|
||||
}
|
||||
if let mergedObjects = userInfo[NSInvalidatedObjectsKey] as? Set<NSManagedObject> {
|
||||
|
||||
updatedObjectIDs.formUnion(mergedObjects.map({ $0.objectID }))
|
||||
}
|
||||
let deletedObjectIDs: Set<NSManagedObject> = (userInfo[NSDeletedObjectsKey] as? Set<NSManagedObject>) ?? []
|
||||
return (updated: updatedObjectIDs, deleted: Set(deletedObjectIDs.map({ $0.objectID })))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func objectsDidChangeObserver<U: AnyObject>(remove: U) {
|
||||
|
||||
_ = self.userInfo(for: .objectsChangeObserver(U.self), initialize: { nil as Any? })
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private struct PropertyKeys {
|
||||
|
||||
static var observerForWillSaveNotification: Void?
|
||||
static var shouldCascadeSavesToParent: Void?
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
private var observerForWillSaveNotification: Internals.NotificationObserver? {
|
||||
|
||||
@@ -114,5 +174,47 @@ extension NSManagedObjectContext {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func userInfo<T>(for key: UserInfoKeys, initialize: @escaping () -> T) -> T {
|
||||
|
||||
let keyString = key.keyString
|
||||
if let value = self.userInfo[keyString] as? T {
|
||||
|
||||
return value
|
||||
}
|
||||
let value = initialize()
|
||||
self.userInfo[keyString] = value
|
||||
return value
|
||||
}
|
||||
|
||||
|
||||
// MARK: - PropertyKeys
|
||||
|
||||
private struct PropertyKeys {
|
||||
|
||||
static var observerForWillSaveNotification: Void?
|
||||
static var shouldCascadeSavesToParent: Void?
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UserInfoKeys
|
||||
|
||||
private enum UserInfoKeys {
|
||||
|
||||
case objectPublishersCache(DynamicObject.Type)
|
||||
case objectsChangeObserver(AnyObject.Type)
|
||||
|
||||
var keyString: String {
|
||||
|
||||
switch self {
|
||||
|
||||
case .objectPublishersCache(let objectType):
|
||||
return "CoreStore.objectPublishersCache(\(Internals.typeName(objectType)))"
|
||||
|
||||
case .objectsChangeObserver:
|
||||
return "CoreStore.objectsChangeObserver"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// NSManagedObjectContext+Logging.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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: - NSManagedObjectContext
|
||||
|
||||
extension NSManagedObjectContext {
|
||||
|
||||
@nonobjc
|
||||
internal func isRunningInAllowedQueue() -> Bool? {
|
||||
|
||||
if self.isTransactionContext {
|
||||
|
||||
return self.parentTransaction?.isRunningInAllowedQueue()
|
||||
}
|
||||
if self.isDataStackContext {
|
||||
|
||||
return Thread.isMainThread
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func isEditableInContext() -> Bool? {
|
||||
|
||||
if self.isTransactionContext {
|
||||
|
||||
return true
|
||||
}
|
||||
if self.isDataStackContext {
|
||||
|
||||
return false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
// MARK: FetchableSource
|
||||
|
||||
@nonobjc
|
||||
public func fetchExisting<D: DynamicObject>(_ object: D) -> D? {
|
||||
public func fetchExisting<O: DynamicObject>(_ object: O) -> O? {
|
||||
|
||||
let rawObject = object.cs_toRaw()
|
||||
if rawObject.objectID.isTemporaryID {
|
||||
@@ -75,12 +75,12 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchExisting<D: DynamicObject>(_ objectID: NSManagedObjectID) -> D? {
|
||||
public func fetchExisting<O: DynamicObject>(_ objectID: NSManagedObjectID) -> O? {
|
||||
|
||||
do {
|
||||
|
||||
let existingObject = try self.existingObject(with: objectID)
|
||||
return D.cs_fromRaw(object: existingObject)
|
||||
return O.cs_fromRaw(object: existingObject)
|
||||
}
|
||||
catch _ {
|
||||
|
||||
@@ -89,25 +89,25 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchExisting<D: DynamicObject, S: Sequence>(_ objects: S) -> [D] where S.Iterator.Element == D {
|
||||
public func fetchExisting<O: DynamicObject, S: Sequence>(_ objects: S) -> [O] where S.Iterator.Element == O {
|
||||
|
||||
return objects.compactMap({ self.fetchExisting($0.cs_id()) })
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchExisting<D: DynamicObject, S: Sequence>(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID {
|
||||
public func fetchExisting<O: DynamicObject, S: Sequence>(_ objectIDs: S) -> [O] where S.Iterator.Element == NSManagedObjectID {
|
||||
|
||||
return objectIDs.compactMap({ self.fetchExisting($0) })
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchOne<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> D? {
|
||||
public func fetchOne<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> O? {
|
||||
|
||||
return try self.fetchOne(from, fetchClauses)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchOne<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> D? {
|
||||
public func fetchOne<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> O? {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||
try from.applyToFetchRequest(fetchRequest, context: self)
|
||||
@@ -126,13 +126,13 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchAll<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> [D] {
|
||||
public func fetchAll<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> [O] {
|
||||
|
||||
return try self.fetchAll(from, fetchClauses)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchAll<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> [D] {
|
||||
public func fetchAll<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> [O] {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||
try from.applyToFetchRequest(fetchRequest, context: self)
|
||||
@@ -152,13 +152,13 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchCount<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> Int {
|
||||
public func fetchCount<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> Int {
|
||||
|
||||
return try self.fetchCount(from, fetchClauses)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchCount<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> Int {
|
||||
public func fetchCount<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> Int {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSNumber>()
|
||||
try from.applyToFetchRequest(fetchRequest, context: self)
|
||||
@@ -176,13 +176,13 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> NSManagedObjectID? {
|
||||
public func fetchObjectID<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> NSManagedObjectID? {
|
||||
|
||||
return try self.fetchObjectID(from, fetchClauses)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> NSManagedObjectID? {
|
||||
public func fetchObjectID<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> NSManagedObjectID? {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObjectID>()
|
||||
try from.applyToFetchRequest(fetchRequest, context: self)
|
||||
@@ -201,13 +201,13 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: FetchClause...) throws -> [NSManagedObjectID] {
|
||||
public func fetchObjectIDs<O>(_ from: From<O>, _ fetchClauses: FetchClause...) throws -> [NSManagedObjectID] {
|
||||
|
||||
return try self.fetchObjectIDs(from, fetchClauses)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) throws -> [NSManagedObjectID] {
|
||||
public func fetchObjectIDs<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) throws -> [NSManagedObjectID] {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObjectID>()
|
||||
try from.applyToFetchRequest(fetchRequest, context: self)
|
||||
@@ -257,13 +257,13 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
// MARK: QueryableSource
|
||||
|
||||
@nonobjc
|
||||
public func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: QueryClause...) throws -> U? {
|
||||
public func queryValue<O, U: QueryableAttributeType>(_ from: From<O>, _ selectClause: Select<O, U>, _ queryClauses: QueryClause...) throws -> U? {
|
||||
|
||||
return try self.queryValue(from, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: [QueryClause]) throws -> U? {
|
||||
public func queryValue<O, U: QueryableAttributeType>(_ from: From<O>, _ selectClause: Select<O, U>, _ queryClauses: [QueryClause]) throws -> U? {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSDictionary>()
|
||||
try from.applyToFetchRequest(fetchRequest, context: self)
|
||||
@@ -283,13 +283,13 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: QueryClause...) throws -> [[String: Any]] {
|
||||
public func queryAttributes<O>(_ from: From<O>, _ selectClause: Select<O, NSDictionary>, _ queryClauses: QueryClause...) throws -> [[String: Any]] {
|
||||
|
||||
return try self.queryAttributes(from, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
public func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: [QueryClause]) throws -> [[String: Any]] {
|
||||
public func queryAttributes<O>(_ from: From<O>, _ selectClause: Select<O, NSDictionary>, _ queryClauses: [QueryClause]) throws -> [[String: Any]] {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSDictionary>()
|
||||
try from.applyToFetchRequest(fetchRequest, context: self)
|
||||
@@ -320,7 +320,7 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
// MARK: Deleting
|
||||
|
||||
@nonobjc
|
||||
internal func deleteAll<D>(_ from: From<D>, _ deleteClauses: [FetchClause]) throws -> Int {
|
||||
internal func deleteAll<O>(_ from: From<O>, _ deleteClauses: [FetchClause]) throws -> Int {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||
try from.applyToFetchRequest(fetchRequest, context: self)
|
||||
@@ -343,9 +343,9 @@ extension NSManagedObjectContext {
|
||||
// MARK: Fetching
|
||||
|
||||
@nonobjc
|
||||
internal func fetchOne<D: NSManagedObject>(_ fetchRequest: Internals.CoreStoreFetchRequest<D>) throws -> D? {
|
||||
internal func fetchOne<O: NSManagedObject>(_ fetchRequest: Internals.CoreStoreFetchRequest<O>) throws -> O? {
|
||||
|
||||
var fetchResults: [D]?
|
||||
var fetchResults: [O]?
|
||||
var fetchError: Error?
|
||||
self.performAndWait {
|
||||
|
||||
@@ -371,9 +371,9 @@ extension NSManagedObjectContext {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func fetchAll<D: NSManagedObject>(_ fetchRequest: Internals.CoreStoreFetchRequest<D>) throws -> [D] {
|
||||
internal func fetchAll<O: NSManagedObject>(_ fetchRequest: Internals.CoreStoreFetchRequest<O>) throws -> [O] {
|
||||
|
||||
var fetchResults: [D]?
|
||||
var fetchResults: [O]?
|
||||
var fetchError: Error?
|
||||
self.performAndWait {
|
||||
|
||||
@@ -458,7 +458,7 @@ extension NSManagedObjectContext {
|
||||
// MARK: Querying
|
||||
|
||||
@nonobjc
|
||||
internal func queryValue<D, U: QueryableAttributeType>(_ selectTerms: [SelectTerm<D>], fetchRequest: Internals.CoreStoreFetchRequest<NSDictionary>) throws -> U? {
|
||||
internal func queryValue<O, U: QueryableAttributeType>(_ selectTerms: [SelectTerm<O>], fetchRequest: Internals.CoreStoreFetchRequest<NSDictionary>) throws -> U? {
|
||||
|
||||
var fetchResults: [Any]?
|
||||
var fetchError: Error?
|
||||
@@ -478,7 +478,7 @@ extension NSManagedObjectContext {
|
||||
if let rawResult = fetchResults.first as? NSDictionary,
|
||||
let rawObject = rawResult[selectTerms.first!.keyPathString] as? U.QueryableNativeType {
|
||||
|
||||
return Select<D, U>.ReturnType.cs_fromQueryableNativeType(rawObject)
|
||||
return Select<O, U>.ReturnType.cs_fromQueryableNativeType(rawObject)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -491,7 +491,7 @@ extension NSManagedObjectContext {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func queryValue<D>(_ selectTerms: [SelectTerm<D>], fetchRequest: Internals.CoreStoreFetchRequest<NSDictionary>) throws -> Any? {
|
||||
internal func queryValue<O>(_ selectTerms: [SelectTerm<O>], fetchRequest: Internals.CoreStoreFetchRequest<NSDictionary>) throws -> Any? {
|
||||
|
||||
var fetchResults: [Any]?
|
||||
var fetchError: Error?
|
||||
@@ -555,7 +555,7 @@ extension NSManagedObjectContext {
|
||||
// MARK: Deleting
|
||||
|
||||
@nonobjc
|
||||
internal func deleteAll<D: NSManagedObject>(_ fetchRequest: Internals.CoreStoreFetchRequest<D>) throws -> Int {
|
||||
internal func deleteAll<O: NSManagedObject>(_ fetchRequest: Internals.CoreStoreFetchRequest<O>) throws -> Int {
|
||||
|
||||
var numberOfDeletedObjects: Int?
|
||||
var fetchError: Error?
|
||||
|
||||
+53
-51
@@ -32,7 +32,7 @@ import CoreData
|
||||
/**
|
||||
The `ObjectMonitor` monitors changes to a single `DynamicObject` instance. Observers that implement the `ObjectObserver` protocol may then register themselves to the `ObjectMonitor`'s `addObserver(_:)` method:
|
||||
```
|
||||
let monitor = CoreStore.monitorObject(object)
|
||||
let monitor = dataStack.monitorObject(object)
|
||||
monitor.addObserver(self)
|
||||
```
|
||||
The created `ObjectMonitor` instance needs to be held on (retained) for as long as the object needs to be observed.
|
||||
@@ -40,22 +40,22 @@ import CoreData
|
||||
Observers registered via `addObserver(_:)` are not retained. `ObjectMonitor` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles.
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
|
||||
public final class ObjectMonitor<O: DynamicObject>: Equatable {
|
||||
|
||||
/**
|
||||
The type for the object contained by the `ObjectMonitor`
|
||||
The object type represented by this `ObjectMonitor`
|
||||
*/
|
||||
public typealias ObjectType = D
|
||||
public typealias ObjectType = O
|
||||
|
||||
/**
|
||||
Returns the `DynamicObject` instance being observed, or `nil` if the object was already deleted.
|
||||
*/
|
||||
public var object: ObjectType? {
|
||||
public var object: O? {
|
||||
|
||||
return self.fetchedResultsController
|
||||
.fetchedObjects?
|
||||
.first
|
||||
.flatMap({ ObjectType.cs_fromRaw(object: $0) })
|
||||
.flatMap({ O.cs_fromRaw(object: $0) })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +77,7 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
|
||||
- parameter observer: an `ObjectObserver` to send change notifications to
|
||||
*/
|
||||
public func addObserver<U: ObjectObserver>(_ observer: U) where U.ObjectEntityType == ObjectType {
|
||||
public func addObserver<U: ObjectObserver>(_ observer: U) where U.ObjectEntityType == O {
|
||||
|
||||
self.unregisterObserver(observer)
|
||||
self.registerObserver(
|
||||
@@ -104,7 +104,7 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
|
||||
- parameter observer: an `ObjectObserver` to unregister notifications to
|
||||
*/
|
||||
public func removeObserver<U: ObjectObserver>(_ observer: U) where U.ObjectEntityType == ObjectType {
|
||||
public func removeObserver<U: ObjectObserver>(_ observer: U) where U.ObjectEntityType == O {
|
||||
|
||||
self.unregisterObserver(observer)
|
||||
}
|
||||
@@ -127,7 +127,7 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (lhs: ObjectMonitor<ObjectType>, rhs: ObjectMonitor<ObjectType>) -> Bool {
|
||||
public static func == (lhs: ObjectMonitor<O>, rhs: ObjectMonitor<O>) -> Bool {
|
||||
|
||||
return lhs === rhs
|
||||
}
|
||||
@@ -137,7 +137,7 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
return lhs.fetchedResultsController === rhs.fetchedResultsController
|
||||
}
|
||||
|
||||
public static func ~= (lhs: ObjectMonitor<ObjectType>, rhs: ObjectMonitor<ObjectType>) -> Bool {
|
||||
public static func ~= (lhs: ObjectMonitor<O>, rhs: ObjectMonitor<O>) -> Bool {
|
||||
|
||||
return lhs === rhs
|
||||
}
|
||||
@@ -158,17 +158,37 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal convenience init(dataStack: DataStack, object: ObjectType) {
|
||||
internal init(objectID: O.ObjectID, context: NSManagedObjectContext) {
|
||||
|
||||
self.init(context: dataStack.mainContext, object: object)
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||
fetchRequest.entity = objectID.entity
|
||||
fetchRequest.fetchLimit = 0
|
||||
fetchRequest.resultType = .managedObjectResultType
|
||||
fetchRequest.sortDescriptors = []
|
||||
fetchRequest.includesPendingChanges = false
|
||||
fetchRequest.shouldRefreshRefetchedObjects = true
|
||||
|
||||
let fetchedResultsController = Internals.CoreStoreFetchedResultsController(
|
||||
context: context,
|
||||
fetchRequest: fetchRequest,
|
||||
from: From<O>([objectID.persistentStore?.configurationName]),
|
||||
applyFetchClauses: Where<O>("SELF", isEqualTo: objectID).applyToFetchRequest
|
||||
)
|
||||
|
||||
let fetchedResultsControllerDelegate = Internals.FetchedResultsControllerDelegate()
|
||||
|
||||
self.id = objectID
|
||||
self.fetchedResultsController = fetchedResultsController
|
||||
self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate
|
||||
|
||||
fetchedResultsControllerDelegate.handler = self
|
||||
fetchedResultsControllerDelegate.fetchedResultsController = fetchedResultsController
|
||||
try! fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
|
||||
self.lastCommittedAttributes = (self.object?.cs_toRaw().committedValues(forKeys: nil) as? [String: NSObject]) ?? [:]
|
||||
}
|
||||
|
||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, object: ObjectType) {
|
||||
|
||||
self.init(context: unsafeTransaction.context, object: object)
|
||||
}
|
||||
|
||||
internal func registerObserver<U: AnyObject>(_ observer: U, willChangeObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<ObjectType>, _ object: ObjectType) -> Void, didDeleteObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<ObjectType>, _ object: ObjectType) -> Void, didUpdateObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<ObjectType>, _ object: ObjectType, _ changedPersistentKeys: Set<String>) -> Void) {
|
||||
internal func registerObserver<U: AnyObject>(_ observer: U, willChangeObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<O>, _ object: O) -> Void, didDeleteObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<O>, _ object: O) -> Void, didUpdateObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<O>, _ object: O, _ changedPersistentKeys: Set<String>) -> Void) {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
@@ -250,6 +270,7 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let id: O.ObjectID
|
||||
private let fetchedResultsController: Internals.CoreStoreFetchedResultsController
|
||||
private let fetchedResultsControllerDelegate: Internals.FetchedResultsControllerDelegate
|
||||
private var lastCommittedAttributes = [String: NSObject]()
|
||||
@@ -257,38 +278,13 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
private var willChangeObjectKey: Void?
|
||||
private var didDeleteObjectKey: Void?
|
||||
private var didUpdateObjectKey: Void?
|
||||
|
||||
private init(context: NSManagedObjectContext, object: ObjectType) {
|
||||
|
||||
let objectID = object.cs_id()
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||
fetchRequest.entity = objectID.entity
|
||||
fetchRequest.fetchLimit = 0
|
||||
fetchRequest.resultType = .managedObjectResultType
|
||||
fetchRequest.sortDescriptors = []
|
||||
fetchRequest.includesPendingChanges = false
|
||||
fetchRequest.shouldRefreshRefetchedObjects = true
|
||||
|
||||
let fetchedResultsController = Internals.CoreStoreFetchedResultsController(
|
||||
context: context,
|
||||
fetchRequest: fetchRequest,
|
||||
from: From<ObjectType>([objectID.persistentStore?.configurationName]),
|
||||
applyFetchClauses: Where<ObjectType>("SELF", isEqualTo: objectID).applyToFetchRequest
|
||||
)
|
||||
|
||||
let fetchedResultsControllerDelegate = Internals.FetchedResultsControllerDelegate()
|
||||
|
||||
self.fetchedResultsController = fetchedResultsController
|
||||
self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate
|
||||
|
||||
fetchedResultsControllerDelegate.handler = self
|
||||
fetchedResultsControllerDelegate.fetchedResultsController = fetchedResultsController
|
||||
try! fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
|
||||
self.lastCommittedAttributes = (self.object?.cs_toRaw().committedValues(forKeys: nil) as? [String: NSObject]) ?? [:]
|
||||
|
||||
private var context: NSManagedObjectContext {
|
||||
|
||||
return self.fetchedResultsController.managedObjectContext
|
||||
}
|
||||
|
||||
private func registerChangeNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ObjectMonitor<ObjectType>) -> Void) {
|
||||
private func registerChangeNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ObjectMonitor<O>) -> Void) {
|
||||
|
||||
Internals.setAssociatedRetainedObject(
|
||||
Internals.NotificationObserver(
|
||||
@@ -308,7 +304,7 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
)
|
||||
}
|
||||
|
||||
private func registerObjectNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ObjectMonitor<ObjectType>, _ object: ObjectType) -> Void) {
|
||||
private func registerObjectNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ObjectMonitor<O>, _ object: O) -> Void) {
|
||||
|
||||
Internals.setAssociatedRetainedObject(
|
||||
Internals.NotificationObserver(
|
||||
@@ -322,13 +318,19 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
|
||||
return
|
||||
}
|
||||
callback(self, ObjectType.cs_fromRaw(object: object))
|
||||
callback(self, O.cs_fromRaw(object: object))
|
||||
}
|
||||
),
|
||||
forKey: notificationKey,
|
||||
inObject: observer
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,9 +30,9 @@ import CoreData
|
||||
// MARK: - ObjectObserver
|
||||
|
||||
/**
|
||||
Implement the `ObjectObserver` protocol to observe changes to a single `DynamicObject` instance. `ObjectObserver`s may register themselves to a `ObjectMonitor`'s `addObserver(_:)` method:
|
||||
Implement the `ObjectObserver` protocol to observe changes to a single `DynamicObject` instance. `ObjectObserver`s may register themselves to an `ObjectMonitor`'s `addObserver(_:)` method:
|
||||
```
|
||||
let monitor = CoreStore.monitorObject(object)
|
||||
let monitor = dataStack.monitorObject(object)
|
||||
monitor.addObserver(self)
|
||||
```
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
//
|
||||
// ObjectPublisher.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
#endif
|
||||
|
||||
#if canImport(SwiftUI)
|
||||
import SwiftUI
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - ObjectPublisher
|
||||
|
||||
/**
|
||||
The `ObjectPublisher` tracks changes to a single `DynamicObject` instance. Objects that need to be notified of `ObjectPublisher` changes may register themselves to its `addObserver(_:_:)` method:
|
||||
```
|
||||
let objectPublisher = CoreStoreDefaults.dataStack.objectPublisher(object)
|
||||
objectPublisher.addObserver(self) { (objectPublisher) in
|
||||
// Handle changes
|
||||
}
|
||||
```
|
||||
The created `ObjectPublisher` instance needs to be held on (retained) for as long as the object needs to be observed.
|
||||
|
||||
Observers registered via `addObserver(_:_:)` are not retained. `ObjectPublisher` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles.
|
||||
|
||||
The `ObjectPublisher`'s `snapshot` value is a lazy copy operation that extracts all property values at a specific point time. This cached copy is invalidated right before the `ObjectPublisher` broadcasts any changes to its observers.
|
||||
*/
|
||||
@dynamicMemberLookup
|
||||
public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hashable {
|
||||
|
||||
// MARK: Public (Accessors)
|
||||
/**
|
||||
A snapshot of the latest state of this list. Returns `nil` if the object has been deleted.
|
||||
*/
|
||||
public var snapshot: ObjectSnapshot<O>? {
|
||||
|
||||
return self.lazySnapshot
|
||||
}
|
||||
|
||||
/**
|
||||
The actual `DynamicObject` instance. Becomes `nil` if the object has been deleted.
|
||||
*/
|
||||
public private(set) lazy var object: O? = self.context.fetchExisting(self.id)
|
||||
|
||||
|
||||
|
||||
// MARK: Public (Observers)
|
||||
|
||||
/**
|
||||
Registers an object as an observer to be notified when changes to the `ObjectPublisher`'s snapshot occur.
|
||||
|
||||
To prevent retain-cycles, `ObjectPublisher` only keeps `weak` references to its observers.
|
||||
|
||||
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
|
||||
|
||||
Calling `addObserver(_:_:)` multiple times on the same observer is safe.
|
||||
|
||||
- parameter observer: an object to become owner of the specified `callback`
|
||||
- parameter callback: the closure to execute when changes occur
|
||||
*/
|
||||
public func addObserver<T: AnyObject>(_ observer: T, _ callback: @escaping (ObjectPublisher<O>) -> Void) {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
"Attempted to add an observer of type \(Internals.typeName(observer)) outside the main thread."
|
||||
)
|
||||
self.observers.setObject(
|
||||
Internals.Closure(callback),
|
||||
forKey: observer
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Unregisters an object from receiving notifications for changes to the `ObjectPublisher`'s snapshot.
|
||||
|
||||
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
|
||||
|
||||
- parameter observer: the object whose notifications will be unregistered
|
||||
*/
|
||||
public func removeObserver<T: AnyObject>(_ observer: T) {
|
||||
|
||||
Internals.assert(
|
||||
Thread.isMainThread,
|
||||
"Attempted to remove an observer of type \(Internals.typeName(observer)) outside the main thread."
|
||||
)
|
||||
self.observers.removeObject(forKey: observer)
|
||||
}
|
||||
|
||||
|
||||
// MARK: ObjectRepresentation
|
||||
|
||||
public typealias ObjectType = O
|
||||
|
||||
public func objectID() -> O.ObjectID {
|
||||
|
||||
return self.id
|
||||
}
|
||||
|
||||
public func asPublisher(in dataStack: DataStack) -> ObjectPublisher<O> {
|
||||
|
||||
let context = dataStack.unsafeContext()
|
||||
if self.context == context {
|
||||
|
||||
return self
|
||||
}
|
||||
return context.objectPublisher(objectID: self.id)
|
||||
}
|
||||
|
||||
public func asReadOnly(in dataStack: DataStack) -> O? {
|
||||
|
||||
return dataStack.unsafeContext().fetchExisting(self.id)
|
||||
}
|
||||
|
||||
public func asEditable(in transaction: BaseDataTransaction) -> O? {
|
||||
|
||||
return transaction.unsafeContext().fetchExisting(self.id)
|
||||
}
|
||||
|
||||
public func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot<O>? {
|
||||
|
||||
let context = dataStack.unsafeContext()
|
||||
if self.context == context {
|
||||
|
||||
return self.lazySnapshot
|
||||
}
|
||||
return ObjectSnapshot<O>(objectID: self.id, context: context)
|
||||
}
|
||||
|
||||
public func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot<O>? {
|
||||
|
||||
let context = transaction.unsafeContext()
|
||||
if self.context == context {
|
||||
|
||||
return self.lazySnapshot
|
||||
}
|
||||
return ObjectSnapshot<O>(objectID: self.id, context: context)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (_ lhs: ObjectPublisher, _ rhs: ObjectPublisher) -> Bool {
|
||||
|
||||
return lhs.id == rhs.id
|
||||
&& lhs.context == rhs.context
|
||||
}
|
||||
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
|
||||
hasher.combine(self.id)
|
||||
hasher.combine(self.context)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal static func createUncached(objectID: O.ObjectID, context: NSManagedObjectContext) -> ObjectPublisher<O> {
|
||||
|
||||
return self.init(
|
||||
objectID: objectID,
|
||||
context: context,
|
||||
initializer: ObjectSnapshot<O>.init(objectID:context:)
|
||||
)
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
self.context.objectsDidChangeObserver(remove: self)
|
||||
self.observers.removeAllObjects()
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate let rawObjectWillChange: Any?
|
||||
|
||||
fileprivate init(objectID: O.ObjectID, context: NSManagedObjectContext, initializer: @escaping (NSManagedObjectID, NSManagedObjectContext) -> ObjectSnapshot<O>?) {
|
||||
|
||||
self.id = objectID
|
||||
self.context = context
|
||||
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||
|
||||
#if canImport(Combine)
|
||||
self.rawObjectWillChange = ObservableObjectPublisher()
|
||||
|
||||
#else
|
||||
self.rawObjectWillChange = nil
|
||||
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
||||
self.rawObjectWillChange = nil
|
||||
}
|
||||
self.$lazySnapshot.initialize { [weak self] in
|
||||
|
||||
guard let self = self else {
|
||||
|
||||
return initializer(objectID, context)
|
||||
}
|
||||
context.objectsDidChangeObserver(for: self).addObserver(self) { [weak self] (updatedIDs, deletedIDs) in
|
||||
|
||||
guard let self = self else {
|
||||
|
||||
return
|
||||
}
|
||||
if deletedIDs.contains(objectID) {
|
||||
|
||||
self.object = nil
|
||||
|
||||
self.willChange()
|
||||
self.$lazySnapshot.reset({ nil })
|
||||
self.didChange()
|
||||
self.notifyObservers()
|
||||
}
|
||||
else if updatedIDs.contains(objectID) {
|
||||
|
||||
self.willChange()
|
||||
self.$lazySnapshot.reset({ initializer(objectID, context) })
|
||||
self.didChange()
|
||||
self.notifyObservers()
|
||||
}
|
||||
}
|
||||
return initializer(objectID, context)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let id: O.ObjectID
|
||||
private let context: NSManagedObjectContext
|
||||
|
||||
@Internals.LazyNonmutating(uninitialized: ())
|
||||
private var lazySnapshot: ObjectSnapshot<O>?
|
||||
|
||||
private lazy var observers: NSMapTable<AnyObject, Internals.Closure<ObjectPublisher<O>, Void>> = .weakToStrongObjects()
|
||||
|
||||
private func notifyObservers() {
|
||||
|
||||
guard let enumerator = self.observers.objectEnumerator() else {
|
||||
|
||||
return
|
||||
}
|
||||
for closure in enumerator {
|
||||
|
||||
(closure as! Internals.Closure<ObjectPublisher
|
||||
<O>, Void>).invoke(with: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
extension ObjectPublisher: ObservableObject {}
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: - ObjectPublisher
|
||||
|
||||
extension ObjectPublisher {
|
||||
|
||||
// MARK: ObservableObject
|
||||
|
||||
#if canImport(Combine)
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
public var objectWillChange: ObservableObjectPublisher {
|
||||
|
||||
return self.rawObjectWillChange! as! ObservableObjectPublisher
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
fileprivate func willChange() {
|
||||
|
||||
guard #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) else {
|
||||
|
||||
return
|
||||
}
|
||||
#if canImport(Combine)
|
||||
|
||||
#if canImport(SwiftUI)
|
||||
|
||||
withAnimation {
|
||||
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
self.objectWillChange.send()
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
fileprivate func didChange() {
|
||||
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ObjectPublisher where O: NSManagedObject
|
||||
|
||||
extension ObjectPublisher where O: NSManagedObject {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
@available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)")
|
||||
public subscript<V: AllowedObjectiveCKeyPathValue>(dynamicMember member: KeyPath<O, V>) -> V {
|
||||
|
||||
fatalError()
|
||||
// return self.snapshot[dynamicMember: member]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public func value<V: AllowedObjectiveCKeyPathValue>(forKeyPath keyPath: KeyPath<O, V>) -> V! {
|
||||
|
||||
let key = String(keyPath: keyPath)
|
||||
return self.snapshot?.dictionaryForValues()[key] as! V?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ObjectPublisher where O: CoreStoreObject
|
||||
|
||||
extension ObjectPublisher where O: CoreStoreObject {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, V>(dynamicMember member: KeyPath<O, ValueContainer<OBase>.Required<V>>) -> V? {
|
||||
|
||||
return self.object?[keyPath: member].value
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, V>(dynamicMember member: KeyPath<O, ValueContainer<OBase>.Optional<V>>) -> V? {
|
||||
|
||||
return self.object?[keyPath: member].value
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, V>(dynamicMember member: KeyPath<O, TransformableContainer<OBase>.Required<V>>) -> V? {
|
||||
|
||||
return self.object?[keyPath: member].value
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, V>(dynamicMember member: KeyPath<O, TransformableContainer<OBase>.Optional<V>>) -> V? {
|
||||
|
||||
return self.object?[keyPath: member].value
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToOne<D>>) -> D? {
|
||||
|
||||
return self.object?[keyPath: member].value
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToManyOrdered<D>>) -> [D]? {
|
||||
|
||||
return self.object?[keyPath: member].value
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToManyUnordered<D>>) -> Set<D>? {
|
||||
|
||||
return self.object?[keyPath: member].value
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<V>(dynamicMember member: KeyPath<O, V>) -> V? {
|
||||
|
||||
return self.object?[keyPath: member]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
//
|
||||
// ObjectRepresentation.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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
|
||||
|
||||
|
||||
// MARK - ObjectRepresentation
|
||||
|
||||
/**
|
||||
An object that acts as interfaces for `CoreStoreObject`s or `NSManagedObject`s
|
||||
*/
|
||||
public protocol ObjectRepresentation {
|
||||
|
||||
/**
|
||||
The object type represented by this protocol
|
||||
*/
|
||||
associatedtype ObjectType: DynamicObject
|
||||
|
||||
/**
|
||||
The internal ID for the object.
|
||||
*/
|
||||
func objectID() -> ObjectType.ObjectID
|
||||
|
||||
/**
|
||||
An instance that may be observed for object changes.
|
||||
*/
|
||||
func asPublisher(in dataStack: DataStack) -> ObjectPublisher<ObjectType>
|
||||
|
||||
/**
|
||||
A read-only instance in the `DataStack`.
|
||||
*/
|
||||
func asReadOnly(in dataStack: DataStack) -> ObjectType?
|
||||
|
||||
/**
|
||||
An instance that may be mutated within a `BaseDataTransaction`.
|
||||
*/
|
||||
func asEditable(in transaction: BaseDataTransaction) -> ObjectType?
|
||||
|
||||
/**
|
||||
A thread-safe `struct` that is a full-copy of the object's properties
|
||||
*/
|
||||
func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot<ObjectType>?
|
||||
|
||||
/**
|
||||
A thread-safe `struct` that is a full-copy of the object's properties
|
||||
*/
|
||||
func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot<ObjectType>?
|
||||
}
|
||||
|
||||
extension NSManagedObject: ObjectRepresentation {}
|
||||
|
||||
extension CoreStoreObject: ObjectRepresentation {}
|
||||
|
||||
extension DynamicObject where Self: ObjectRepresentation {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
An `ObjectPublisher` wrapper for the exact same object
|
||||
*/
|
||||
public func asPublisher() -> ObjectPublisher<Self>? {
|
||||
|
||||
return self.cs_toRaw()
|
||||
.managedObjectContext
|
||||
.map({ $0.objectPublisher(objectID: self.cs_id()) })
|
||||
}
|
||||
|
||||
/**
|
||||
A thread-safe `struct` that is a full-copy of the object's properties
|
||||
*/
|
||||
public func asSnapshot() -> ObjectSnapshot<Self>? {
|
||||
|
||||
return self.cs_toRaw()
|
||||
.managedObjectContext
|
||||
.flatMap({ ObjectSnapshot<Self>(objectID: self.cs_id(), context: $0) })
|
||||
}
|
||||
|
||||
|
||||
// MARK: ObjectRepresentation
|
||||
|
||||
public func objectID() -> Self.ObjectID {
|
||||
|
||||
return self.cs_id()
|
||||
}
|
||||
|
||||
public func asPublisher(in dataStack: DataStack) -> ObjectPublisher<Self> {
|
||||
|
||||
let context = dataStack.unsafeContext()
|
||||
return context.objectPublisher(objectID: self.cs_id())
|
||||
}
|
||||
|
||||
public func asReadOnly(in dataStack: DataStack) -> Self? {
|
||||
|
||||
let context = dataStack.unsafeContext()
|
||||
if self.cs_toRaw().managedObjectContext == context {
|
||||
|
||||
return self
|
||||
}
|
||||
return context.fetchExisting(self.cs_id())
|
||||
}
|
||||
|
||||
public func asEditable(in transaction: BaseDataTransaction) -> Self? {
|
||||
|
||||
let context = transaction.unsafeContext()
|
||||
if self.cs_toRaw().managedObjectContext == context {
|
||||
|
||||
return self
|
||||
}
|
||||
return context.fetchExisting(self.cs_id())
|
||||
}
|
||||
|
||||
public func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot<Self>? {
|
||||
|
||||
let context = dataStack.unsafeContext()
|
||||
return ObjectSnapshot<Self>(objectID: self.cs_id(), context: context)
|
||||
}
|
||||
|
||||
public func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot<Self>? {
|
||||
|
||||
let context = transaction.unsafeContext()
|
||||
return ObjectSnapshot<Self>(objectID: self.cs_id(), context: context)
|
||||
}
|
||||
}
|
||||
+236
-75
@@ -24,58 +24,149 @@
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import Foundation
|
||||
|
||||
#if canImport(UIKit)
|
||||
import UIKit
|
||||
|
||||
#elseif canImport(AppKit)
|
||||
import AppKit
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - ObjectSnapshot
|
||||
|
||||
/**
|
||||
An `ObjectSnapshot` contains "snapshot" values from a `DynamicObject` instance copied at a specific point in time.
|
||||
The `ObjectSnapshot` is a full copy of a `DynamicObject`'s properties at a given point in time. This is useful especially when keeping thread-safe state values, in ViewModels for example. Since this is a value type, any changes in this `struct` does not affect the actual object.
|
||||
*/
|
||||
@dynamicMemberLookup
|
||||
public struct ObjectSnapshot<O: DynamicObject> {
|
||||
public struct ObjectSnapshot<O: DynamicObject>: ObjectRepresentation, Hashable {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func dictionaryForValues() -> [String: Any] {
|
||||
|
||||
return self.values
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
// MARK: ObjectRepresentation
|
||||
|
||||
public typealias ObjectType = O
|
||||
|
||||
fileprivate var attributes: [KeyPathString: Any]
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private init() {
|
||||
public func objectID() -> O.ObjectID {
|
||||
|
||||
self.attributes = [:]
|
||||
return self.id
|
||||
}
|
||||
|
||||
public func asPublisher(in dataStack: DataStack) -> ObjectPublisher<O> {
|
||||
|
||||
let context = dataStack.unsafeContext()
|
||||
return context.objectPublisher(objectID: self.id)
|
||||
}
|
||||
|
||||
public func asReadOnly(in dataStack: DataStack) -> O? {
|
||||
|
||||
return dataStack.unsafeContext().fetchExisting(self.id)
|
||||
}
|
||||
|
||||
public func asEditable(in transaction: BaseDataTransaction) -> O? {
|
||||
|
||||
return transaction.unsafeContext().fetchExisting(self.id)
|
||||
}
|
||||
|
||||
public func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot<O>? {
|
||||
|
||||
let context = dataStack.unsafeContext()
|
||||
return ObjectSnapshot<O>(objectID: self.id, context: context)
|
||||
}
|
||||
|
||||
public func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot<O>? {
|
||||
|
||||
let context = transaction.unsafeContext()
|
||||
return ObjectSnapshot<O>(objectID: self.id, context: context)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (_ lhs: Self, _ rhs: Self) -> Bool {
|
||||
|
||||
return lhs.id == rhs.id
|
||||
&& lhs.valuesRef == rhs.valuesRef
|
||||
}
|
||||
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
|
||||
hasher.combine(self.id)
|
||||
hasher.combine(self.valuesRef)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init?(objectID: O.ObjectID, context: NSManagedObjectContext) {
|
||||
|
||||
guard let values = O.cs_snapshotDictionary(id: objectID, context: context) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
self.id = objectID
|
||||
self.context = context
|
||||
self.values = values
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate var values: [String: Any]
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let id: O.ObjectID
|
||||
private let context: NSManagedObjectContext
|
||||
|
||||
private var valuesRef: NSDictionary {
|
||||
|
||||
return self.values as NSDictionary
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ObjectSnapshot where O: NSManagedObject
|
||||
|
||||
extension ObjectSnapshot where O: NSManagedObject {
|
||||
|
||||
/**
|
||||
Initializes an `ObjectSnapshot` instance by copying all attribute values from the given `NSManagedObject`.
|
||||
*/
|
||||
public init(from object: O) {
|
||||
|
||||
self.attributes = object.dictionaryWithValues(
|
||||
forKeys: Array(object.entity.attributesByName.keys)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
@available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)")
|
||||
public subscript<V: AllowedObjectiveCKeyPathValue>(dynamicMember member: KeyPath<O, V>) -> V {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.attributes[key]! as! V
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.attributes[key] = newValue
|
||||
}
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! V
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public func value<V: AllowedObjectiveCKeyPathValue>(forKeyPath keyPath: KeyPath<O, V>) -> V! {
|
||||
|
||||
let key = String(keyPath: keyPath)
|
||||
return self.values[key] as! V?
|
||||
}
|
||||
|
||||
/**
|
||||
Mutates the value for the property identified by a given key.
|
||||
*/
|
||||
public mutating func setValue<V: AllowedObjectiveCKeyPathValue>(_ value: V!, forKeyPath keyPath: KeyPath<O, V>) {
|
||||
|
||||
let key = String(keyPath: keyPath)
|
||||
self.values[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,61 +174,131 @@ extension ObjectSnapshot where O: NSManagedObject {
|
||||
// MARK: - ObjectSnapshot where O: CoreStoreObject
|
||||
|
||||
extension ObjectSnapshot where O: CoreStoreObject {
|
||||
|
||||
/**
|
||||
Initializes an `ObjectSnapshot` instance by copying all attribute values from the given `CoreStoreObject`.
|
||||
*/
|
||||
public init(from object: O) {
|
||||
|
||||
var attributes: [KeyPathString: Any] = [:]
|
||||
Self.initializeAttributes(
|
||||
mirror: Mirror(reflecting: object),
|
||||
object: object,
|
||||
into: &attributes
|
||||
)
|
||||
self.attributes = attributes
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<K: AttributeKeyPathStringConvertible>(dynamicMember member: KeyPath<O, K>) -> K.ReturnValueType {
|
||||
|
||||
public subscript<OBase, V>(dynamicMember member: KeyPath<O, ValueContainer<OBase>.Required<V>>) -> V {
|
||||
|
||||
get {
|
||||
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.attributes[key]! as! K.ReturnValueType
|
||||
return self.values[key] as! V
|
||||
}
|
||||
set {
|
||||
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.attributes[key] = newValue
|
||||
self.values[key] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private static func initializeAttributes(mirror: Mirror, object: CoreStoreObject, into attributes: inout [KeyPathString: Any]) {
|
||||
|
||||
if let superClassMirror = mirror.superclassMirror {
|
||||
|
||||
self.initializeAttributes(
|
||||
mirror: superClassMirror,
|
||||
object: object,
|
||||
into: &attributes
|
||||
)
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, V>(dynamicMember member: KeyPath<O, ValueContainer<OBase>.Optional<V>>) -> V? {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as? V
|
||||
}
|
||||
for child in mirror.children {
|
||||
|
||||
switch child.value {
|
||||
|
||||
case let property as AttributeProtocol:
|
||||
attributes[property.keyPath] = property.valueForSnapshot
|
||||
|
||||
default:
|
||||
continue
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.values[key] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, V>(dynamicMember member: KeyPath<O, TransformableContainer<OBase>.Required<V>>) -> V {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! V
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.values[key] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, V>(dynamicMember member: KeyPath<O, TransformableContainer<OBase>.Optional<V>>) -> V? {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as? V
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.values[key] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToOne<D>>) -> ObjectPublisher<D>? {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
guard let id = self.values[key] as? D.ObjectID else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return self.context.objectPublisher(objectID: id)
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.values[key] = newValue?.objectID()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToManyOrdered<D>>) -> [ObjectPublisher<D>] {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
let context = self.context
|
||||
let ids = self.values[key] as! [D.ObjectID]
|
||||
return ids.map(context.objectPublisher(objectID:))
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.values[key] = newValue.map({ $0.objectID() })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToManyUnordered<D>>) -> Set<ObjectPublisher<D>> {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
let context = self.context
|
||||
let ids = self.values[key] as! Set<D.ObjectID>
|
||||
return Set(ids.map(context.objectPublisher(objectID:)))
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.values[key] = Set(newValue.map({ $0.objectID() }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
-26
@@ -32,7 +32,7 @@ import CoreData
|
||||
/**
|
||||
The `OrderBy` clause specifies the sort order for results for a fetch or a query.
|
||||
*/
|
||||
public struct OrderBy<D: DynamicObject>: OrderByClause, FetchClause, QueryClause, DeleteClause, Hashable {
|
||||
public struct OrderBy<O: DynamicObject>: OrderByClause, FetchClause, QueryClause, DeleteClause, Hashable {
|
||||
|
||||
/**
|
||||
Combines two `OrderBy` sort descriptors together
|
||||
@@ -102,7 +102,7 @@ public struct OrderBy<D: DynamicObject>: OrderByClause, FetchClause, QueryClause
|
||||
|
||||
// MARK: OrderByClause
|
||||
|
||||
public typealias ObjectType = D
|
||||
public typealias ObjectType = O
|
||||
|
||||
public let sortDescriptors: [NSSortDescriptor]
|
||||
|
||||
@@ -170,7 +170,7 @@ public struct OrderBy<D: DynamicObject>: OrderByClause, FetchClause, QueryClause
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in ascending order
|
||||
*/
|
||||
public static func ascending<T>(_ keyPath: KeyPath<D, T>) -> SortKey where D: NSManagedObject {
|
||||
public static func ascending<T>(_ keyPath: KeyPath<O, T>) -> SortKey where O: NSManagedObject {
|
||||
|
||||
return .ascending(keyPath._kvcKeyPathString!)
|
||||
}
|
||||
@@ -178,7 +178,7 @@ public struct OrderBy<D: DynamicObject>: OrderByClause, FetchClause, QueryClause
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in descending order
|
||||
*/
|
||||
public static func descending<T>(_ keyPath: KeyPath<D, T>) -> SortKey where D: NSManagedObject {
|
||||
public static func descending<T>(_ keyPath: KeyPath<O, T>) -> SortKey where O: NSManagedObject {
|
||||
|
||||
return .descending(keyPath._kvcKeyPathString!)
|
||||
}
|
||||
@@ -189,65 +189,65 @@ public struct OrderBy<D: DynamicObject>: OrderByClause, FetchClause, QueryClause
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in ascending order
|
||||
*/
|
||||
public static func ascending<T>(_ attribute: KeyPath<D, ValueContainer<D>.Required<T>>) -> SortKey {
|
||||
public static func ascending<T>(_ attribute: KeyPath<O, ValueContainer<O>.Required<T>>) -> SortKey {
|
||||
|
||||
return .ascending(D.meta[keyPath: attribute].keyPath)
|
||||
return .ascending(O.meta[keyPath: attribute].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in ascending order
|
||||
*/
|
||||
public static func ascending<T>(_ attribute: KeyPath<D, ValueContainer<D>.Optional<T>>) -> SortKey {
|
||||
public static func ascending<T>(_ attribute: KeyPath<O, ValueContainer<O>.Optional<T>>) -> SortKey {
|
||||
|
||||
return .ascending(D.meta[keyPath: attribute].keyPath)
|
||||
return .ascending(O.meta[keyPath: attribute].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in ascending order
|
||||
*/
|
||||
public static func ascending<T>(_ attribute: KeyPath<D, TransformableContainer<D>.Required<T>>) -> SortKey {
|
||||
public static func ascending<T>(_ attribute: KeyPath<O, TransformableContainer<O>.Required<T>>) -> SortKey {
|
||||
|
||||
return .ascending(D.meta[keyPath: attribute].keyPath)
|
||||
return .ascending(O.meta[keyPath: attribute].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in ascending order
|
||||
*/
|
||||
public static func ascending<T>(_ attribute: KeyPath<D, TransformableContainer<D>.Optional<T>>) -> SortKey {
|
||||
public static func ascending<T>(_ attribute: KeyPath<O, TransformableContainer<O>.Optional<T>>) -> SortKey {
|
||||
|
||||
return .ascending(D.meta[keyPath: attribute].keyPath)
|
||||
return .ascending(O.meta[keyPath: attribute].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in descending order
|
||||
*/
|
||||
public static func descending<T>(_ attribute: KeyPath<D, ValueContainer<D>.Required<T>>) -> SortKey {
|
||||
public static func descending<T>(_ attribute: KeyPath<O, ValueContainer<O>.Required<T>>) -> SortKey {
|
||||
|
||||
return .descending(D.meta[keyPath: attribute].keyPath)
|
||||
return .descending(O.meta[keyPath: attribute].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in descending order
|
||||
*/
|
||||
public static func descending<T>(_ attribute: KeyPath<D, ValueContainer<D>.Optional<T>>) -> SortKey {
|
||||
public static func descending<T>(_ attribute: KeyPath<O, ValueContainer<O>.Optional<T>>) -> SortKey {
|
||||
|
||||
return .descending(D.meta[keyPath: attribute].keyPath)
|
||||
return .descending(O.meta[keyPath: attribute].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in descending order
|
||||
*/
|
||||
public static func descending<T>(_ attribute: KeyPath<D, TransformableContainer<D>.Required<T>>) -> SortKey {
|
||||
public static func descending<T>(_ attribute: KeyPath<O, TransformableContainer<O>.Required<T>>) -> SortKey {
|
||||
|
||||
return .descending(D.meta[keyPath: attribute].keyPath)
|
||||
return .descending(O.meta[keyPath: attribute].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in descending order
|
||||
*/
|
||||
public static func descending<T>(_ attribute: KeyPath<D, TransformableContainer<D>.Optional<T>>) -> SortKey {
|
||||
public static func descending<T>(_ attribute: KeyPath<O, TransformableContainer<O>.Optional<T>>) -> SortKey {
|
||||
|
||||
return .descending(D.meta[keyPath: attribute].keyPath)
|
||||
return .descending(O.meta[keyPath: attribute].keyPath)
|
||||
}
|
||||
|
||||
|
||||
@@ -255,27 +255,33 @@ public struct OrderBy<D: DynamicObject>: OrderByClause, FetchClause, QueryClause
|
||||
|
||||
fileprivate let descriptor: NSSortDescriptor
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
|
||||
// MARK: - OrderBy.SortKey where D: CoreStoreObject
|
||||
// MARK: - OrderBy.SortKey where O: CoreStoreObject
|
||||
|
||||
extension OrderBy.SortKey where D: CoreStoreObject {
|
||||
extension OrderBy.SortKey where O: CoreStoreObject {
|
||||
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in ascending order
|
||||
*/
|
||||
public static func ascending<K: KeyPathStringConvertible>(_ attribute: (D) -> K) -> OrderBy<D>.SortKey {
|
||||
public static func ascending<K: KeyPathStringConvertible>(_ attribute: (O) -> K) -> OrderBy<O>.SortKey {
|
||||
|
||||
return .ascending(attribute(D.meta).cs_keyPathString)
|
||||
return .ascending(attribute(O.meta).cs_keyPathString)
|
||||
}
|
||||
|
||||
/**
|
||||
Indicates that the `KeyPathString` should be sorted in descending order
|
||||
*/
|
||||
public static func descending<K: KeyPathStringConvertible>(_ attribute: (D) -> K) -> OrderBy<D>.SortKey {
|
||||
public static func descending<K: KeyPathStringConvertible>(_ attribute: (O) -> K) -> OrderBy<O>.SortKey {
|
||||
|
||||
return .descending(attribute(D.meta).cs_keyPathString)
|
||||
return .descending(attribute(O.meta).cs_keyPathString)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// PropertyProtocol.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 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: - PropertyProtocol
|
||||
|
||||
internal protocol PropertyProtocol: AnyObject {
|
||||
|
||||
var keyPath: KeyPathString { get }
|
||||
}
|
||||
@@ -39,16 +39,22 @@ import CoreData
|
||||
)
|
||||
```
|
||||
*/
|
||||
public struct QueryChainBuilder<D: DynamicObject, R: SelectResultType>: QueryChainableBuilderType {
|
||||
public struct QueryChainBuilder<O: DynamicObject, R: SelectResultType>: QueryChainableBuilderType {
|
||||
|
||||
// MARK: QueryChainableBuilderType
|
||||
|
||||
public typealias ObjectType = D
|
||||
public typealias ObjectType = O
|
||||
public typealias ResultType = R
|
||||
|
||||
public var from: From<D>
|
||||
public var select: Select<D, R>
|
||||
public var from: From<O>
|
||||
public var select: Select<O, R>
|
||||
public var queryClauses: [QueryClause] = []
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public protocol QueryableSource: AnyObject {
|
||||
- returns: the result of the the query, or `nil` if no match was found. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: QueryClause...) throws -> U?
|
||||
func queryValue<O, U: QueryableAttributeType>(_ from: From<O>, _ selectClause: Select<O, U>, _ queryClauses: QueryClause...) throws -> U?
|
||||
|
||||
/**
|
||||
Queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
|
||||
@@ -58,7 +58,7 @@ public protocol QueryableSource: AnyObject {
|
||||
- returns: the result of the the query, or `nil` if no match was found. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: [QueryClause]) throws -> U?
|
||||
func queryValue<O, U: QueryableAttributeType>(_ from: From<O>, _ selectClause: Select<O, U>, _ queryClauses: [QueryClause]) throws -> U?
|
||||
|
||||
/**
|
||||
Queries a property value or aggregate as specified by the `QueryChainableBuilderType` built from a chain of clauses.
|
||||
@@ -88,7 +88,7 @@ public protocol QueryableSource: AnyObject {
|
||||
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: QueryClause...) throws -> [[String: Any]]
|
||||
func queryAttributes<O>(_ from: From<O>, _ selectClause: Select<O, NSDictionary>, _ queryClauses: QueryClause...) throws -> [[String: Any]]
|
||||
|
||||
/**
|
||||
Queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
|
||||
@@ -101,7 +101,7 @@ public protocol QueryableSource: AnyObject {
|
||||
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
|
||||
- throws: `CoreStoreError.persistentStoreNotFound` if the specified entity could not be found in any store's schema.
|
||||
*/
|
||||
func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: [QueryClause]) throws -> [[String: Any]]
|
||||
func queryAttributes<O>(_ from: From<O>, _ selectClause: Select<O, NSDictionary>, _ queryClauses: [QueryClause]) throws -> [[String: Any]]
|
||||
|
||||
/**
|
||||
Queries a dictionary of attribute values or as specified by the `QueryChainableBuilderType` built from a chain of clauses.
|
||||
|
||||
@@ -249,11 +249,15 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
// MARK: RelationshipKeyPathStringConvertible
|
||||
|
||||
public typealias ReturnValueType = DestinationValueType?
|
||||
|
||||
|
||||
// MARK: PropertyProtocol
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
|
||||
|
||||
// MARK: RelationshipProtocol
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
|
||||
internal let isToMany = false
|
||||
internal let isOrdered = false
|
||||
internal let deleteRule: NSDeleteRule
|
||||
@@ -308,6 +312,11 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return self.value?.objectID()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
@@ -535,11 +544,15 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
// MARK: RelationshipKeyPathStringConvertible
|
||||
|
||||
public typealias ReturnValueType = [DestinationValueType]
|
||||
|
||||
|
||||
// MARK: PropertyProtocol
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
|
||||
|
||||
// MARK: RelationshipProtocol
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
|
||||
internal let isToMany = true
|
||||
internal let isOptional = true
|
||||
internal let isOrdered = true
|
||||
@@ -595,6 +608,11 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return self.value.map({ $0.objectID() })
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
@@ -827,11 +845,15 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
// MARK: RelationshipKeyPathStringConvertible
|
||||
|
||||
public typealias ReturnValueType = Set<DestinationValueType>
|
||||
|
||||
|
||||
// MARK: PropertyProtocol
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
|
||||
|
||||
// MARK: RelationshipProtocol
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
internal let isToMany = true
|
||||
internal let isOptional = true
|
||||
internal let isOrdered = false
|
||||
@@ -887,6 +909,11 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return Set(self.value.map({ $0.objectID() }))
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@@ -29,9 +29,8 @@ import CoreData
|
||||
|
||||
// MARK: - RelationshipProtocol
|
||||
|
||||
internal protocol RelationshipProtocol: AnyObject {
|
||||
|
||||
var keyPath: KeyPathString { get }
|
||||
internal protocol RelationshipProtocol: PropertyProtocol {
|
||||
|
||||
var isToMany: Bool { get }
|
||||
var isOrdered: Bool { get }
|
||||
var deleteRule: NSDeleteRule { get }
|
||||
@@ -42,4 +41,5 @@ internal protocol RelationshipProtocol: AnyObject {
|
||||
var renamingIdentifier: () -> String? { get }
|
||||
var minCount: Int { get }
|
||||
var maxCount: Int { get }
|
||||
var valueForSnapshot: Any? { get }
|
||||
}
|
||||
|
||||
+24
-18
@@ -32,7 +32,7 @@ import CoreData
|
||||
/**
|
||||
The `SectionBy` clause indicates the key path to use to group the `ListMonitor` objects into sections. An optional closure can also be provided to transform the value into an appropriate section name:
|
||||
```
|
||||
let monitor = CoreStore.monitorSectionedList(
|
||||
let monitor = dataStack.monitorSectionedList(
|
||||
From<Person>(),
|
||||
SectionBy("age") { "Age \($0)" },
|
||||
OrderBy(.ascending("lastName"))
|
||||
@@ -40,7 +40,7 @@ import CoreData
|
||||
```
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public struct SectionBy<D: DynamicObject> {
|
||||
public struct SectionBy<O: DynamicObject> {
|
||||
|
||||
/**
|
||||
Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections
|
||||
@@ -70,17 +70,23 @@ public struct SectionBy<D: DynamicObject> {
|
||||
|
||||
internal let sectionKeyPath: KeyPathString
|
||||
internal let sectionIndexTransformer: (_ sectionName: String?) -> String?
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
@available(macOS 10.12, *)
|
||||
extension SectionBy where D: NSManagedObject {
|
||||
extension SectionBy where O: NSManagedObject {
|
||||
|
||||
/**
|
||||
Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections
|
||||
|
||||
- parameter sectionKeyPath: the key path to use to group the objects into sections
|
||||
*/
|
||||
public init<T>(_ sectionKeyPath: KeyPath<D, T>) {
|
||||
public init<T>(_ sectionKeyPath: KeyPath<O, T>) {
|
||||
|
||||
self.init(sectionKeyPath, { $0 })
|
||||
}
|
||||
@@ -92,21 +98,21 @@ extension SectionBy where D: NSManagedObject {
|
||||
- parameter sectionKeyPath: the key path to use to group the objects into sections
|
||||
- parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name
|
||||
*/
|
||||
public init<T>(_ sectionKeyPath: KeyPath<D, T>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) {
|
||||
public init<T>(_ sectionKeyPath: KeyPath<O, T>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) {
|
||||
|
||||
self.init(sectionKeyPath._kvcKeyPathString!, sectionIndexTransformer)
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 10.12, *)
|
||||
extension SectionBy where D: CoreStoreObject {
|
||||
extension SectionBy where O: CoreStoreObject {
|
||||
|
||||
/**
|
||||
Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections
|
||||
|
||||
- parameter sectionKeyPath: the key path to use to group the objects into sections
|
||||
*/
|
||||
public init<T>(_ sectionKeyPath: KeyPath<D, ValueContainer<D>.Required<T>>) {
|
||||
public init<T>(_ sectionKeyPath: KeyPath<O, ValueContainer<O>.Required<T>>) {
|
||||
|
||||
self.init(sectionKeyPath, { $0 })
|
||||
}
|
||||
@@ -116,7 +122,7 @@ extension SectionBy where D: CoreStoreObject {
|
||||
|
||||
- parameter sectionKeyPath: the key path to use to group the objects into sections
|
||||
*/
|
||||
public init<T>(_ sectionKeyPath: KeyPath<D, ValueContainer<D>.Optional<T>>) {
|
||||
public init<T>(_ sectionKeyPath: KeyPath<O, ValueContainer<O>.Optional<T>>) {
|
||||
|
||||
self.init(sectionKeyPath, { $0 })
|
||||
}
|
||||
@@ -126,7 +132,7 @@ extension SectionBy where D: CoreStoreObject {
|
||||
|
||||
- parameter sectionKeyPath: the key path to use to group the objects into sections
|
||||
*/
|
||||
public init<T>(_ sectionKeyPath: KeyPath<D, TransformableContainer<D>.Required<T>>) {
|
||||
public init<T>(_ sectionKeyPath: KeyPath<O, TransformableContainer<O>.Required<T>>) {
|
||||
|
||||
self.init(sectionKeyPath, { $0 })
|
||||
}
|
||||
@@ -136,7 +142,7 @@ extension SectionBy where D: CoreStoreObject {
|
||||
|
||||
- parameter sectionKeyPath: the key path to use to group the objects into sections
|
||||
*/
|
||||
public init<T>(_ sectionKeyPath: KeyPath<D, TransformableContainer<D>.Optional<T>>) {
|
||||
public init<T>(_ sectionKeyPath: KeyPath<O, TransformableContainer<O>.Optional<T>>) {
|
||||
|
||||
self.init(sectionKeyPath, { $0 })
|
||||
}
|
||||
@@ -148,9 +154,9 @@ extension SectionBy where D: CoreStoreObject {
|
||||
- parameter sectionKeyPath: the key path to use to group the objects into sections
|
||||
- parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name
|
||||
*/
|
||||
public init<T>(_ sectionKeyPath: KeyPath<D, ValueContainer<D>.Required<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) {
|
||||
public init<T>(_ sectionKeyPath: KeyPath<O, ValueContainer<O>.Required<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) {
|
||||
|
||||
self.init(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
self.init(O.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,9 +166,9 @@ extension SectionBy where D: CoreStoreObject {
|
||||
- parameter sectionKeyPath: the key path to use to group the objects into sections
|
||||
- parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name
|
||||
*/
|
||||
public init<T>(_ sectionKeyPath: KeyPath<D, ValueContainer<D>.Optional<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) {
|
||||
public init<T>(_ sectionKeyPath: KeyPath<O, ValueContainer<O>.Optional<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) {
|
||||
|
||||
self.init(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
self.init(O.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,9 +178,9 @@ extension SectionBy where D: CoreStoreObject {
|
||||
- parameter sectionKeyPath: the key path to use to group the objects into sections
|
||||
- parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name
|
||||
*/
|
||||
public init<T>(_ sectionKeyPath: KeyPath<D, TransformableContainer<D>.Required<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) {
|
||||
public init<T>(_ sectionKeyPath: KeyPath<O, TransformableContainer<O>.Required<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) {
|
||||
|
||||
self.init(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
self.init(O.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,8 +190,8 @@ extension SectionBy where D: CoreStoreObject {
|
||||
- parameter sectionKeyPath: the key path to use to group the objects into sections
|
||||
- parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name
|
||||
*/
|
||||
public init<T>(_ sectionKeyPath: KeyPath<D, TransformableContainer<D>.Optional<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) {
|
||||
public init<T>(_ sectionKeyPath: KeyPath<O, TransformableContainer<O>.Optional<T>>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) {
|
||||
|
||||
self.init(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
self.init(O.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,13 +41,19 @@ import CoreData
|
||||
```
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public struct SectionMonitorChainBuilder<D: DynamicObject>: SectionMonitorBuilderType {
|
||||
public struct SectionMonitorChainBuilder<O: DynamicObject>: SectionMonitorBuilderType {
|
||||
|
||||
// MARK: SectionMonitorBuilderType
|
||||
|
||||
public var from: From<D>
|
||||
public var sectionBy: SectionBy<D>
|
||||
public var from: From<O>
|
||||
public var sectionBy: SectionBy<O>
|
||||
public var fetchClauses: [FetchClause] = []
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
|
||||
|
||||
+111
-99
@@ -52,12 +52,12 @@ public protocol SelectAttributesResultType: SelectResultType {
|
||||
/**
|
||||
The `SelectTerm` is passed to the `Select` clause to indicate the attributes/aggregate keys to be queried.
|
||||
*/
|
||||
public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
public enum SelectTerm<O: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying an entity attribute. A shorter way to do the same is to assign from the string keypath directly:
|
||||
```
|
||||
let fullName = CoreStore.queryValue(
|
||||
let fullName = dataStack.queryValue(
|
||||
From<MyPersonEntity>(),
|
||||
Select<String>(.attribute("fullName")),
|
||||
Where("employeeID", isEqualTo: 1111)
|
||||
@@ -65,7 +65,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
```
|
||||
is equivalent to:
|
||||
```
|
||||
let fullName = CoreStore.queryValue(
|
||||
let fullName = dataStack.queryValue(
|
||||
From<MyPersonEntity>(),
|
||||
Select<String>("fullName"),
|
||||
Where("employeeID", isEqualTo: 1111)
|
||||
@@ -74,7 +74,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
- parameter keyPath: the attribute name
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
|
||||
*/
|
||||
public static func attribute(_ keyPath: KeyPathString) -> SelectTerm<D> {
|
||||
public static func attribute(_ keyPath: KeyPathString) -> SelectTerm<O> {
|
||||
|
||||
return ._attribute(keyPath)
|
||||
}
|
||||
@@ -82,7 +82,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute.
|
||||
```
|
||||
let averageAge = CoreStore.queryValue(
|
||||
let averageAge = dataStack.queryValue(
|
||||
From<MyPersonEntity>(),
|
||||
Select<Int>(.average("age"))
|
||||
)
|
||||
@@ -91,7 +91,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
|
||||
*/
|
||||
public static func average(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func average(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return ._aggregate(
|
||||
function: "average:",
|
||||
@@ -104,7 +104,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for a count query.
|
||||
```
|
||||
let numberOfEmployees = CoreStore.queryValue(
|
||||
let numberOfEmployees = dataStack.queryValue(
|
||||
From<MyPersonEntity>(),
|
||||
Select<Int>(.count("employeeID"))
|
||||
)
|
||||
@@ -113,7 +113,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for a count query
|
||||
*/
|
||||
public static func count(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func count(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return ._aggregate(
|
||||
function: "count:",
|
||||
@@ -126,7 +126,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute.
|
||||
```
|
||||
let maximumAge = CoreStore.queryValue(
|
||||
let maximumAge = dataStack.queryValue(
|
||||
From<MyPersonEntity>(),
|
||||
Select<Int>(.maximum("age"))
|
||||
)
|
||||
@@ -135,7 +135,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
|
||||
*/
|
||||
public static func maximum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func maximum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return ._aggregate(
|
||||
function: "max:",
|
||||
@@ -148,7 +148,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute.
|
||||
```
|
||||
let minimumAge = CoreStore.queryValue(
|
||||
let minimumAge = dataStack.queryValue(
|
||||
From<MyPersonEntity>(),
|
||||
Select<Int>(.minimum("age"))
|
||||
)
|
||||
@@ -157,7 +157,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
|
||||
*/
|
||||
public static func minimum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func minimum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return ._aggregate(
|
||||
function: "min:",
|
||||
@@ -170,7 +170,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute.
|
||||
```
|
||||
let totalAge = CoreStore.queryValue(
|
||||
let totalAge = dataStack.queryValue(
|
||||
From<MyPersonEntity>(),
|
||||
Select<Int>(.sum("age"))
|
||||
)
|
||||
@@ -179,7 +179,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func sum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func sum(_ keyPath: KeyPathString, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return ._aggregate(
|
||||
function: "sum:",
|
||||
@@ -192,7 +192,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the `NSManagedObjectID`.
|
||||
```
|
||||
let objectID = CoreStore.queryValue(
|
||||
let objectID = dataStack.queryValue(
|
||||
From<MyPersonEntity>(),
|
||||
Select<NSManagedObjectID>(),
|
||||
Where("employeeID", isEqualTo: 1111)
|
||||
@@ -202,7 +202,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "objecID" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func objectID(as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func objectID(as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return ._identity(
|
||||
alias: alias ?? "objectID",
|
||||
@@ -231,7 +231,7 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (lhs: SelectTerm<D>, rhs: SelectTerm<D>) -> Bool {
|
||||
public static func == (lhs: SelectTerm<O>, rhs: SelectTerm<O>) -> Bool {
|
||||
|
||||
switch (lhs, rhs) {
|
||||
|
||||
@@ -296,19 +296,25 @@ public enum SelectTerm<D: DynamicObject>: ExpressibleByStringLiteral, Hashable {
|
||||
case ._identity(let alias, _): return alias
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
|
||||
// MARK: - SelectTerm where D: NSManagedObject
|
||||
// MARK: - SelectTerm where O: NSManagedObject
|
||||
|
||||
extension SelectTerm where D: NSManagedObject {
|
||||
extension SelectTerm where O: NSManagedObject {
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying an entity attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
|
||||
*/
|
||||
public static func attribute<V>(_ keyPath: KeyPath<D, V>) -> SelectTerm<D> {
|
||||
public static func attribute<V>(_ keyPath: KeyPath<O, V>) -> SelectTerm<O> {
|
||||
|
||||
return self.attribute(keyPath._kvcKeyPathString!)
|
||||
}
|
||||
@@ -319,7 +325,7 @@ extension SelectTerm where D: NSManagedObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
|
||||
*/
|
||||
public static func average<V>(_ keyPath: KeyPath<D, V>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func average<V>(_ keyPath: KeyPath<O, V>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.average(keyPath._kvcKeyPathString!, as: alias)
|
||||
}
|
||||
@@ -330,7 +336,7 @@ extension SelectTerm where D: NSManagedObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for a count query
|
||||
*/
|
||||
public static func count<V>(_ keyPath: KeyPath<D, V>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func count<V>(_ keyPath: KeyPath<O, V>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.count(keyPath._kvcKeyPathString!, as: alias)
|
||||
}
|
||||
@@ -341,7 +347,7 @@ extension SelectTerm where D: NSManagedObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
|
||||
*/
|
||||
public static func maximum<V>(_ keyPath: KeyPath<D, V>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func maximum<V>(_ keyPath: KeyPath<O, V>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.maximum(keyPath._kvcKeyPathString!, as: alias)
|
||||
}
|
||||
@@ -352,7 +358,7 @@ extension SelectTerm where D: NSManagedObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
|
||||
*/
|
||||
public static func minimum<V>(_ keyPath: KeyPath<D, V>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func minimum<V>(_ keyPath: KeyPath<O, V>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.minimum(keyPath._kvcKeyPathString!, as: alias)
|
||||
}
|
||||
@@ -363,25 +369,25 @@ extension SelectTerm where D: NSManagedObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func sum<V>(_ keyPath: KeyPath<D, V>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func sum<V>(_ keyPath: KeyPath<O, V>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.sum(keyPath._kvcKeyPathString!, as: alias)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - SelectTerm where D: CoreStoreObject
|
||||
// MARK: - SelectTerm where O: CoreStoreObject
|
||||
|
||||
extension SelectTerm where D: CoreStoreObject {
|
||||
extension SelectTerm where O: CoreStoreObject {
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying an entity attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
|
||||
*/
|
||||
public static func attribute<V>(_ keyPath: KeyPath<D, ValueContainer<D>.Required<V>>) -> SelectTerm<D> {
|
||||
public static func attribute<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>) -> SelectTerm<O> {
|
||||
|
||||
return self.attribute(D.meta[keyPath: keyPath].keyPath)
|
||||
return self.attribute(O.meta[keyPath: keyPath].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,9 +395,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter keyPath: the attribute name
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
|
||||
*/
|
||||
public static func attribute<V>(_ keyPath: KeyPath<D, ValueContainer<D>.Optional<V>>) -> SelectTerm<D> {
|
||||
public static func attribute<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>) -> SelectTerm<O> {
|
||||
|
||||
return self.attribute(D.meta[keyPath: keyPath].keyPath)
|
||||
return self.attribute(O.meta[keyPath: keyPath].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,9 +405,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter keyPath: the attribute name
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
|
||||
*/
|
||||
public static func attribute<V>(_ keyPath: KeyPath<D, TransformableContainer<D>.Required<V>>) -> SelectTerm<D> {
|
||||
public static func attribute<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<V>>) -> SelectTerm<O> {
|
||||
|
||||
return self.attribute(D.meta[keyPath: keyPath].keyPath)
|
||||
return self.attribute(O.meta[keyPath: keyPath].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -409,9 +415,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter keyPath: the attribute name
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
|
||||
*/
|
||||
public static func attribute<V>(_ keyPath: KeyPath<D, TransformableContainer<D>.Optional<V>>) -> SelectTerm<D> {
|
||||
public static func attribute<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<V>>) -> SelectTerm<O> {
|
||||
|
||||
return self.attribute(D.meta[keyPath: keyPath].keyPath)
|
||||
return self.attribute(O.meta[keyPath: keyPath].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,9 +426,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
|
||||
*/
|
||||
public static func average<V>(_ keyPath: KeyPath<D, ValueContainer<D>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func average<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.average(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.average(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,9 +437,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
|
||||
*/
|
||||
public static func average<V>(_ keyPath: KeyPath<D, ValueContainer<D>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func average<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.average(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.average(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -442,9 +448,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
|
||||
*/
|
||||
public static func average<V>(_ keyPath: KeyPath<D, TransformableContainer<D>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func average<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.average(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.average(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -453,9 +459,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
|
||||
*/
|
||||
public static func average<V>(_ keyPath: KeyPath<D, TransformableContainer<D>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func average<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.average(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.average(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -464,10 +470,10 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for a count query
|
||||
*/
|
||||
public static func count<V>(_ keyPath: KeyPath<D,
|
||||
ValueContainer<D>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func count<V>(_ keyPath: KeyPath<O,
|
||||
ValueContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.count(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.count(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -476,10 +482,10 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for a count query
|
||||
*/
|
||||
public static func count<V>(_ keyPath: KeyPath<D,
|
||||
ValueContainer<D>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func count<V>(_ keyPath: KeyPath<O,
|
||||
ValueContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.count(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.count(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -488,10 +494,10 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for a count query
|
||||
*/
|
||||
public static func count<V>(_ keyPath: KeyPath<D,
|
||||
TransformableContainer<D>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func count<V>(_ keyPath: KeyPath<O,
|
||||
TransformableContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.count(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.count(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,10 +506,10 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for a count query
|
||||
*/
|
||||
public static func count<V>(_ keyPath: KeyPath<D,
|
||||
TransformableContainer<D>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func count<V>(_ keyPath: KeyPath<O,
|
||||
TransformableContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.count(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.count(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -512,10 +518,10 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
|
||||
*/
|
||||
public static func maximum<V>(_ keyPath: KeyPath<D,
|
||||
ValueContainer<D>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func maximum<V>(_ keyPath: KeyPath<O,
|
||||
ValueContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.maximum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.maximum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,10 +530,10 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
|
||||
*/
|
||||
public static func maximum<V>(_ keyPath: KeyPath<D,
|
||||
ValueContainer<D>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func maximum<V>(_ keyPath: KeyPath<O,
|
||||
ValueContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.maximum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.maximum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -536,10 +542,10 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
|
||||
*/
|
||||
public static func maximum<V>(_ keyPath: KeyPath<D,
|
||||
TransformableContainer<D>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func maximum<V>(_ keyPath: KeyPath<O,
|
||||
TransformableContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.maximum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.maximum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -548,10 +554,10 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
|
||||
*/
|
||||
public static func maximum<V>(_ keyPath: KeyPath<D,
|
||||
TransformableContainer<D>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func maximum<V>(_ keyPath: KeyPath<O,
|
||||
TransformableContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.maximum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.maximum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -560,9 +566,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
|
||||
*/
|
||||
public static func minimum<V>(_ keyPath: KeyPath<D, ValueContainer<D>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func minimum<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.minimum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.minimum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,9 +577,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
|
||||
*/
|
||||
public static func minimum<V>(_ keyPath: KeyPath<D, ValueContainer<D>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func minimum<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.minimum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.minimum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -582,9 +588,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
|
||||
*/
|
||||
public static func minimum<V>(_ keyPath: KeyPath<D, TransformableContainer<D>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func minimum<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.minimum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.minimum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -593,9 +599,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
|
||||
*/
|
||||
public static func minimum<V>(_ keyPath: KeyPath<D, TransformableContainer<D>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func minimum<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.minimum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.minimum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -604,9 +610,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func sum<V>(_ keyPath: KeyPath<D, ValueContainer<D>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func sum<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.sum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.sum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -615,9 +621,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func sum<V>(_ keyPath: KeyPath<D, ValueContainer<D>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func sum<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.sum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.sum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -626,9 +632,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func sum<V>(_ keyPath: KeyPath<D, TransformableContainer<D>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func sum<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.sum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.sum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -637,9 +643,9 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func sum<V>(_ keyPath: KeyPath<D, TransformableContainer<D>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<D> {
|
||||
public static func sum<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.sum(D.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.sum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,14 +657,14 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
|
||||
You can bind the return type by specializing the initializer:
|
||||
```
|
||||
let maximumAge = CoreStore.queryValue(
|
||||
let maximumAge = dataStack.queryValue(
|
||||
From<MyPersonEntity>(),
|
||||
Select<Int>(.maximum("age"))
|
||||
)
|
||||
```
|
||||
or by casting the type of the return value:
|
||||
```
|
||||
let maximumAge: Int = CoreStore.queryValue(
|
||||
let maximumAge: Int = dataStack.queryValue(
|
||||
From<MyPersonEntity>(),
|
||||
Select(.maximum("age"))
|
||||
)
|
||||
@@ -672,7 +678,7 @@ extension SelectTerm where D: CoreStoreObject {
|
||||
|
||||
- parameter sortDescriptors: a series of `NSSortDescriptor`s
|
||||
*/
|
||||
public struct Select<D: DynamicObject, T: SelectResultType>: SelectClause, Hashable {
|
||||
public struct Select<O: DynamicObject, T: SelectResultType>: SelectClause, Hashable {
|
||||
|
||||
/**
|
||||
Initializes a `Select` clause with a list of `SelectTerm`s
|
||||
@@ -680,7 +686,7 @@ public struct Select<D: DynamicObject, T: SelectResultType>: SelectClause, Hasha
|
||||
- parameter selectTerm: a `SelectTerm`
|
||||
- parameter selectTerms: a series of `SelectTerm`s
|
||||
*/
|
||||
public init(_ selectTerm: SelectTerm<D>, _ selectTerms: SelectTerm<D>...) {
|
||||
public init(_ selectTerm: SelectTerm<O>, _ selectTerms: SelectTerm<O>...) {
|
||||
|
||||
self.selectTerms = [selectTerm] + selectTerms
|
||||
}
|
||||
@@ -690,7 +696,7 @@ public struct Select<D: DynamicObject, T: SelectResultType>: SelectClause, Hasha
|
||||
|
||||
- parameter selectTerms: a series of `SelectTerm`s
|
||||
*/
|
||||
public init(_ selectTerms: [SelectTerm<D>]) {
|
||||
public init(_ selectTerms: [SelectTerm<O>]) {
|
||||
|
||||
self.selectTerms = selectTerms
|
||||
}
|
||||
@@ -698,7 +704,7 @@ public struct Select<D: DynamicObject, T: SelectResultType>: SelectClause, Hasha
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == <T, U>(lhs: Select<D, T>, rhs: Select<D, U>) -> Bool {
|
||||
public static func == <T, U>(lhs: Select<O, T>, rhs: Select<O, U>) -> Bool {
|
||||
|
||||
return lhs.selectTerms == rhs.selectTerms
|
||||
}
|
||||
@@ -706,10 +712,10 @@ public struct Select<D: DynamicObject, T: SelectResultType>: SelectClause, Hasha
|
||||
|
||||
// MARK: SelectClause
|
||||
|
||||
public typealias ObjectType = D
|
||||
public typealias ObjectType = O
|
||||
public typealias ReturnType = T
|
||||
|
||||
public let selectTerms: [SelectTerm<D>]
|
||||
public let selectTerms: [SelectTerm<O>]
|
||||
|
||||
|
||||
// MARK: Hashable
|
||||
@@ -806,6 +812,12 @@ public struct Select<D: DynamicObject, T: SelectResultType>: SelectClause, Hasha
|
||||
|
||||
fetchRequest.propertiesToFetch = propertiesToFetch
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "O")
|
||||
public typealias D = O
|
||||
}
|
||||
|
||||
extension Select where T: NSManagedObjectID {
|
||||
@@ -819,25 +831,25 @@ extension Select where T: NSManagedObjectID {
|
||||
}
|
||||
}
|
||||
|
||||
extension Select where D: NSManagedObject {
|
||||
extension Select where O: NSManagedObject {
|
||||
|
||||
/**
|
||||
Initializes a `Select` that queries the value of an attribute pertained by a keyPath
|
||||
- parameter keyPath: the keyPath for the attribute
|
||||
*/
|
||||
public init(_ keyPath: KeyPath<D, T>) {
|
||||
public init(_ keyPath: KeyPath<O, T>) {
|
||||
|
||||
self.init(.attribute(keyPath))
|
||||
}
|
||||
}
|
||||
|
||||
extension Select where D: CoreStoreObject, T: ImportableAttributeType {
|
||||
extension Select where O: CoreStoreObject, T: ImportableAttributeType {
|
||||
|
||||
/**
|
||||
Initializes a `Select` that queries the value of an attribute pertained by a keyPath
|
||||
- parameter keyPath: the keyPath for the attribute
|
||||
*/
|
||||
public init(_ keyPath: KeyPath<D, ValueContainer<D>.Required<T>>) {
|
||||
public init(_ keyPath: KeyPath<O, ValueContainer<O>.Required<T>>) {
|
||||
|
||||
self.init(.attribute(keyPath))
|
||||
}
|
||||
@@ -846,19 +858,19 @@ extension Select where D: CoreStoreObject, T: ImportableAttributeType {
|
||||
Initializes a `Select` that queries the value of an attribute pertained by a keyPath
|
||||
- parameter keyPath: the keyPath for the attribute
|
||||
*/
|
||||
public init(_ keyPath: KeyPath<D, ValueContainer<D>.Optional<T>>) {
|
||||
public init(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<T>>) {
|
||||
|
||||
self.init(.attribute(keyPath))
|
||||
}
|
||||
}
|
||||
|
||||
extension Select where D: CoreStoreObject, T: ImportableAttributeType & NSCoding & NSCopying {
|
||||
extension Select where O: CoreStoreObject, T: ImportableAttributeType & NSCoding & NSCopying {
|
||||
|
||||
/**
|
||||
Initializes a `Select` that queries the value of an attribute pertained by a keyPath
|
||||
- parameter keyPath: the keyPath for the attribute
|
||||
*/
|
||||
public init(_ keyPath: KeyPath<D, TransformableContainer<D>.Required<T>>) {
|
||||
public init(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<T>>) {
|
||||
|
||||
self.init(.attribute(keyPath))
|
||||
}
|
||||
@@ -867,7 +879,7 @@ extension Select where D: CoreStoreObject, T: ImportableAttributeType & NSCoding
|
||||
Initializes a `Select` that queries the value of an attribute pertained by a keyPath
|
||||
- parameter keyPath: the keyPath for the attribute
|
||||
*/
|
||||
public init(_ keyPath: KeyPath<D, TransformableContainer<D>.Optional<T>>) {
|
||||
public init(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<T>>) {
|
||||
|
||||
self.init(.attribute(keyPath))
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import CoreData
|
||||
`SetupResult.success` indicates that the storage setup succeeded. The associated object for this `enum` value is the related `StorageInterface` instance.
|
||||
`SetupResult.failure` indicates that the storage setup failed. The associated object for this value is the related `CoreStoreError` enum value.
|
||||
```
|
||||
try! CoreStore.addStorage(
|
||||
try! dataStack.addStorage(
|
||||
SQLiteStore(),
|
||||
completion: { (result: SetupResult) -> Void in
|
||||
switch result {
|
||||
|
||||
@@ -161,108 +161,3 @@ extension LocalStorage {
|
||||
&& persistentStore.url == self.fileURL
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CloudStorageOptions
|
||||
|
||||
/**
|
||||
The `CloudStorageOptions` provides settings that tells the `DataStack` how to setup the persistent store for `LocalStorage` implementers.
|
||||
*/
|
||||
public struct CloudStorageOptions: OptionSet, ExpressibleByNilLiteral {
|
||||
|
||||
/**
|
||||
Tells the `DataStack` that the store should not be migrated or recreated, and should simply fail on model mismatch
|
||||
*/
|
||||
public static let none = CloudStorageOptions(rawValue: 0)
|
||||
|
||||
/**
|
||||
Tells the `DataStack` to delete and recreate the local store from the cloud store on model mismatch, otherwise exceptions will be thrown on failure instead
|
||||
*/
|
||||
public static let recreateLocalStoreOnModelMismatch = CloudStorageOptions(rawValue: 1 << 0)
|
||||
|
||||
/**
|
||||
Tells the `DataStack` to allow lightweight migration for the store when added synchronously
|
||||
*/
|
||||
public static let allowSynchronousLightweightMigration = CloudStorageOptions(rawValue: 1 << 2)
|
||||
|
||||
|
||||
// MARK: OptionSetType
|
||||
|
||||
public init(rawValue: Int) {
|
||||
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
|
||||
// MARK: RawRepresentable
|
||||
|
||||
public let rawValue: Int
|
||||
|
||||
|
||||
// MARK: ExpressibleByNilLiteral
|
||||
|
||||
public init(nilLiteral: ()) {
|
||||
|
||||
self.rawValue = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CloudStorage
|
||||
|
||||
/**
|
||||
The `CloudStorage` represents `StorageInterface`s that are synchronized from a cloud-based store.
|
||||
*/
|
||||
public protocol CloudStorage: StorageInterface {
|
||||
|
||||
/**
|
||||
The `NSURL` that points to the store file
|
||||
*/
|
||||
var cacheFileURL: URL { get }
|
||||
|
||||
/**
|
||||
Options that tell the `DataStack` how to setup the persistent store
|
||||
*/
|
||||
var cloudStorageOptions: CloudStorageOptions { get }
|
||||
|
||||
/**
|
||||
The options dictionary for the specified `CloudStorageOptions`
|
||||
*/
|
||||
func dictionary(forOptions options: CloudStorageOptions) -> [AnyHashable: Any]?
|
||||
|
||||
/**
|
||||
Called by the `DataStack` to perform actual deletion of the store file from disk. **Do not call directly!** The `sourceModel` argument is a hint for the existing store's model version. Implementers can use the `sourceModel` to perform necessary store operations. (Cloud stores for example, can set the NSPersistentStoreRemoveUbiquitousMetadataOption option before deleting)
|
||||
*/
|
||||
func cs_eraseStorageAndWait(soureModel: NSManagedObjectModel) throws
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
extension CloudStorage {
|
||||
|
||||
internal func matchesPersistentStore(_ persistentStore: NSPersistentStore) -> Bool {
|
||||
|
||||
guard persistentStore.type == Self.storeType
|
||||
&& persistentStore.configurationName == (self.configuration ?? DataStack.defaultConfigurationName) else {
|
||||
|
||||
return false
|
||||
}
|
||||
guard persistentStore.url == self.cacheFileURL else {
|
||||
|
||||
return false
|
||||
}
|
||||
guard let persistentStoreOptions = persistentStore.options,
|
||||
let storeOptions = self.storeOptions else {
|
||||
|
||||
return persistentStore.options == nil && self.storeOptions == nil
|
||||
}
|
||||
return storeOptions.reduce(true) { (isMatch, tuple) in
|
||||
|
||||
let (key, value) = tuple
|
||||
let obj1 = persistentStoreOptions[key] as? NSObject
|
||||
let obj2 = value as? NSObject
|
||||
return isMatch && (obj1 == obj2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import CoreData
|
||||
// MARK: - SynchronousDataTransaction
|
||||
|
||||
/**
|
||||
The `SynchronousDataTransaction` provides an interface for `DynamicObject` creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from `DataStack.beginSynchronous(_:)`, or from `CoreStore.beginSynchronous(_:)`.
|
||||
The `SynchronousDataTransaction` provides an interface for `DynamicObject` creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from `DataStack.beginSynchronous(_:)`.
|
||||
*/
|
||||
public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
|
||||
@@ -55,7 +55,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
- parameter into: the `Into` clause indicating the destination `NSManagedObject` or `CoreStoreObject` entity type and the destination configuration
|
||||
- returns: a new `NSManagedObject` or `CoreStoreObject` instance of the specified entity type.
|
||||
*/
|
||||
public override func create<D>(_ into: Into<D>) -> D {
|
||||
public override func create<O>(_ into: Into<O>) -> O {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
@@ -71,7 +71,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
- parameter object: the `NSManagedObject` or `CoreStoreObject` to be edited
|
||||
- returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`.
|
||||
*/
|
||||
public override func edit<D: DynamicObject>(_ object: D?) -> D? {
|
||||
public override func edit<O: DynamicObject>(_ object: O?) -> O? {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
@@ -88,7 +88,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
- parameter objectID: the `NSManagedObjectID` for the object to be edited
|
||||
- returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`.
|
||||
*/
|
||||
public override func edit<D>(_ into: Into<D>, _ objectID: NSManagedObjectID) -> D? {
|
||||
public override func edit<O>(_ into: Into<O>, _ objectID: NSManagedObjectID) -> O? {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
@@ -97,51 +97,50 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
|
||||
return super.edit(into, objectID)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Deletes a specified `NSManagedObject` or `CoreStoreObject`.
|
||||
|
||||
- parameter object: the `NSManagedObject` or `CoreStoreObject` type to be deleted
|
||||
Deletes the objects with the specified `NSManagedObjectID`s.
|
||||
|
||||
- parameter objectIDs: the `NSManagedObjectID`s of the objects to delete
|
||||
*/
|
||||
public override func delete<D: DynamicObject>(_ object: D?) {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to delete an entity of type \(Internals.typeName(object)) from an already committed \(Internals.typeName(self))."
|
||||
)
|
||||
|
||||
super.delete(object)
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes the specified `DynamicObject`s.
|
||||
|
||||
- parameter object1: the `DynamicObject` to be deleted
|
||||
- parameter object2: another `DynamicObject` to be deleted
|
||||
- parameter objects: other `DynamicObject`s to be deleted
|
||||
*/
|
||||
public override func delete<D: DynamicObject>(_ object1: D?, _ object2: D?, _ objects: D?...) {
|
||||
|
||||
public override func delete<S: Sequence>(objectIDs: S) where S.Iterator.Element: NSManagedObjectID {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to delete an entities from an already committed \(Internals.typeName(self))."
|
||||
)
|
||||
|
||||
super.delete(([object1, object2] + objects).compactMap { $0 })
|
||||
|
||||
super.delete(objectIDs: objectIDs)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Deletes the specified `DynamicObject`s.
|
||||
|
||||
- parameter objects: the `DynamicObject`s to be deleted
|
||||
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by series of `ObjectRepresentation`s.
|
||||
|
||||
- parameter object: the `ObjectRepresentation` representing an `NSManagedObject` or `CoreStoreObject` to be deleted
|
||||
- parameter objects: other `ObjectRepresentation`s representing `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
||||
*/
|
||||
public override func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: DynamicObject {
|
||||
|
||||
public override func delete<O: ObjectRepresentation>(_ object: O?, _ objects: O?...) {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to delete an entities from an already committed \(Internals.typeName(self))."
|
||||
)
|
||||
|
||||
|
||||
super.delete(([object] + objects).compactMap { $0 })
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by an `ObjectRepresenation`.
|
||||
|
||||
- parameter objects: the `ObjectRepresenation`s representing `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
||||
*/
|
||||
public override func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: ObjectRepresentation {
|
||||
|
||||
Internals.assert(
|
||||
!self.isCommitted,
|
||||
"Attempted to delete an entities from an already committed \(Internals.typeName(self))."
|
||||
)
|
||||
|
||||
super.delete(objects)
|
||||
}
|
||||
|
||||
|
||||
@@ -209,6 +209,11 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
||||
public typealias ReturnValueType = DestinationValueType
|
||||
|
||||
|
||||
// MARK: PropertyProtocol
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
|
||||
|
||||
// MARK: AttributeProtocol
|
||||
|
||||
internal static var attributeType: NSAttributeType {
|
||||
@@ -216,7 +221,6 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
||||
return .transformableAttributeType
|
||||
}
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
internal let isOptional = false
|
||||
internal let isTransient: Bool
|
||||
internal let allowsExternalBinaryDataStorage: Bool
|
||||
@@ -268,7 +272,8 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return self.value
|
||||
}
|
||||
|
||||
@@ -426,6 +431,11 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
||||
public typealias ReturnValueType = DestinationValueType?
|
||||
|
||||
|
||||
// MARK: PropertyProtocol
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
|
||||
|
||||
// MARK: AttributeProtocol
|
||||
|
||||
internal static var attributeType: NSAttributeType {
|
||||
@@ -433,7 +443,6 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
||||
return .transformableAttributeType
|
||||
}
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
internal let isOptional = true
|
||||
internal let isTransient: Bool
|
||||
internal let allowsExternalBinaryDataStorage: Bool
|
||||
@@ -485,8 +494,9 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
return self.value as Any
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return self.value
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public final class UnsafeDataModelSchema: DynamicSchema {
|
||||
/**
|
||||
Initializes a `UnsafeDataModelSchema` from an `NSManagedObjectModel`.
|
||||
```
|
||||
CoreStore.defaultStack = DataStack(
|
||||
CoreStoreDefaults.dataStack = DataStack(
|
||||
UnsafeDataModelSchema(modelName: "MyAppV1", model: model)
|
||||
)
|
||||
```
|
||||
|
||||
@@ -33,17 +33,14 @@ import CoreData
|
||||
extension UnsafeDataTransaction {
|
||||
|
||||
/**
|
||||
Creates a `ObjectMonitor` for the specified `DynamicObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `DynamicObject`.
|
||||
Creates an `ObjectMonitor` for the specified `DynamicObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `DynamicObject`.
|
||||
|
||||
- parameter object: the `DynamicObject` to observe changes from
|
||||
- returns: a `ObjectMonitor` that monitors changes to `object`
|
||||
- returns: an `ObjectMonitor` that monitors changes to `object`
|
||||
*/
|
||||
public func monitorObject<D>(_ object: D) -> ObjectMonitor<D> {
|
||||
|
||||
return ObjectMonitor(
|
||||
unsafeTransaction: self,
|
||||
object: object
|
||||
)
|
||||
public func monitorObject<O: DynamicObject>(_ object: O) -> ObjectMonitor<O> {
|
||||
|
||||
return .init(objectID: object.cs_id(), context: self.unsafeContext())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,7 +50,7 @@ extension UnsafeDataTransaction {
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public func monitorList<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> ListMonitor<D> {
|
||||
public func monitorList<O>(_ from: From<O>, _ fetchClauses: FetchClause...) -> ListMonitor<O> {
|
||||
|
||||
return self.monitorList(from, fetchClauses)
|
||||
}
|
||||
@@ -65,10 +62,10 @@ extension UnsafeDataTransaction {
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public func monitorList<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> ListMonitor<D> {
|
||||
public func monitorList<O>(_ from: From<O>, _ fetchClauses: [FetchClause]) -> ListMonitor<O> {
|
||||
|
||||
Internals.assert(
|
||||
fetchClauses.filter { $0 is OrderBy<D> }.count > 0,
|
||||
fetchClauses.filter { $0 is OrderBy<O> }.count > 0,
|
||||
"A ListMonitor requires an OrderBy clause."
|
||||
)
|
||||
|
||||
@@ -112,7 +109,7 @@ extension UnsafeDataTransaction {
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public func monitorList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ fetchClauses: FetchClause...) {
|
||||
public func monitorList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ fetchClauses: FetchClause...) {
|
||||
|
||||
self.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
|
||||
}
|
||||
@@ -124,10 +121,10 @@ extension UnsafeDataTransaction {
|
||||
- parameter from: a `From` clause indicating the entity type
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public func monitorList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ fetchClauses: [FetchClause]) {
|
||||
public func monitorList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ fetchClauses: [FetchClause]) {
|
||||
|
||||
Internals.assert(
|
||||
fetchClauses.filter { $0 is OrderBy<D> }.count > 0,
|
||||
fetchClauses.filter { $0 is OrderBy<O> }.count > 0,
|
||||
"A ListMonitor requires an OrderBy clause."
|
||||
)
|
||||
|
||||
@@ -176,7 +173,7 @@ extension UnsafeDataTransaction {
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public func monitorSectionedList<D>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: FetchClause...) -> ListMonitor<D> {
|
||||
public func monitorSectionedList<O>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: FetchClause...) -> ListMonitor<O> {
|
||||
|
||||
return self.monitorSectionedList(from, sectionBy, fetchClauses)
|
||||
}
|
||||
@@ -189,10 +186,10 @@ extension UnsafeDataTransaction {
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
- returns: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
public func monitorSectionedList<D>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: [FetchClause]) -> ListMonitor<D> {
|
||||
public func monitorSectionedList<O>(_ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: [FetchClause]) -> ListMonitor<O> {
|
||||
|
||||
Internals.assert(
|
||||
fetchClauses.filter { $0 is OrderBy<D> }.count > 0,
|
||||
fetchClauses.filter { $0 is OrderBy<O> }.count > 0,
|
||||
"A ListMonitor requires an OrderBy clause."
|
||||
)
|
||||
|
||||
@@ -237,7 +234,7 @@ extension UnsafeDataTransaction {
|
||||
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public func monitorSectionedList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: FetchClause...) {
|
||||
public func monitorSectionedList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: FetchClause...) {
|
||||
|
||||
self.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
|
||||
}
|
||||
@@ -250,10 +247,10 @@ extension UnsafeDataTransaction {
|
||||
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
|
||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||
*/
|
||||
public func monitorSectionedList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: [FetchClause]) {
|
||||
public func monitorSectionedList<O>(createAsynchronously: @escaping (ListMonitor<O>) -> Void, _ from: From<O>, _ sectionBy: SectionBy<O>, _ fetchClauses: [FetchClause]) {
|
||||
|
||||
Internals.assert(
|
||||
fetchClauses.filter { $0 is OrderBy<D> }.count > 0,
|
||||
fetchClauses.filter { $0 is OrderBy<O> }.count > 0,
|
||||
"A ListMonitor requires an OrderBy clause."
|
||||
)
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import Foundation
|
||||
enum Static {
|
||||
static var myDataKey: Void?
|
||||
}
|
||||
CoreStore.defaultStack.userInfo[&Static.myDataKey] = myObject
|
||||
CoreStoreDefaults.dataStack.userInfo[&Static.myDataKey] = myObject
|
||||
```
|
||||
- Important: Do not use this class to store thread-sensitive data.
|
||||
*/
|
||||
@@ -46,7 +46,7 @@ public final class UserInfo {
|
||||
enum Static {
|
||||
static var myDataKey: Void?
|
||||
}
|
||||
CoreStore.defaultStack.userInfo[&Static.myDataKey] = myObject
|
||||
CoreStoreDefaults.dataStack.userInfo[&Static.myDataKey] = myObject
|
||||
```
|
||||
- Important: Do not use this method to store thread-sensitive data.
|
||||
- parameter key: the key for custom data. Make sure this is a static pointer that will never be changed.
|
||||
@@ -79,7 +79,7 @@ public final class UserInfo {
|
||||
enum Static {
|
||||
static var myDataKey: Void?
|
||||
}
|
||||
CoreStore.defaultStack.userInfo[&Static.myDataKey, lazyInit: { MyObject() }] = myObject
|
||||
CoreStoreDefaults.dataStack.userInfo[&Static.myDataKey, lazyInit: { MyObject() }] = myObject
|
||||
```
|
||||
- Important: Do not use this method to store thread-sensitive data.
|
||||
- parameter key: the key for custom data. Make sure this is a static pointer that will never be changed.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user