mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-31 14:43:09 +02:00
WIP: ObjectRepresentable
This commit is contained in:
@@ -50,8 +50,6 @@ public final class LiveObject<O: DynamicObject>: ObjectRepresentation, Hashable
|
|||||||
|
|
||||||
return self.lazySnapshot
|
return self.lazySnapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
public private(set) lazy var object: O = self.context.fetchExisting(self.objectID)!
|
|
||||||
|
|
||||||
public func addObserver<T: AnyObject>(_ observer: T, _ callback: @escaping (LiveObject<O>) -> Void) {
|
public func addObserver<T: AnyObject>(_ observer: T, _ callback: @escaping (LiveObject<O>) -> Void) {
|
||||||
|
|
||||||
@@ -76,27 +74,49 @@ public final class LiveObject<O: DynamicObject>: ObjectRepresentation, Hashable
|
|||||||
|
|
||||||
public typealias ObjectType = O
|
public typealias ObjectType = O
|
||||||
|
|
||||||
public static func cs_fromRaw(object: NSManagedObject) -> Self {
|
public func objectID() -> O.ObjectID {
|
||||||
|
|
||||||
return self.init(
|
return self.id
|
||||||
objectID: object.objectID,
|
|
||||||
context: object.managedObjectContext!
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func cs_id() -> O.ObjectID {
|
public func asLiveObject(in dataStack: DataStack) -> LiveObject<O>? {
|
||||||
|
|
||||||
return self.objectID
|
let context = dataStack.unsafeContext()
|
||||||
|
if self.context == context {
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
return Self.init(objectID: self.id, context: context)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func cs_object() -> O? {
|
public func asEditable(in transaction: BaseDataTransaction) -> O? {
|
||||||
|
|
||||||
return self.context.fetchExisting(self.objectID)
|
return self.context.fetchExisting(self.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func cs_rawObject(in context: NSManagedObjectContext) -> NSManagedObject? {
|
public func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot<O>? {
|
||||||
|
|
||||||
return self.object.cs_toRaw()
|
let context = dataStack.unsafeContext()
|
||||||
|
if self.context == context {
|
||||||
|
|
||||||
|
return self.lazySnapshot
|
||||||
|
}
|
||||||
|
return .init(id: self.id, context: context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot<O>? {
|
||||||
|
|
||||||
|
let context = transaction.unsafeContext()
|
||||||
|
if self.context == context {
|
||||||
|
|
||||||
|
return self.lazySnapshot
|
||||||
|
}
|
||||||
|
return .init(id: self.id, context: context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asObjectMonitor(in dataStack: DataStack) -> ObjectMonitor<O>? {
|
||||||
|
|
||||||
|
return .init(objectID: self.id, context: dataStack.unsafeContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -104,7 +124,7 @@ public final class LiveObject<O: DynamicObject>: ObjectRepresentation, Hashable
|
|||||||
|
|
||||||
public static func == (_ lhs: LiveObject, _ rhs: LiveObject) -> Bool {
|
public static func == (_ lhs: LiveObject, _ rhs: LiveObject) -> Bool {
|
||||||
|
|
||||||
return lhs.objectID == rhs.objectID
|
return lhs.id == rhs.id
|
||||||
&& lhs.context == rhs.context
|
&& lhs.context == rhs.context
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +133,7 @@ public final class LiveObject<O: DynamicObject>: ObjectRepresentation, Hashable
|
|||||||
|
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
|
|
||||||
hasher.combine(self.objectID)
|
hasher.combine(self.id)
|
||||||
hasher.combine(self.context)
|
hasher.combine(self.context)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +161,7 @@ public final class LiveObject<O: DynamicObject>: ObjectRepresentation, Hashable
|
|||||||
|
|
||||||
fileprivate init(objectID: O.ObjectID, context: NSManagedObjectContext, initializer: @escaping (NSManagedObjectID, NSManagedObjectContext) -> ObjectSnapshot<O>) {
|
fileprivate init(objectID: O.ObjectID, context: NSManagedObjectContext, initializer: @escaping (NSManagedObjectID, NSManagedObjectContext) -> ObjectSnapshot<O>) {
|
||||||
|
|
||||||
self.objectID = objectID
|
self.id = 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, *) {
|
||||||
|
|
||||||
@@ -175,7 +195,7 @@ public final class LiveObject<O: DynamicObject>: ObjectRepresentation, Hashable
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let objectID: O.ObjectID
|
private let id: O.ObjectID
|
||||||
private let context: NSManagedObjectContext
|
private let context: NSManagedObjectContext
|
||||||
|
|
||||||
@Internals.LazyNonmutating(uninitialized: ())
|
@Internals.LazyNonmutating(uninitialized: ())
|
||||||
|
|||||||
@@ -122,6 +122,44 @@ public final class ObjectMonitor<D: DynamicObject>: ObjectRepresentation, Equata
|
|||||||
|
|
||||||
// MARK: ObjectRepresentation
|
// MARK: ObjectRepresentation
|
||||||
|
|
||||||
|
public func objectID() -> D.ObjectID {
|
||||||
|
|
||||||
|
return self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asLiveObject(in dataStack: DataStack) -> LiveObject<D>? {
|
||||||
|
|
||||||
|
let context = dataStack.unsafeContext()
|
||||||
|
return .init(objectID: self.id, context: context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asEditable(in transaction: BaseDataTransaction) -> D? {
|
||||||
|
|
||||||
|
return self.context.fetchExisting(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot<D>? {
|
||||||
|
|
||||||
|
let context = dataStack.unsafeContext()
|
||||||
|
return .init(id: self.id, context: context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot<D>? {
|
||||||
|
|
||||||
|
let context = transaction.unsafeContext()
|
||||||
|
return .init(id: self.id, context: context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asObjectMonitor(in dataStack: DataStack) -> ObjectMonitor<D>? {
|
||||||
|
|
||||||
|
let context = dataStack.unsafeContext()
|
||||||
|
if self.context == context {
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
return .init(objectID: self.id, context: dataStack.unsafeContext())
|
||||||
|
}
|
||||||
|
|
||||||
public typealias ObjectType = D
|
public typealias ObjectType = D
|
||||||
|
|
||||||
public static func cs_fromRaw(object: NSManagedObject) -> Self {
|
public static func cs_fromRaw(object: NSManagedObject) -> Self {
|
||||||
@@ -181,14 +219,34 @@ public final class ObjectMonitor<D: DynamicObject>: ObjectRepresentation, Equata
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal convenience init<O: ObjectRepresentation>(dataStack: DataStack, object: O) where O.ObjectType == ObjectType {
|
internal init(objectID: ObjectType.ObjectID, context: NSManagedObjectContext) {
|
||||||
|
|
||||||
self.init(context: dataStack.mainContext, objectID: object.cs_id())
|
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||||
}
|
fetchRequest.entity = objectID.entity
|
||||||
|
fetchRequest.fetchLimit = 0
|
||||||
internal convenience init<O: ObjectRepresentation>(unsafeTransaction: UnsafeDataTransaction, object: O) where O.ObjectType == ObjectType {
|
fetchRequest.resultType = .managedObjectResultType
|
||||||
|
fetchRequest.sortDescriptors = []
|
||||||
|
fetchRequest.includesPendingChanges = false
|
||||||
|
fetchRequest.shouldRefreshRefetchedObjects = true
|
||||||
|
|
||||||
self.init(context: unsafeTransaction.context, objectID: object.cs_id())
|
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.objectID = 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 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) {
|
||||||
@@ -282,36 +340,6 @@ public final class ObjectMonitor<D: DynamicObject>: ObjectRepresentation, Equata
|
|||||||
private var didDeleteObjectKey: Void?
|
private var didDeleteObjectKey: Void?
|
||||||
private var didUpdateObjectKey: Void?
|
private var didUpdateObjectKey: Void?
|
||||||
|
|
||||||
private init(context: NSManagedObjectContext, objectID: ObjectType.ObjectID) {
|
|
||||||
|
|
||||||
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.objectID = 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]) ?? [:]
|
|
||||||
}
|
|
||||||
|
|
||||||
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<ObjectType>) -> Void) {
|
||||||
|
|
||||||
Internals.setAssociatedRetainedObject(
|
Internals.setAssociatedRetainedObject(
|
||||||
|
|||||||
@@ -39,35 +39,66 @@ public protocol ObjectRepresentation {
|
|||||||
associatedtype ObjectType: DynamicObject
|
associatedtype ObjectType: DynamicObject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Used internally by CoreStore. Do not call directly.
|
The internal ID for the object.
|
||||||
*/
|
*/
|
||||||
static func cs_fromRaw(object: NSManagedObject) -> Self
|
func objectID() -> ObjectType.ObjectID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Used internally by CoreStore. Do not call directly.
|
An instance that may be observed for object changes.
|
||||||
*/
|
*/
|
||||||
func cs_id() -> ObjectType.ObjectID
|
func asLiveObject(in dataStack: DataStack) -> LiveObject<ObjectType>?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Used internally by CoreStore. Do not call directly.
|
An instance that may be mutated within a transaction.
|
||||||
*/
|
*/
|
||||||
func cs_object() -> ObjectType?
|
func asEditable(in transaction: BaseDataTransaction) -> ObjectType?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Used internally by CoreStore. Do not call directly.
|
A thread-safe `struct` that is a full-copy of the object's properties
|
||||||
*/
|
*/
|
||||||
func cs_rawObject(in context: NSManagedObjectContext) -> NSManagedObject?
|
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>?
|
||||||
|
|
||||||
|
/**
|
||||||
|
An instance that may be observed for property-specific changes.
|
||||||
|
*/
|
||||||
|
func asObjectMonitor(in dataStack: DataStack) -> ObjectMonitor<ObjectType>?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NSManagedObject: ObjectRepresentation {}
|
extension LiveObject {
|
||||||
|
|
||||||
|
public static func cs_object(in context: NSManagedObjectContext) -> LiveObject<O>? {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension CoreStoreObject: ObjectRepresentation {}
|
extension ObjectMonitor {
|
||||||
|
|
||||||
|
public static func cs_object(in context: NSManagedObjectContext) -> LiveObject<D>? {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NSManagedObject: ObjectRepresentation {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CoreStoreObject: ObjectRepresentation {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extension DynamicObject where Self: ObjectRepresentation {
|
extension DynamicObject where Self: ObjectRepresentation {
|
||||||
|
|
||||||
public func cs_object() -> Self? {
|
public static func cs_object(in context: NSManagedObjectContext) -> LiveObject<Self>? {
|
||||||
|
|
||||||
return self
|
fatalError()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func cs_rawObject(in context: NSManagedObjectContext) -> NSManagedObject? {
|
public func cs_rawObject(in context: NSManagedObjectContext) -> NSManagedObject? {
|
||||||
|
|||||||
@@ -37,16 +37,55 @@ import AppKit
|
|||||||
// MARK: - ObjectSnapshot
|
// MARK: - ObjectSnapshot
|
||||||
|
|
||||||
@dynamicMemberLookup
|
@dynamicMemberLookup
|
||||||
public struct ObjectSnapshot<O: DynamicObject>: SnapshotResult, Identifiable, Hashable {
|
public struct ObjectSnapshot<O: DynamicObject>: SnapshotResult, ObjectRepresentation, Hashable {
|
||||||
|
|
||||||
// MARK: SnapshotResult
|
// MARK: SnapshotResult
|
||||||
|
|
||||||
public typealias ObjectType = O
|
public typealias ObjectType = O
|
||||||
|
|
||||||
|
|
||||||
// MARK: Identifiable
|
// MARK: ObjectRepresentation
|
||||||
|
|
||||||
public let id: O.ObjectID
|
public func objectID() -> O.ObjectID {
|
||||||
|
|
||||||
|
return self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asLiveObject(in dataStack: DataStack) -> LiveObject<O>? {
|
||||||
|
|
||||||
|
let context = dataStack.unsafeContext()
|
||||||
|
return .init(objectID: self.id, context: context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asEditable(in transaction: BaseDataTransaction) -> O? {
|
||||||
|
|
||||||
|
return self.context.fetchExisting(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot<O>? {
|
||||||
|
|
||||||
|
let context = dataStack.unsafeContext()
|
||||||
|
if self.context == context {
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
return .init(id: self.id, context: context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot<O>? {
|
||||||
|
|
||||||
|
let context = transaction.unsafeContext()
|
||||||
|
if self.context == context {
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
return .init(id: self.id, context: context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func asObjectMonitor(in dataStack: DataStack) -> ObjectMonitor<O>? {
|
||||||
|
|
||||||
|
return .init(objectID: self.id, context: dataStack.unsafeContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Equatable
|
// MARK: Equatable
|
||||||
@@ -79,6 +118,7 @@ public struct ObjectSnapshot<O: DynamicObject>: SnapshotResult, Identifiable, Ha
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
|
private let id: O.ObjectID
|
||||||
private let context: NSManagedObjectContext
|
private let context: NSManagedObjectContext
|
||||||
private let values: NSDictionary
|
private let values: NSDictionary
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user