diff --git a/Sources/AttributeProtocol.swift b/Sources/AttributeProtocol.swift index 37aa359..df70185 100644 --- a/Sources/AttributeProtocol.swift +++ b/Sources/AttributeProtocol.swift @@ -43,5 +43,5 @@ internal protocol AttributeProtocol: PropertyProtocol { var rawObject: CoreStoreManagedObject? { get set } var getter: CoreStoreManagedObject.CustomGetter? { get } var setter: CoreStoreManagedObject.CustomSetter? { get } - var valueForSnapshot: Any { get } + var valueForSnapshot: Any? { get } } diff --git a/Sources/CoreStore+CustomDebugStringConvertible.swift b/Sources/CoreStore+CustomDebugStringConvertible.swift index 40324bc..df4aad7 100644 --- a/Sources/CoreStore+CustomDebugStringConvertible.swift +++ b/Sources/CoreStore+CustomDebugStringConvertible.swift @@ -374,15 +374,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, *) @@ -410,6 +429,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 { @@ -569,6 +638,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 { diff --git a/Sources/DataStack+DataSources.swift b/Sources/DataStack+DataSources.swift index cba6d2a..306761a 100644 --- a/Sources/DataStack+DataSources.swift +++ b/Sources/DataStack+DataSources.swift @@ -41,7 +41,18 @@ extension DataStack { */ public func publishObject(_ object: O) -> ObjectPublisher { - return ObjectPublisher(objectID: object.cs_id(), context: self.unsafeContext()) + 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(_ objectID: O.ObjectID) -> ObjectPublisher { + + return ObjectPublisher(objectID: objectID, context: self.unsafeContext()) } /** diff --git a/Sources/DiffableDataSource.CollectionView-UIKit.swift b/Sources/DiffableDataSource.CollectionView-UIKit.swift index 985905f..04943f0 100644 --- a/Sources/DiffableDataSource.CollectionView-UIKit.swift +++ b/Sources/DiffableDataSource.CollectionView-UIKit.swift @@ -120,7 +120,7 @@ extension DiffableDataSource { let diffableSnapshot = snapshot.diffableSnapshot self.dispatcher.apply( - diffableSnapshot as! Internals.DiffableDataSourceSnapshot, + diffableSnapshot, view: self.collectionView, animatingDifferences: animatingDifferences, performUpdates: { collectionView, changeset, setSections in diff --git a/Sources/DiffableDataSource.TableView-UIKit.swift b/Sources/DiffableDataSource.TableView-UIKit.swift index a57cb89..35928b2 100644 --- a/Sources/DiffableDataSource.TableView-UIKit.swift +++ b/Sources/DiffableDataSource.TableView-UIKit.swift @@ -127,31 +127,19 @@ extension DiffableDataSource { @nonobjc public func apply(_ snapshot: ListSnapshot, animatingDifferences: Bool = true) { - let diffableSnapshot = snapshot.diffableSnapshot -// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) { -// -// self.modernDataSource.apply( -// diffableSnapshot as! NSDiffableDataSourceSnapshot, -// animatingDifferences: animatingDifferences, -// completion: nil -// ) -// } -// else { - - self.dispatcher.apply( - diffableSnapshot as! Internals.DiffableDataSourceSnapshot, - view: self.tableView, - animatingDifferences: animatingDifferences, - performUpdates: { tableView, changeset, setSections in - - tableView.reload( - using: changeset, - with: self.defaultRowAnimation, - setData: setSections - ) - } - ) -// } + self.dispatcher.apply( + snapshot.diffableSnapshot, + view: self.tableView, + animatingDifferences: animatingDifferences, + performUpdates: { tableView, changeset, setSections in + + tableView.reload( + using: changeset, + with: self.defaultRowAnimation, + setData: setSections + ) + } + ) } /** diff --git a/Sources/Internals.FetchedDiffableDataSourceSnapshotDelegate.swift b/Sources/Internals.FetchedDiffableDataSourceSnapshotDelegate.swift index 4348422..60e3b2d 100644 --- a/Sources/Internals.FetchedDiffableDataSourceSnapshotDelegate.swift +++ b/Sources/Internals.FetchedDiffableDataSourceSnapshotDelegate.swift @@ -41,9 +41,6 @@ import AppKit internal protocol FetchedDiffableDataSourceSnapshotHandler: AnyObject { -// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -// func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: NSDiffableDataSourceSnapshot) - func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: Internals.DiffableDataSourceSnapshot) func controller(_ controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? @@ -80,15 +77,6 @@ extension Internals { internal func initialFetch() { -// #if canImport(UIKit) || canImport(AppKit) -// -// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) { -// -// return -// } -// -// #endif - guard let fetchedResultsController = self.fetchedResultsController else { return @@ -98,20 +86,6 @@ extension Internals { // MARK: NSFetchedResultsControllerDelegate - -// #if canImport(UIKit) || canImport(AppKit) -// -// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -// @objc -// dynamic func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) { -// -// self.handler?.controller( -// controller, -// didChangeContentWith: snapshot as NSDiffableDataSourceSnapshot -// ) -// } -// -// #endif @objc dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController) { diff --git a/Sources/ListPublisher.swift b/Sources/ListPublisher.swift index d09b7b2..71881e1 100644 --- a/Sources/ListPublisher.swift +++ b/Sources/ListPublisher.swift @@ -397,15 +397,6 @@ public final class ListPublisher: Hashable { extension ListPublisher: FetchedDiffableDataSourceSnapshotHandler { // MARK: FetchedDiffableDataSourceSnapshotHandler - -// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -// internal func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: NSDiffableDataSourceSnapshot) { -// -// self.snapshot = .init( -// diffableSnapshot: snapshot, -// context: controller.managedObjectContext -// ) -// } internal func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: Internals.DiffableDataSourceSnapshot) { diff --git a/Sources/ListSnapshot.swift b/Sources/ListSnapshot.swift index 2ec94b7..ca13a77 100644 --- a/Sources/ListSnapshot.swift +++ b/Sources/ListSnapshot.swift @@ -629,28 +629,14 @@ public struct ListSnapshot: RandomAccessCollection, Hashable { // MARK: Internal - internal private(set) var diffableSnapshot: DiffableDataSourceSnapshotProtocol + internal private(set) var diffableSnapshot: Internals.DiffableDataSourceSnapshot internal init() { -// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) { -// -// self.diffableSnapshot = NSDiffableDataSourceSnapshot() -// } -// else { - - self.diffableSnapshot = Internals.DiffableDataSourceSnapshot() -// } + self.diffableSnapshot = Internals.DiffableDataSourceSnapshot() self.context = nil } -// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -// internal init(diffableSnapshot: NSDiffableDataSourceSnapshot, context: NSManagedObjectContext) { -// -// self.diffableSnapshot = diffableSnapshot -// self.context = context -// } - internal init(diffableSnapshot: Internals.DiffableDataSourceSnapshot, context: NSManagedObjectContext) { self.diffableSnapshot = diffableSnapshot diff --git a/Sources/NSManagedObjectContext+CoreStore.swift b/Sources/NSManagedObjectContext+CoreStore.swift index 1e3a636..fe54272 100644 --- a/Sources/NSManagedObjectContext+CoreStore.swift +++ b/Sources/NSManagedObjectContext+CoreStore.swift @@ -178,9 +178,9 @@ extension NSManagedObjectContext { private func userInfo(for key: UserInfoKeys, initialize: @escaping () -> T) -> T { let keyString = key.keyString - if let value = self.userInfo[keyString] { + if let value = self.userInfo[keyString] as? T { - return value as! T + return value } let value = initialize() self.userInfo[keyString] = value diff --git a/Sources/ObjectPublisher.swift b/Sources/ObjectPublisher.swift index 98718f5..ce2cfde 100644 --- a/Sources/ObjectPublisher.swift +++ b/Sources/ObjectPublisher.swift @@ -336,7 +336,6 @@ extension ObjectPublisher { // MARK: - ObjectPublisher where O: NSManagedObject -@available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)") extension ObjectPublisher where O: NSManagedObject { // MARK: Public @@ -344,11 +343,21 @@ extension ObjectPublisher where O: NSManagedObject { /** 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(dynamicMember member: KeyPath) -> V { fatalError() // return self.snapshot[dynamicMember: member] } + + /** + Returns the value for the property identified by a given key. + */ + public func value(forKeyPath keyPath: KeyPath) -> V! { + + let key = String(keyPath: keyPath) + return self.snapshot?.dictionaryForValues()[key] as! V? + } } diff --git a/Sources/ObjectRepresentation.swift b/Sources/ObjectRepresentation.swift index 3eb2f64..863366c 100644 --- a/Sources/ObjectRepresentation.swift +++ b/Sources/ObjectRepresentation.swift @@ -75,6 +75,19 @@ extension CoreStoreObject: ObjectRepresentation {} extension DynamicObject where Self: ObjectRepresentation { + // MARK: Public + + /** + A thread-safe `struct` that is a full-copy of the object's properties + */ + public func asSnapshot() -> ObjectSnapshot? { + + return self.cs_toRaw() + .managedObjectContext + .flatMap({ ObjectSnapshot(objectID: self.cs_id(), context: $0) }) + } + + // MARK: ObjectRepresentation public func objectID() -> Self.ObjectID { diff --git a/Sources/ObjectSnapshot.swift b/Sources/ObjectSnapshot.swift index 92dd900..f01cbff 100644 --- a/Sources/ObjectSnapshot.swift +++ b/Sources/ObjectSnapshot.swift @@ -41,6 +41,14 @@ import AppKit */ @dynamicMemberLookup public struct ObjectSnapshot: ObjectRepresentation, Hashable { + + // MARK: Public + + public func dictionaryForValues() -> [String: Any] { + + return self.values + } + // MARK: ObjectRepresentation @@ -112,11 +120,15 @@ public struct ObjectSnapshot: ObjectRepresentation, Hashable { } + // MARK: FilePrivate + + fileprivate var values: [String: Any] + + // MARK: Private private let id: O.ObjectID private let context: NSManagedObjectContext - private var values: [String: Any] private var valuesRef: NSDictionary { @@ -127,17 +139,35 @@ public struct ObjectSnapshot: ObjectRepresentation, Hashable { // MARK: - ObjectSnapshot where O: NSManagedObject -@available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)") extension ObjectSnapshot where O: NSManagedObject { /** 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(dynamicMember member: KeyPath) -> V { 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(forKeyPath keyPath: KeyPath) -> 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(_ value: V!, forKeyPath keyPath: KeyPath) { + + let key = String(keyPath: keyPath) + self.values[key] = value + } } @@ -170,7 +200,7 @@ extension ObjectSnapshot where O: CoreStoreObject { get { let key = String(keyPath: member) - return self.values[key] as! V? + return self.values[key] as? V } set { @@ -204,7 +234,7 @@ extension ObjectSnapshot where O: CoreStoreObject { get { let key = String(keyPath: member) - return self.values[key] as! V? + return self.values[key] as? V } set { @@ -221,7 +251,7 @@ extension ObjectSnapshot where O: CoreStoreObject { get { let key = String(keyPath: member) - guard let id = self.values[key] as! D.ObjectID? else { + guard let id = self.values[key] as? D.ObjectID else { return nil } diff --git a/Sources/Relationship.swift b/Sources/Relationship.swift index 120a799..74c72d5 100644 --- a/Sources/Relationship.swift +++ b/Sources/Relationship.swift @@ -313,9 +313,9 @@ public enum RelationshipContainer { } } - internal var valueForSnapshot: Any { + internal var valueForSnapshot: Any? { - return self.value?.objectID() as Any + return self.value?.objectID() } @@ -609,9 +609,9 @@ public enum RelationshipContainer { } } - internal var valueForSnapshot: Any { + internal var valueForSnapshot: Any? { - return self.value.map({ $0.objectID() }) as Any + return self.value.map({ $0.objectID() }) } @@ -910,9 +910,9 @@ public enum RelationshipContainer { } } - internal var valueForSnapshot: Any { + internal var valueForSnapshot: Any? { - return Set(self.value.map({ $0.objectID() })) as Any + return Set(self.value.map({ $0.objectID() })) } diff --git a/Sources/RelationshipProtocol.swift b/Sources/RelationshipProtocol.swift index b2269a5..0f53b88 100644 --- a/Sources/RelationshipProtocol.swift +++ b/Sources/RelationshipProtocol.swift @@ -41,5 +41,5 @@ internal protocol RelationshipProtocol: PropertyProtocol { var renamingIdentifier: () -> String? { get } var minCount: Int { get } var maxCount: Int { get } - var valueForSnapshot: Any { get } + var valueForSnapshot: Any? { get } } diff --git a/Sources/Transformable.swift b/Sources/Transformable.swift index f26ed6c..5129047 100644 --- a/Sources/Transformable.swift +++ b/Sources/Transformable.swift @@ -272,9 +272,9 @@ public enum TransformableContainer { } } - internal var valueForSnapshot: Any { + internal var valueForSnapshot: Any? { - return self.value as Any + return self.value } @@ -494,9 +494,9 @@ public enum TransformableContainer { } } - internal var valueForSnapshot: Any { + internal var valueForSnapshot: Any? { - return self.value as Any + return self.value } diff --git a/Sources/Value.swift b/Sources/Value.swift index 3951192..c66412d 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -310,9 +310,9 @@ public enum ValueContainer { } } - internal var valueForSnapshot: Any { + internal var valueForSnapshot: Any? { - return self.value as Any + return self.value } @@ -575,9 +575,9 @@ public enum ValueContainer { } } - internal var valueForSnapshot: Any { + internal var valueForSnapshot: Any? { - return self.value as Any + return self.value }