mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-18 07:24:09 +01:00
WIP: ObjectRepresentable utilities
This commit is contained in:
@@ -136,14 +136,15 @@ class ListObserverDemoViewController: UITableViewController {
|
|||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
ColorsDemo.palettes.addObserver(self) { [weak self] (liveList, snapshot) in
|
ColorsDemo.palettes.addObserver(self) { [weak self] (liveList) in
|
||||||
|
|
||||||
guard let self = self else {
|
guard let self = self else {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.dataSource?.apply(snapshot, animatingDifferences: true)
|
self.dataSource?.apply(liveList.snapshot, animatingDifferences: true)
|
||||||
}
|
}
|
||||||
|
self.dataSource?.apply(ColorsDemo.palettes.snapshot, animatingDifferences: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
@@ -152,8 +153,8 @@ class ListObserverDemoViewController: UITableViewController {
|
|||||||
|
|
||||||
switch (segue.identifier, segue.destination, sender) {
|
switch (segue.identifier, segue.destination, sender) {
|
||||||
|
|
||||||
case ("ObjectObserverDemoViewController"?, let destinationViewController as ObjectObserverDemoViewController, let palette as Palette):
|
case ("ObjectObserverDemoViewController"?, let destinationViewController as ObjectObserverDemoViewController, let palette as LiveObject<Palette>):
|
||||||
destinationViewController.palette = palette
|
destinationViewController.setPalette(palette)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@@ -169,7 +170,7 @@ class ListObserverDemoViewController: UITableViewController {
|
|||||||
|
|
||||||
self.performSegue(
|
self.performSegue(
|
||||||
withIdentifier: "ObjectObserverDemoViewController",
|
withIdentifier: "ObjectObserverDemoViewController",
|
||||||
sender: ColorsDemo.palettes[indexPath: indexPath]?.object
|
sender: ColorsDemo.palettes[indexPath: indexPath]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,30 +14,23 @@ import CoreStore
|
|||||||
|
|
||||||
class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
||||||
|
|
||||||
var palette: Palette? {
|
func setPalette<O: ObjectRepresentation>(_ newValue: O?) where O.ObjectType == Palette {
|
||||||
|
|
||||||
get {
|
guard self.monitor?.cs_id() != newValue?.cs_id() else {
|
||||||
|
|
||||||
return self.monitor?.object
|
return
|
||||||
}
|
}
|
||||||
set {
|
if let newValue = newValue {
|
||||||
|
|
||||||
guard self.monitor?.object != newValue else {
|
self.monitor = ColorsDemo.stack.monitorObject(newValue)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
return
|
self.monitor = nil
|
||||||
}
|
|
||||||
|
|
||||||
if let palette = newValue {
|
|
||||||
|
|
||||||
self.monitor = ColorsDemo.stack.monitorObject(palette)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
self.monitor = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: NSObject
|
// MARK: NSObject
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
|||||||
@@ -110,43 +110,27 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deletes a specified `NSManagedObject` or `CoreStoreObject`.
|
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by series of `ObjectRepresentation`s.
|
||||||
|
|
||||||
- parameter object: the `NSManagedObject` or `CoreStoreObject` 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 override func delete<D: DynamicObject>(_ object: D?) {
|
public override func delete<O: ObjectRepresentation>(_ object: O?, _ objects: O?...) {
|
||||||
|
|
||||||
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?...) {
|
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
!self.isCommitted,
|
!self.isCommitted,
|
||||||
"Attempted to delete an entities from an already committed \(Internals.typeName(self))."
|
"Attempted to delete an entities from an already committed \(Internals.typeName(self))."
|
||||||
)
|
)
|
||||||
|
|
||||||
super.delete(([object1, object2] + objects).compactMap { $0 })
|
super.delete(([object] + objects).compactMap { $0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deletes the specified `DynamicObject`s.
|
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by an `ObjectRepresenation`.
|
||||||
|
|
||||||
- parameter objects: the `DynamicObject`s to be deleted
|
- 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: DynamicObject {
|
public override func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: ObjectRepresentation {
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
!self.isCommitted,
|
!self.isCommitted,
|
||||||
|
|||||||
@@ -156,47 +156,36 @@ public /*abstract*/ class BaseDataTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deletes a specified `NSManagedObject` or `CoreStoreObject`.
|
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by series of `ObjectRepresentation`s.
|
||||||
|
|
||||||
- parameter object: the `NSManagedObject` or `CoreStoreObject` 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>(_ object: D?) {
|
public func delete<O: ObjectRepresentation>(_ object: O?, _ objects: O?...) {
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
self.isRunningInAllowedQueue(),
|
self.isRunningInAllowedQueue(),
|
||||||
"Attempted to delete an entity outside its designated queue."
|
"Attempted to delete an entity outside its designated queue."
|
||||||
)
|
)
|
||||||
let context = self.context
|
self.delete(([object] + objects).compactMap { $0 })
|
||||||
object
|
|
||||||
.flatMap(context.fetchExisting)
|
|
||||||
.flatMap({ context.delete($0.cs_toRaw()) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s.
|
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by an `ObjectRepresenation`.
|
||||||
|
|
||||||
- parameter object1: the `NSManagedObject` or `CoreStoreObject` to be deleted
|
- parameter objects: the `ObjectRepresenation`s representing `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
||||||
- parameter object2: another `NSManagedObject` or `CoreStoreObject` to be deleted
|
|
||||||
- parameter objects: other `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
|
||||||
*/
|
*/
|
||||||
public func delete<D: DynamicObject>(_ object1: D?, _ object2: D?, _ objects: D?...) {
|
public func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: ObjectRepresentation {
|
||||||
|
|
||||||
self.delete(([object1, object2] + objects).compactMap { $0 })
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s.
|
|
||||||
|
|
||||||
- parameter objects: the `NSManagedObject`s or `CoreStoreObject`s to be deleted
|
|
||||||
*/
|
|
||||||
public func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: DynamicObject {
|
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
self.isRunningInAllowedQueue(),
|
self.isRunningInAllowedQueue(),
|
||||||
"Attempted to delete entities outside their designated queue."
|
"Attempted to delete entities outside their designated queue."
|
||||||
)
|
)
|
||||||
let context = self.context
|
let context = self.context
|
||||||
objects.forEach { context.fetchExisting($0).flatMap({ context.delete($0.cs_toRaw()) }) }
|
objects.forEach {
|
||||||
|
|
||||||
|
$0.cs_rawObject(in: context).map({ context.delete($0) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,10 +206,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.
|
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.
|
- returns: `true` if the object has any property values changed.
|
||||||
*/
|
*/
|
||||||
public func objectHasPersistentChangedValues<D: DynamicObject>(_ entity: D) -> Bool {
|
public func objectHasPersistentChangedValues<D: DynamicObject>(_ object: D) -> Bool {
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
self.isRunningInAllowedQueue(),
|
self.isRunningInAllowedQueue(),
|
||||||
@@ -230,7 +219,7 @@ public /*abstract*/ class BaseDataTransaction {
|
|||||||
!self.isCommitted,
|
!self.isCommitted,
|
||||||
"Attempted to access inserted objects from an already committed \(Internals.typeName(self))."
|
"Attempted to access inserted objects from an already committed \(Internals.typeName(self))."
|
||||||
)
|
)
|
||||||
return entity.cs_toRaw().hasPersistentChangedValues
|
return object.cs_toRaw().hasPersistentChangedValues
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ import CoreData
|
|||||||
extension CoreStore {
|
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 `defaultStack`, creates an `ObjectMonitor` for the specified `ObjectRepresentation`. 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
|
- parameter object: the `ObjectRepresentation` to observe changes from
|
||||||
- returns: a `ObjectMonitor` that monitors changes to `object`
|
- returns: a `ObjectMonitor` that monitors changes to `object`
|
||||||
*/
|
*/
|
||||||
public static func monitorObject<D>(_ object: D) -> ObjectMonitor<D> {
|
public static func monitorObject<O: ObjectRepresentation>(_ object: O) -> ObjectMonitor<O.ObjectType> {
|
||||||
|
|
||||||
return Shared.defaultStack.monitorObject(object)
|
return Shared.defaultStack.monitorObject(object)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ import CoreData
|
|||||||
extension DataStack {
|
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`.
|
Creates an `ObjectMonitor` for the specified `ObjectRepresentation`. 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
|
- parameter object: the `ObjectRepresentation` to observe changes from
|
||||||
- returns: a `ObjectMonitor` that monitors changes to `object`
|
- returns: a `ObjectMonitor` that monitors changes to `object`
|
||||||
*/
|
*/
|
||||||
public func monitorObject<D>(_ object: D) -> ObjectMonitor<D> {
|
public func monitorObject<O: ObjectRepresentation>(_ object: O) -> ObjectMonitor<O.ObjectType> {
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
Thread.isMainThread,
|
Thread.isMainThread,
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import CoreData
|
|||||||
All CoreStore's utilities are designed around `DynamicObject` instances. `NSManagedObject` and `CoreStoreObject` instances all conform to `DynamicObject`.
|
All CoreStore's utilities are designed around `DynamicObject` instances. `NSManagedObject` and `CoreStoreObject` instances all conform to `DynamicObject`.
|
||||||
*/
|
*/
|
||||||
public protocol DynamicObject: AnyObject {
|
public protocol DynamicObject: AnyObject {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The object ID for this instance
|
The object ID for this instance
|
||||||
*/
|
*/
|
||||||
@@ -62,23 +61,18 @@ public protocol DynamicObject: AnyObject {
|
|||||||
/**
|
/**
|
||||||
Used internally by CoreStore. Do not call directly.
|
Used internally by CoreStore. Do not call directly.
|
||||||
*/
|
*/
|
||||||
func cs_id() -> ObjectID
|
func cs_toRaw() -> NSManagedObject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Used internally by CoreStore. Do not call directly.
|
Used internally by CoreStore. Do not call directly.
|
||||||
*/
|
*/
|
||||||
func cs_toRaw() -> NSManagedObject
|
func cs_id() -> ObjectID
|
||||||
}
|
}
|
||||||
|
|
||||||
extension DynamicObject {
|
extension DynamicObject {
|
||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
// internal static func keyPathBuilder() -> DynamicObjectMeta<Never, Self> {
|
|
||||||
//
|
|
||||||
// return .init(keyPathString: "SELF")
|
|
||||||
// }
|
|
||||||
|
|
||||||
internal func runtimeType() -> Self.Type {
|
internal func runtimeType() -> Self.Type {
|
||||||
|
|
||||||
// Self.self does not return runtime-created types
|
// Self.self does not return runtime-created types
|
||||||
@@ -120,15 +114,15 @@ extension NSManagedObject: DynamicObject {
|
|||||||
return object.isKind(of: self)
|
return object.isKind(of: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func cs_id() -> ObjectID {
|
|
||||||
|
|
||||||
return self.objectID
|
|
||||||
}
|
|
||||||
|
|
||||||
public func cs_toRaw() -> NSManagedObject {
|
public func cs_toRaw() -> NSManagedObject {
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func cs_id() -> ObjectID {
|
||||||
|
|
||||||
|
return self.objectID
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -210,13 +204,13 @@ extension CoreStoreObject {
|
|||||||
return (self as AnyClass).isSubclass(of: type as AnyClass)
|
return (self as AnyClass).isSubclass(of: type as AnyClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func cs_id() -> ObjectID {
|
|
||||||
|
|
||||||
return self.rawObject!.objectID
|
|
||||||
}
|
|
||||||
|
|
||||||
public func cs_toRaw() -> NSManagedObject {
|
public func cs_toRaw() -> NSManagedObject {
|
||||||
|
|
||||||
return self.rawObject!
|
return self.rawObject!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func cs_id() -> ObjectID {
|
||||||
|
|
||||||
|
return self.rawObject!.objectID
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,12 +116,16 @@ extension Internals {
|
|||||||
@objc
|
@objc
|
||||||
dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||||
|
|
||||||
|
var snapshot = Internals.DiffableDataSourceSnapshot(
|
||||||
|
sections: controller.sections ?? []
|
||||||
|
)
|
||||||
|
snapshot.reloadItems(self.reloadedIDs)
|
||||||
|
|
||||||
self.handler?.controller(
|
self.handler?.controller(
|
||||||
controller,
|
controller,
|
||||||
didChangeContentWith: Internals.DiffableDataSourceSnapshot(
|
didChangeContentWith: snapshot
|
||||||
sections: controller.sections ?? []
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
self.reloadedIDs.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
@@ -132,5 +136,17 @@ extension Internals {
|
|||||||
sectionIndexTitleForSectionName: sectionName
|
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.reloadedIDs.append(object.objectID)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private var reloadedIDs: [NSManagedObjectID] = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ extension Internals {
|
|||||||
self.observers.setObject(Closure<T, Void>(closure), forKey: observer)
|
self.observers.setObject(Closure<T, Void>(closure), forKey: observer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal func removeObserver<U: AnyObject>(_ observer: U) {
|
||||||
|
|
||||||
|
self.observers.removeObject(forKey: observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
return indices.map { position in
|
return indices.map { position in
|
||||||
|
|
||||||
let itemID = itemIDs[position]
|
let itemID = itemIDs[position]
|
||||||
return LiveObject<O>(id: itemID, context: context)
|
return LiveObject<O>(objectID: itemID, context: context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||||
return itemIDs.map {
|
return itemIDs.map {
|
||||||
|
|
||||||
return LiveObject<O>(id: $0, context: context)
|
return LiveObject<O>(objectID: $0, context: context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
return itemIndices.map { position in
|
return itemIndices.map { position in
|
||||||
|
|
||||||
let itemID = itemIDs[position]
|
let itemID = itemIDs[position]
|
||||||
return LiveObject<O>(id: itemID, context: context)
|
return LiveObject<O>(objectID: itemID, context: context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
|
|
||||||
let context = self.context!
|
let context = self.context!
|
||||||
let itemID = self.diffableSnapshot.itemIdentifiers[position]
|
let itemID = self.diffableSnapshot.itemIdentifiers[position]
|
||||||
return LiveObject<O>(id: itemID, context: context)
|
return LiveObject<O>(objectID: itemID, context: context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
let context = self.context
|
let context = self.context
|
||||||
return self.snapshot
|
return self.snapshot
|
||||||
.itemIdentifiers(inSection: sectionID)
|
.itemIdentifiers(inSection: sectionID)
|
||||||
.map({ context.liveObject(id: $0) })
|
.map({ context.liveObject(objectID: $0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscript(itemID itemID: ItemID) -> LiveObject<O>? {
|
public subscript(itemID itemID: ItemID) -> LiveObject<O>? {
|
||||||
@@ -59,7 +59,7 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return self.context.liveObject(id: validID)
|
return self.context.liveObject(objectID: validID)
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscript(indexPath indexPath: IndexPath) -> LiveObject<O>? {
|
public subscript(indexPath indexPath: IndexPath) -> LiveObject<O>? {
|
||||||
@@ -77,7 +77,7 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let itemID = itemIdentifiers[indexPath.item]
|
let itemID = itemIdentifiers[indexPath.item]
|
||||||
return self.context.liveObject(id: itemID)
|
return self.context.liveObject(objectID: itemID)
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscript<S: Sequence>(section sectionID: SectionID, itemIndices itemIndices: S) -> [LiveObject<O>] where S.Element == Int {
|
public subscript<S: Sequence>(section sectionID: SectionID, itemIndices itemIndices: S) -> [LiveObject<O>] where S.Element == Int {
|
||||||
@@ -87,7 +87,7 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
return itemIndices.map { position in
|
return itemIndices.map { position in
|
||||||
|
|
||||||
let itemID = itemIDs[position]
|
let itemID = itemIDs[position]
|
||||||
return context.liveObject(id: itemID)
|
return context.liveObject(objectID: itemID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
}
|
}
|
||||||
didSet {
|
didSet {
|
||||||
|
|
||||||
self.notifyObservers(self.snapshot)
|
self.notifyObservers()
|
||||||
self.didChange()
|
self.didChange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +123,7 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
|
|
||||||
let context = self.context
|
let context = self.context
|
||||||
return self.snapshot.itemIdentifiers
|
return self.snapshot.itemIdentifiers
|
||||||
.map({ context.liveObject(id: $0) })
|
.map({ context.liveObject(objectID: $0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
public func numberOfItems(inSection identifier: SectionID) -> Int {
|
public func numberOfItems(inSection identifier: SectionID) -> Int {
|
||||||
@@ -136,7 +136,7 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
let context = self.context
|
let context = self.context
|
||||||
return self.snapshot
|
return self.snapshot
|
||||||
.itemIdentifiers(inSection: identifier)
|
.itemIdentifiers(inSection: identifier)
|
||||||
.map({ context.liveObject(id: $0) })
|
.map({ context.liveObject(objectID: $0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
public func items(inSection identifier: SectionID, atIndices indices: IndexSet) -> [LiveObject<O>] {
|
public func items(inSection identifier: SectionID, atIndices indices: IndexSet) -> [LiveObject<O>] {
|
||||||
@@ -146,18 +146,18 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
return indices.map { position in
|
return indices.map { position in
|
||||||
|
|
||||||
let itemID = itemIDs[position]
|
let itemID = itemIDs[position]
|
||||||
return context.liveObject(id: itemID)
|
return context.liveObject(objectID: itemID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func section(containingItem item: LiveObject<O>) -> SectionID? {
|
public func section(containingItem item: LiveObject<O>) -> SectionID? {
|
||||||
|
|
||||||
return self.snapshot.sectionIdentifier(containingItem: item.id)
|
return self.snapshot.sectionIdentifier(containingItem: item.cs_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
public func indexOfItem(_ item: LiveObject<O>) -> Int? {
|
public func indexOfItem(_ item: LiveObject<O>) -> Int? {
|
||||||
|
|
||||||
return self.snapshot.indexOfItem(item.id)
|
return self.snapshot.indexOfItem(item.cs_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
public func indexOfSection(_ identifier: SectionID) -> Int? {
|
public func indexOfSection(_ identifier: SectionID) -> Int? {
|
||||||
@@ -165,14 +165,12 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
return self.snapshot.indexOfSection(identifier)
|
return self.snapshot.indexOfSection(identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func addObserver<T: AnyObject>(_ observer: T, _ callback: @escaping (LiveList<O>, ListSnapshot<O>) -> Void) {
|
public func addObserver<T: AnyObject>(_ observer: T, _ callback: @escaping (LiveList<O>) -> Void) {
|
||||||
|
|
||||||
self.observers.setObject(
|
self.observers.setObject(
|
||||||
Internals.Closure(callback),
|
Internals.Closure(callback),
|
||||||
forKey: observer
|
forKey: observer
|
||||||
)
|
)
|
||||||
|
|
||||||
callback(self, self.snapshot)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func removeObserver<T: AnyObject>(_ observer: T) {
|
public func removeObserver<T: AnyObject>(_ observer: T) {
|
||||||
@@ -272,7 +270,7 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
|
|
||||||
private let from: From<ObjectType>
|
private let from: From<ObjectType>
|
||||||
private let sectionBy: SectionBy<ObjectType>?
|
private let sectionBy: SectionBy<ObjectType>?
|
||||||
private let observers: NSMapTable<AnyObject, Internals.Closure<(LiveList<O>, ListSnapshot<O>), Void>> = .weakToStrongObjects()
|
private lazy var observers: NSMapTable<AnyObject, Internals.Closure<LiveList<O>, Void>> = .weakToStrongObjects()
|
||||||
|
|
||||||
private lazy var context: NSManagedObjectContext = self.fetchedResultsController.managedObjectContext
|
private lazy var context: NSManagedObjectContext = self.fetchedResultsController.managedObjectContext
|
||||||
|
|
||||||
@@ -338,7 +336,7 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func notifyObservers(_ snapshot: ListSnapshot<O>) {
|
private func notifyObservers() {
|
||||||
|
|
||||||
guard let enumerator = self.observers.objectEnumerator() else {
|
guard let enumerator = self.observers.objectEnumerator() else {
|
||||||
|
|
||||||
@@ -346,7 +344,7 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
}
|
}
|
||||||
for closure in enumerator {
|
for closure in enumerator {
|
||||||
|
|
||||||
(closure as! Internals.Closure<(LiveList<O>, ListSnapshot<O>), Void>).invoke(with: (self, snapshot))
|
(closure as! Internals.Closure<LiveList<O>, Void>).invoke(with: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import SwiftUI
|
|||||||
// MARK: - LiveObject
|
// MARK: - LiveObject
|
||||||
|
|
||||||
@dynamicMemberLookup
|
@dynamicMemberLookup
|
||||||
public final class LiveObject<O: DynamicObject>: Identifiable, Hashable {
|
public final class LiveObject<O: DynamicObject>: ObjectRepresentation, Hashable {
|
||||||
|
|
||||||
// MARK: Public
|
// MARK: Public
|
||||||
|
|
||||||
@@ -51,19 +51,60 @@ public final class LiveObject<O: DynamicObject>: Identifiable, Hashable {
|
|||||||
return self.lazySnapshot
|
return self.lazySnapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
public private(set) lazy var object: O = self.context.fetchExisting(self.id)!
|
public private(set) lazy var object: O = self.context.fetchExisting(self.objectID)!
|
||||||
|
|
||||||
|
public func addObserver<T: AnyObject>(_ observer: T, _ callback: @escaping (LiveObject<O>) -> Void) {
|
||||||
|
|
||||||
|
self.observers.setObject(
|
||||||
|
Internals.Closure(callback),
|
||||||
|
forKey: observer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func removeObserver<T: AnyObject>(_ observer: T) {
|
||||||
|
|
||||||
|
self.observers.removeObject(forKey: observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
|
||||||
|
self.observers.removeAllObjects()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Identifiable
|
// MARK: ObjectRepresentation
|
||||||
|
|
||||||
public let id: O.ObjectID
|
public typealias ObjectType = O
|
||||||
|
|
||||||
|
public static func cs_fromRaw(object: NSManagedObject) -> Self {
|
||||||
|
|
||||||
|
return self.init(
|
||||||
|
objectID: object.objectID,
|
||||||
|
context: object.managedObjectContext!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cs_id() -> O.ObjectID {
|
||||||
|
|
||||||
|
return self.objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cs_object() -> O? {
|
||||||
|
|
||||||
|
return self.context.fetchExisting(self.objectID)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cs_rawObject(in context: NSManagedObjectContext) -> NSManagedObject? {
|
||||||
|
|
||||||
|
return self.object.cs_toRaw()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Equatable
|
// MARK: Equatable
|
||||||
|
|
||||||
public static func == (_ lhs: LiveObject, _ rhs: LiveObject) -> Bool {
|
public static func == (_ lhs: LiveObject, _ rhs: LiveObject) -> Bool {
|
||||||
|
|
||||||
return lhs.id == rhs.id
|
return lhs.objectID == rhs.objectID
|
||||||
&& lhs.context == rhs.context
|
&& lhs.context == rhs.context
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,23 +113,25 @@ public final class LiveObject<O: DynamicObject>: Identifiable, Hashable {
|
|||||||
|
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
|
|
||||||
hasher.combine(self.id)
|
hasher.combine(self.objectID)
|
||||||
hasher.combine(self.context)
|
hasher.combine(self.context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: LiveResult
|
// MARK: LiveResult
|
||||||
|
|
||||||
public typealias ObjectType = O
|
|
||||||
|
|
||||||
public typealias SnapshotType = ObjectSnapshot<O>
|
public typealias SnapshotType = ObjectSnapshot<O>
|
||||||
|
|
||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal convenience init(id: ID, context: NSManagedObjectContext) {
|
internal convenience init(objectID: O.ObjectID, context: NSManagedObjectContext) {
|
||||||
|
|
||||||
self.init(id: id, context: context, initializer: ObjectSnapshot<O>.init(id:context:))
|
self.init(
|
||||||
|
objectID: objectID,
|
||||||
|
context: context,
|
||||||
|
initializer: ObjectSnapshot<O>.init(id:context:)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -96,9 +139,9 @@ public final class LiveObject<O: DynamicObject>: Identifiable, Hashable {
|
|||||||
|
|
||||||
fileprivate let rawObjectWillChange: Any?
|
fileprivate let rawObjectWillChange: Any?
|
||||||
|
|
||||||
fileprivate init(id: O.ObjectID, context: NSManagedObjectContext, initializer: @escaping (NSManagedObjectID, NSManagedObjectContext) -> ObjectSnapshot<O>) {
|
fileprivate init(objectID: O.ObjectID, context: NSManagedObjectContext, initializer: @escaping (NSManagedObjectID, NSManagedObjectContext) -> ObjectSnapshot<O>) {
|
||||||
|
|
||||||
self.id = id
|
self.objectID = objectID
|
||||||
self.context = context
|
self.context = context
|
||||||
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
|
|
||||||
@@ -114,7 +157,7 @@ public final class LiveObject<O: DynamicObject>: Identifiable, Hashable {
|
|||||||
|
|
||||||
self.rawObjectWillChange = nil
|
self.rawObjectWillChange = nil
|
||||||
}
|
}
|
||||||
self.$lazySnapshot.initialize({ initializer(id, context) })
|
self.$lazySnapshot.initialize({ initializer(objectID, context) })
|
||||||
|
|
||||||
context.objectsDidChangeObserver(for: self).addObserver(self) { [weak self] (objectIDs) in
|
context.objectsDidChangeObserver(for: self).addObserver(self) { [weak self] (objectIDs) in
|
||||||
|
|
||||||
@@ -122,18 +165,36 @@ public final class LiveObject<O: DynamicObject>: Identifiable, Hashable {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.$lazySnapshot.reset({ initializer(id, context) })
|
|
||||||
self.willChange()
|
self.willChange()
|
||||||
|
self.$lazySnapshot.reset({ initializer(objectID, context) })
|
||||||
|
self.notifyObservers()
|
||||||
|
self.didChange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
|
private let objectID: O.ObjectID
|
||||||
private let context: NSManagedObjectContext
|
private let context: NSManagedObjectContext
|
||||||
|
|
||||||
@Internals.LazyNonmutating(uninitialized: ())
|
@Internals.LazyNonmutating(uninitialized: ())
|
||||||
private var lazySnapshot: ObjectSnapshot<O>
|
private var lazySnapshot: ObjectSnapshot<O>
|
||||||
|
|
||||||
|
private lazy var observers: NSMapTable<AnyObject, Internals.Closure<LiveObject<O>, Void>> = .weakToStrongObjects()
|
||||||
|
|
||||||
|
private func notifyObservers() {
|
||||||
|
|
||||||
|
guard let enumerator = self.observers.objectEnumerator() else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for closure in enumerator {
|
||||||
|
|
||||||
|
(closure as! Internals.Closure<LiveObject
|
||||||
|
<O>, Void>).invoke(with: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
internal func liveObject<D: DynamicObject>(id: NSManagedObjectID) -> LiveObject<D> {
|
internal func liveObject<D: DynamicObject>(objectID: NSManagedObjectID) -> LiveObject<D> {
|
||||||
|
|
||||||
let cache: NSMapTable<NSManagedObjectID, LiveObject<D>> = self.userInfo(for: .liveObjectsCache(D.self)) {
|
let cache: NSMapTable<NSManagedObjectID, LiveObject<D>> = self.userInfo(for: .liveObjectsCache(D.self)) {
|
||||||
|
|
||||||
@@ -95,12 +95,12 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
return Internals.with {
|
return Internals.with {
|
||||||
|
|
||||||
if let liveObject = cache.object(forKey: id) {
|
if let liveObject = cache.object(forKey: objectID) {
|
||||||
|
|
||||||
return liveObject
|
return liveObject
|
||||||
}
|
}
|
||||||
let liveObject = LiveObject<D>(id: id, context: self)
|
let liveObject = LiveObject<D>(objectID: objectID, context: self)
|
||||||
cache.setObject(liveObject, forKey: id)
|
cache.setObject(liveObject, forKey: objectID)
|
||||||
return liveObject
|
return liveObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,12 +40,7 @@ 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.
|
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, *)
|
@available(macOS 10.12, *)
|
||||||
public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
public final class ObjectMonitor<D: DynamicObject>: ObjectRepresentation, Equatable {
|
||||||
|
|
||||||
/**
|
|
||||||
The type for the object contained by the `ObjectMonitor`
|
|
||||||
*/
|
|
||||||
public typealias ObjectType = D
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns the `DynamicObject` instance being observed, or `nil` if the object was already deleted.
|
Returns the `DynamicObject` instance being observed, or `nil` if the object was already deleted.
|
||||||
@@ -125,6 +120,34 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
|||||||
public let userInfo = UserInfo()
|
public let userInfo = UserInfo()
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: ObjectRepresentation
|
||||||
|
|
||||||
|
public typealias ObjectType = D
|
||||||
|
|
||||||
|
public static func cs_fromRaw(object: NSManagedObject) -> Self {
|
||||||
|
|
||||||
|
return self.init(
|
||||||
|
context: object.managedObjectContext!,
|
||||||
|
objectID: object.objectID
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cs_id() -> ObjectType.ObjectID {
|
||||||
|
|
||||||
|
return self.objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cs_object() -> D? {
|
||||||
|
|
||||||
|
return self.object
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cs_rawObject(in context: NSManagedObjectContext) -> NSManagedObject? {
|
||||||
|
|
||||||
|
return context.fetchExisting(self.objectID)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Equatable
|
// MARK: Equatable
|
||||||
|
|
||||||
public static func == (lhs: ObjectMonitor<ObjectType>, rhs: ObjectMonitor<ObjectType>) -> Bool {
|
public static func == (lhs: ObjectMonitor<ObjectType>, rhs: ObjectMonitor<ObjectType>) -> Bool {
|
||||||
@@ -158,14 +181,14 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal convenience init(dataStack: DataStack, object: ObjectType) {
|
internal convenience init<O: ObjectRepresentation>(dataStack: DataStack, object: O) where O.ObjectType == ObjectType {
|
||||||
|
|
||||||
self.init(context: dataStack.mainContext, object: object)
|
self.init(context: dataStack.mainContext, objectID: object.cs_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, object: ObjectType) {
|
internal convenience init<O: ObjectRepresentation>(unsafeTransaction: UnsafeDataTransaction, object: O) where O.ObjectType == ObjectType {
|
||||||
|
|
||||||
self.init(context: unsafeTransaction.context, object: object)
|
self.init(context: unsafeTransaction.context, objectID: object.cs_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
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<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) {
|
||||||
@@ -250,6 +273,7 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
|
private let objectID: ObjectType.ObjectID
|
||||||
private let fetchedResultsController: Internals.CoreStoreFetchedResultsController
|
private let fetchedResultsController: Internals.CoreStoreFetchedResultsController
|
||||||
private let fetchedResultsControllerDelegate: Internals.FetchedResultsControllerDelegate
|
private let fetchedResultsControllerDelegate: Internals.FetchedResultsControllerDelegate
|
||||||
private var lastCommittedAttributes = [String: NSObject]()
|
private var lastCommittedAttributes = [String: NSObject]()
|
||||||
@@ -258,9 +282,8 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
|||||||
private var didDeleteObjectKey: Void?
|
private var didDeleteObjectKey: Void?
|
||||||
private var didUpdateObjectKey: Void?
|
private var didUpdateObjectKey: Void?
|
||||||
|
|
||||||
private init(context: NSManagedObjectContext, object: ObjectType) {
|
private init(context: NSManagedObjectContext, objectID: ObjectType.ObjectID) {
|
||||||
|
|
||||||
let objectID = object.cs_id()
|
|
||||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||||
fetchRequest.entity = objectID.entity
|
fetchRequest.entity = objectID.entity
|
||||||
fetchRequest.fetchLimit = 0
|
fetchRequest.fetchLimit = 0
|
||||||
@@ -278,6 +301,7 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
|||||||
|
|
||||||
let fetchedResultsControllerDelegate = Internals.FetchedResultsControllerDelegate()
|
let fetchedResultsControllerDelegate = Internals.FetchedResultsControllerDelegate()
|
||||||
|
|
||||||
|
self.objectID = objectID
|
||||||
self.fetchedResultsController = fetchedResultsController
|
self.fetchedResultsController = fetchedResultsController
|
||||||
self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate
|
self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,76 @@
|
|||||||
// ObjectRepresentation.swift
|
// ObjectRepresentation.swift
|
||||||
// CoreStore
|
// CoreStore
|
||||||
//
|
//
|
||||||
// Created by John Estropia on 2019/10/11.
|
// Copyright © 2018 John Rommel Estropia
|
||||||
// Copyright © 2019 John Rommel Estropia. All rights reserved.
|
//
|
||||||
|
// 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 - 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
Used internally by CoreStore. Do not call directly.
|
||||||
|
*/
|
||||||
|
static func cs_fromRaw(object: NSManagedObject) -> Self
|
||||||
|
|
||||||
|
/**
|
||||||
|
Used internally by CoreStore. Do not call directly.
|
||||||
|
*/
|
||||||
|
func cs_id() -> ObjectType.ObjectID
|
||||||
|
|
||||||
|
/**
|
||||||
|
Used internally by CoreStore. Do not call directly.
|
||||||
|
*/
|
||||||
|
func cs_object() -> ObjectType?
|
||||||
|
|
||||||
|
/**
|
||||||
|
Used internally by CoreStore. Do not call directly.
|
||||||
|
*/
|
||||||
|
func cs_rawObject(in context: NSManagedObjectContext) -> NSManagedObject?
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NSManagedObject: ObjectRepresentation {}
|
||||||
|
|
||||||
|
extension CoreStoreObject: ObjectRepresentation {}
|
||||||
|
|
||||||
|
extension DynamicObject where Self: ObjectRepresentation {
|
||||||
|
|
||||||
|
public func cs_object() -> Self? {
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cs_rawObject(in context: NSManagedObjectContext) -> NSManagedObject? {
|
||||||
|
|
||||||
|
return context.fetchExisting(self.cs_id())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -99,43 +99,27 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deletes a specified `NSManagedObject` or `CoreStoreObject`.
|
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by series of `ObjectRepresentation`s.
|
||||||
|
|
||||||
- parameter object: the `NSManagedObject` or `CoreStoreObject` type 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 override func delete<D: DynamicObject>(_ object: D?) {
|
public override func delete<O: ObjectRepresentation>(_ object: O?, _ objects: O?...) {
|
||||||
|
|
||||||
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?...) {
|
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
!self.isCommitted,
|
!self.isCommitted,
|
||||||
"Attempted to delete an entities from an already committed \(Internals.typeName(self))."
|
"Attempted to delete an entities from an already committed \(Internals.typeName(self))."
|
||||||
)
|
)
|
||||||
|
|
||||||
super.delete(([object1, object2] + objects).compactMap { $0 })
|
super.delete(([object] + objects).compactMap { $0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deletes the specified `DynamicObject`s.
|
Deletes the specified `NSManagedObject`s or `CoreStoreObject`s represented by an `ObjectRepresenation`.
|
||||||
|
|
||||||
- parameter objects: the `DynamicObject`s to be deleted
|
- 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: DynamicObject {
|
public override func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: ObjectRepresentation {
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
!self.isCommitted,
|
!self.isCommitted,
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ import CoreData
|
|||||||
extension UnsafeDataTransaction {
|
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 a `ObjectMonitor` for the specified `ObjectRepresentation`. 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
|
- parameter object: the `ObjectRepresentation` to observe changes from
|
||||||
- returns: a `ObjectMonitor` that monitors changes to `object`
|
- returns: a `ObjectMonitor` that monitors changes to `object`
|
||||||
*/
|
*/
|
||||||
public func monitorObject<D>(_ object: D) -> ObjectMonitor<D> {
|
public func monitorObject<O: ObjectRepresentation>(_ object: O) -> ObjectMonitor<O.ObjectType> {
|
||||||
|
|
||||||
return ObjectMonitor(
|
return ObjectMonitor(
|
||||||
unsafeTransaction: self,
|
unsafeTransaction: self,
|
||||||
|
|||||||
Reference in New Issue
Block a user