diff --git a/Sources/DataStack+DataSources.swift b/Sources/DataStack+DataSources.swift index 306761a..a1817f4 100644 --- a/Sources/DataStack+DataSources.swift +++ b/Sources/DataStack+DataSources.swift @@ -52,7 +52,8 @@ extension DataStack { */ public func publishObject(_ objectID: O.ObjectID) -> ObjectPublisher { - return ObjectPublisher(objectID: objectID, context: self.unsafeContext()) + let context = self.unsafeContext() + return context.objectPublisher(objectID: objectID) } /** diff --git a/Sources/Internals.DiffableDataSourceSnapshot.swift b/Sources/Internals.DiffableDataSourceSnapshot.swift index a587090..2aca088 100644 --- a/Sources/Internals.DiffableDataSourceSnapshot.swift +++ b/Sources/Internals.DiffableDataSourceSnapshot.swift @@ -121,6 +121,16 @@ extension Internals { return self.structure.allSectionIDs.firstIndex(of: identifier) } + func isReloaded(sectionID: String) -> Bool? { + + return self.structure.isReloaded(sectionID: sectionID) + } + + func isReloaded(itemID: NSManagedObjectID) -> Bool? { + + return self.structure.isReloaded(itemID: itemID) + } + mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: String?) { self.structure.append(itemIDs: identifiers, to: sectionIdentifier) @@ -329,6 +339,24 @@ extension Internals { return self.sections.lazy.flatMap({ $0.elements }).map({ $0.differenceIdentifier }) } + func isReloaded(sectionID: String) -> Bool? { + + return self.sections.first(where: { $0.differenceIdentifier == sectionID })?.isReloaded + } + + func isReloaded(itemID: NSManagedObjectID) -> Bool? { + + for section in self.sections { + + guard let item = section.elements.first(where: { $0.differenceIdentifier == itemID }) else { + + return nil + } + return item.isReloaded + } + return nil + } + func items(in sectionID: String) -> [NSManagedObjectID] { guard let sectionIndex = self.sectionIndex(of: sectionID) else { @@ -340,7 +368,7 @@ extension Internals { func section(containing itemID: NSManagedObjectID) -> String? { - return self.itemPositionMap()[itemID]?.section.differenceIdentifier + return self.itemPositionMap(itemID)?.section.differenceIdentifier } mutating func append(itemIDs: [NSManagedObjectID], to sectionID: String?) { @@ -369,7 +397,7 @@ extension Internals { mutating func insert(itemIDs: [NSManagedObjectID], before beforeItemID: NSManagedObjectID) { - guard let itemPosition = self.itemPositionMap()[beforeItemID] else { + guard let itemPosition = self.itemPositionMap(beforeItemID) else { Internals.abort("Item \(beforeItemID) does not exist") } @@ -380,7 +408,7 @@ extension Internals { mutating func insert(itemIDs: [NSManagedObjectID], after afterItemID: NSManagedObjectID) { - guard let itemPosition = self.itemPositionMap()[afterItemID] else { + guard let itemPosition = self.itemPositionMap(afterItemID) else { Internals.abort("Item \(afterItemID) does not exist") } @@ -433,7 +461,7 @@ extension Internals { Internals.abort("Item \(itemID) does not exist") } - guard let itemPosition = self.itemPositionMap()[beforeItemID] else { + guard let itemPosition = self.itemPositionMap(beforeItemID) else { Internals.abort("Item \(beforeItemID) does not exist") } @@ -447,7 +475,7 @@ extension Internals { Internals.abort("Item \(itemID) does not exist") } - guard let itemPosition = self.itemPositionMap()[afterItemID] else { + guard let itemPosition = self.itemPositionMap(afterItemID) else { Internals.abort("Item \(afterItemID) does not exist") } @@ -556,7 +584,7 @@ extension Internals { @discardableResult private mutating func remove(itemID: NSManagedObjectID) -> Item? { - guard let itemPosition = self.itemPositionMap()[itemID] else { + guard let itemPosition = self.itemPositionMap(itemID) else { return nil } @@ -574,6 +602,28 @@ extension Internals { 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 diff --git a/Sources/ListSnapshot.swift b/Sources/ListSnapshot.swift index ca13a77..bd47761 100644 --- a/Sources/ListSnapshot.swift +++ b/Sources/ListSnapshot.swift @@ -214,6 +214,28 @@ public struct ListSnapshot: RandomAccessCollection, Hashable { return snapshot.numberOfItems(inSection: sectionID) > 0 } + /** + Checks if the `ListSnapshot` has changes in the specified section. + + - parameter sectionID: the section identifier. Using an index outside the valid range will return `nil`. + - returns: `true` if the specified section has changes, `false` if no changes, or `nil` if the `sectionID` does not exist + */ + public func hasChanges(sectionID: String) -> Bool? { + + return self.diffableSnapshot.isReloaded(sectionID: sectionID) + } + + /** + Checks if the `ListSnapshot` has changes for the specified item. + + - parameter itemID: the item identifier. Using an index outside the valid range will return `nil`. + - returns: `true` if the specified item has changes, `false` if no changes, or `nil` if the `itemID` does not exist + */ + public func hasChanges(itemID: NSManagedObjectID) -> Bool? { + + return self.diffableSnapshot.isReloaded(itemID: itemID) + } + /** The number of items in all sections in the `ListSnapshot` */ @@ -340,7 +362,7 @@ public struct ListSnapshot: RandomAccessCollection, Hashable { return indices.map { position in let itemID = itemIDs[position] - return ObjectPublisher(objectID: itemID, context: context) + return context.objectPublisher(objectID: itemID) } } @@ -354,10 +376,7 @@ public struct ListSnapshot: RandomAccessCollection, Hashable { let context = self.context! let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID) - return itemIDs.map { - - return ObjectPublisher(objectID: $0, context: context) - } + return itemIDs.map(context.objectPublisher(objectID:)) } /** @@ -374,7 +393,7 @@ public struct ListSnapshot: RandomAccessCollection, Hashable { return itemIndices.map { position in let itemID = itemIDs[position] - return ObjectPublisher(objectID: itemID, context: context) + return context.objectPublisher(objectID: itemID) } } @@ -391,7 +410,7 @@ public struct ListSnapshot: RandomAccessCollection, Hashable { return indices.lazy.map { position in let itemID = itemIDs[position] - return ObjectPublisher(objectID: itemID, context: context) + return context.objectPublisher(objectID: itemID) } } @@ -405,10 +424,7 @@ public struct ListSnapshot: RandomAccessCollection, Hashable { let context = self.context! let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID) - return itemIDs.lazy.map { - - return ObjectPublisher(objectID: $0, context: context) - } + return itemIDs.lazy.map(context.objectPublisher(objectID:)) } /** @@ -425,7 +441,7 @@ public struct ListSnapshot: RandomAccessCollection, Hashable { return itemIndices.lazy.map { position in let itemID = itemIDs[position] - return ObjectPublisher(objectID: itemID, context: context) + return context.objectPublisher(objectID: itemID) } } diff --git a/Sources/NSManagedObjectContext+CoreStore.swift b/Sources/NSManagedObjectContext+CoreStore.swift index fe54272..3602d65 100644 --- a/Sources/NSManagedObjectContext+CoreStore.swift +++ b/Sources/NSManagedObjectContext+CoreStore.swift @@ -99,7 +99,7 @@ extension NSManagedObjectContext { return objectPublisher } - let objectPublisher = ObjectPublisher(objectID: objectID, context: self) + let objectPublisher = ObjectPublisher.createUncached(objectID: objectID, context: self) cache.setObject(objectPublisher, forKey: objectID) return objectPublisher } diff --git a/Sources/ObjectPublisher.swift b/Sources/ObjectPublisher.swift index ce2cfde..d2ff520 100644 --- a/Sources/ObjectPublisher.swift +++ b/Sources/ObjectPublisher.swift @@ -130,7 +130,7 @@ public final class ObjectPublisher: ObjectRepresentation, Hash return self } - return Self.init(objectID: self.id, context: context) + return context.objectPublisher(objectID: self.id) } public func asReadOnly(in dataStack: DataStack) -> O? { @@ -184,9 +184,9 @@ public final class ObjectPublisher: ObjectRepresentation, Hash // MARK: Internal - internal convenience init(objectID: O.ObjectID, context: NSManagedObjectContext) { + internal static func createUncached(objectID: O.ObjectID, context: NSManagedObjectContext) -> ObjectPublisher { - self.init( + return self.init( objectID: objectID, context: context, initializer: ObjectSnapshot.init(objectID:context:) diff --git a/Sources/ObjectRepresentation.swift b/Sources/ObjectRepresentation.swift index 863366c..a9d4101 100644 --- a/Sources/ObjectRepresentation.swift +++ b/Sources/ObjectRepresentation.swift @@ -98,7 +98,7 @@ extension DynamicObject where Self: ObjectRepresentation { public func asPublisher(in dataStack: DataStack) -> ObjectPublisher { let context = dataStack.unsafeContext() - return ObjectPublisher(objectID: self.cs_id(), context: context) + return context.objectPublisher(objectID: self.cs_id()) } public func asReadOnly(in dataStack: DataStack) -> Self? { diff --git a/Sources/ObjectSnapshot.swift b/Sources/ObjectSnapshot.swift index f01cbff..9eb5a9f 100644 --- a/Sources/ObjectSnapshot.swift +++ b/Sources/ObjectSnapshot.swift @@ -62,7 +62,7 @@ public struct ObjectSnapshot: ObjectRepresentation, Hashable { public func asPublisher(in dataStack: DataStack) -> ObjectPublisher { let context = dataStack.unsafeContext() - return ObjectPublisher(objectID: self.id, context: context) + return context.objectPublisher(objectID: self.id) } public func asReadOnly(in dataStack: DataStack) -> O? {