mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-04-19 15:31:21 +02:00
improved caching in utility methods
This commit is contained in:
@@ -51,6 +51,6 @@
|
|||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>UIUserInterfaceStyle</key>
|
<key>UIUserInterfaceStyle</key>
|
||||||
<string>light</string>
|
<string>Light</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ struct SwiftUIView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
List {
|
List {
|
||||||
ForEach(palettes.sections, id: \.self) { (sectionID) in
|
ForEach(palettes.sectionIdentifiers, id: \.self) { (sectionID) in
|
||||||
Section(header: Text(sectionID)) {
|
Section(header: Text(sectionID)) {
|
||||||
ForEach(self.palettes[section: sectionID], id: \.self) { palette in
|
ForEach(self.palettes[section: sectionID], id: \.self) { palette in
|
||||||
NavigationLink(
|
NavigationLink(
|
||||||
@@ -118,7 +118,6 @@ struct SwiftUIView: View {
|
|||||||
self.needsShowAlert = true
|
self.needsShowAlert = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.colorScheme(.dark)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@State
|
@State
|
||||||
|
|||||||
@@ -31,4 +31,32 @@ import CoreData
|
|||||||
|
|
||||||
internal protocol DiffableDataSourceSnapshotProtocol {
|
internal protocol DiffableDataSourceSnapshotProtocol {
|
||||||
|
|
||||||
|
init()
|
||||||
|
|
||||||
|
var numberOfItems: Int { get }
|
||||||
|
var numberOfSections: Int { get }
|
||||||
|
var sectionIdentifiers: [String] { get }
|
||||||
|
var itemIdentifiers: [NSManagedObjectID] { get }
|
||||||
|
|
||||||
|
func numberOfItems(inSection identifier: String) -> Int
|
||||||
|
func itemIdentifiers(inSection identifier: String) -> [NSManagedObjectID]
|
||||||
|
func sectionIdentifier(containingItem identifier: NSManagedObjectID) -> String?
|
||||||
|
func indexOfItem(_ identifier: NSManagedObjectID) -> Int?
|
||||||
|
func indexOfSection(_ identifier: String) -> Int?
|
||||||
|
|
||||||
|
mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: String?)
|
||||||
|
mutating func insertItems(_ identifiers: [NSManagedObjectID], beforeItem beforeIdentifier: NSManagedObjectID)
|
||||||
|
mutating func insertItems(_ identifiers: [NSManagedObjectID], afterItem afterIdentifier: NSManagedObjectID)
|
||||||
|
mutating func deleteItems(_ identifiers: [NSManagedObjectID])
|
||||||
|
mutating func deleteAllItems()
|
||||||
|
mutating func moveItem(_ identifier: NSManagedObjectID, beforeItem toIdentifier: NSManagedObjectID)
|
||||||
|
mutating func moveItem(_ identifier: NSManagedObjectID, afterItem toIdentifier: NSManagedObjectID)
|
||||||
|
mutating func reloadItems(_ identifiers: [NSManagedObjectID])
|
||||||
|
mutating func appendSections(_ identifiers: [String])
|
||||||
|
mutating func insertSections(_ identifiers: [String], beforeSection toIdentifier: String)
|
||||||
|
mutating func insertSections(_ identifiers: [String], afterSection toIdentifier: String)
|
||||||
|
mutating func deleteSections(_ identifiers: [String])
|
||||||
|
mutating func moveSection(_ identifier: String, beforeSection toIdentifier: String)
|
||||||
|
mutating func moveSection(_ identifier: String, afterSection toIdentifier: String)
|
||||||
|
mutating func reloadSections(_ identifiers: [String])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,24 +40,26 @@ import AppKit
|
|||||||
|
|
||||||
extension Internals {
|
extension Internals {
|
||||||
|
|
||||||
|
|
||||||
// MARK: - DiffableDataSourceSnapshot
|
// MARK: - DiffableDataSourceSnapshot
|
||||||
|
|
||||||
// Implementation based on https://github.com/ra1028/DiffableDataSources
|
// Implementation based on https://github.com/ra1028/DiffableDataSources
|
||||||
internal struct DiffableDataSourceSnapshot {
|
internal struct DiffableDataSourceSnapshot: DiffableDataSourceSnapshotProtocol {
|
||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
init() {
|
|
||||||
|
|
||||||
self.structure = .init()
|
|
||||||
}
|
|
||||||
|
|
||||||
init(sections: [NSFetchedResultsSectionInfo]) {
|
init(sections: [NSFetchedResultsSectionInfo]) {
|
||||||
|
|
||||||
self.structure = .init(sections: sections)
|
self.structure = .init(sections: sections)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: DiffableDataSourceSnapshotProtocol
|
||||||
|
|
||||||
|
init() {
|
||||||
|
|
||||||
|
self.structure = .init()
|
||||||
|
}
|
||||||
|
|
||||||
var numberOfItems: Int {
|
var numberOfItems: Int {
|
||||||
|
|
||||||
return self.structure.allItemIDs.count
|
return self.structure.allItemIDs.count
|
||||||
@@ -68,37 +70,37 @@ extension Internals {
|
|||||||
return self.structure.allSectionIDs.count
|
return self.structure.allSectionIDs.count
|
||||||
}
|
}
|
||||||
|
|
||||||
var allSectionIDs: [String] {
|
var sectionIdentifiers: [String] {
|
||||||
|
|
||||||
return self.structure.allSectionIDs
|
return self.structure.allSectionIDs
|
||||||
}
|
}
|
||||||
|
|
||||||
var allItemIDs: [NSManagedObjectID] {
|
var itemIdentifiers: [NSManagedObjectID] {
|
||||||
|
|
||||||
return self.structure.allItemIDs
|
return self.structure.allItemIDs
|
||||||
}
|
}
|
||||||
|
|
||||||
func numberOfItems(inSection identifier: String) -> Int {
|
func numberOfItems(inSection identifier: String) -> Int {
|
||||||
|
|
||||||
return self.itemIDs(inSection: identifier).count
|
return self.itemIdentifiers(inSection: identifier).count
|
||||||
}
|
}
|
||||||
|
|
||||||
func itemIDs(inSection identifier: String) -> [NSManagedObjectID] {
|
func itemIdentifiers(inSection identifier: String) -> [NSManagedObjectID] {
|
||||||
|
|
||||||
return self.structure.items(in: identifier)
|
return self.structure.items(in: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sectionIDs(containingItem identifier: NSManagedObjectID) -> String? {
|
func sectionIdentifier(containingItem identifier: NSManagedObjectID) -> String? {
|
||||||
|
|
||||||
return self.structure.section(containing: identifier)
|
return self.structure.section(containing: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
func indexOfItemID(_ identifier: NSManagedObjectID) -> Int? {
|
func indexOfItem(_ identifier: NSManagedObjectID) -> Int? {
|
||||||
|
|
||||||
return self.structure.allItemIDs.firstIndex(of: identifier)
|
return self.structure.allItemIDs.firstIndex(of: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
func indexOfSectionID(_ identifier: String) -> Int? {
|
func indexOfSection(_ identifier: String) -> Int? {
|
||||||
|
|
||||||
return self.structure.allSectionIDs.firstIndex(of: identifier)
|
return self.structure.allSectionIDs.firstIndex(of: identifier)
|
||||||
}
|
}
|
||||||
@@ -138,9 +140,9 @@ extension Internals {
|
|||||||
self.structure.move(itemID: identifier, after: toIdentifier)
|
self.structure.move(itemID: identifier, after: toIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func reloadItems<S: Sequence>(_ identifiers: S, nextStateTag: UUID) where S.Element == NSManagedObjectID {
|
mutating func reloadItems(_ identifiers: [NSManagedObjectID]) {
|
||||||
|
|
||||||
self.structure.update(itemIDs: identifiers, nextStateTag: nextStateTag)
|
self.structure.update(itemIDs: identifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func appendSections(_ identifiers: [String]) {
|
mutating func appendSections(_ identifiers: [String]) {
|
||||||
@@ -173,9 +175,9 @@ extension Internals {
|
|||||||
self.structure.move(sectionID: identifier, after: toIdentifier)
|
self.structure.move(sectionID: identifier, after: toIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func reloadSections<S: Sequence>(_ identifiers: S, nextStateTag: UUID) where S.Element == String {
|
mutating func reloadSections(_ identifiers: [String]) {
|
||||||
|
|
||||||
self.structure.update(sectionIDs: identifiers, nextStateTag: nextStateTag)
|
self.structure.update(sectionIDs: identifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -388,7 +390,7 @@ extension Internals {
|
|||||||
.insert(removed, at: itemIndex)
|
.insert(removed, at: itemIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func update<S: Sequence>(itemIDs: S, nextStateTag: UUID) where S.Element == NSManagedObjectID {
|
mutating func update<S: Sequence>(itemIDs: S) where S.Element == NSManagedObjectID {
|
||||||
|
|
||||||
let itemPositionMap = self.itemPositionMap()
|
let itemPositionMap = self.itemPositionMap()
|
||||||
for itemID in itemIDs {
|
for itemID in itemIDs {
|
||||||
@@ -464,7 +466,7 @@ extension Internals {
|
|||||||
self.sections.insert(removed, at: sectionIndex)
|
self.sections.insert(removed, at: sectionIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func update<S: Sequence>(sectionIDs: S, nextStateTag: UUID) where S.Element == String {
|
mutating func update<S: Sequence>(sectionIDs: S) where S.Element == String {
|
||||||
|
|
||||||
for sectionID in sectionIDs {
|
for sectionID in sectionIDs {
|
||||||
|
|
||||||
@@ -590,4 +592,76 @@ extension Internals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - NSDiffableDataSourceSnapshot: DiffableDataSourceSnapshotProtocol
|
||||||
|
|
||||||
|
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *)
|
||||||
|
extension NSDiffableDataSourceSnapshot: DiffableDataSourceSnapshotProtocol where SectionIdentifierType == NSString, ItemIdentifierType == NSManagedObjectID {
|
||||||
|
|
||||||
|
internal var sectionIdentifiers: [String] {
|
||||||
|
|
||||||
|
return self.sectionIdentifiers as [NSString] as [String]
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func numberOfItems(inSection identifier: String) -> Int {
|
||||||
|
|
||||||
|
return self.numberOfItems(inSection: identifier as NSString)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func itemIdentifiers(inSection identifier: String) -> [NSManagedObjectID] {
|
||||||
|
|
||||||
|
return self.itemIdentifiers(inSection: identifier as NSString)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func sectionIdentifier(containingItem identifier: NSManagedObjectID) -> String? {
|
||||||
|
|
||||||
|
return self.sectionIdentifier(containingItem: identifier) as NSString? as String?
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func indexOfSection(_ identifier: String) -> Int? {
|
||||||
|
|
||||||
|
return self.indexOfSection(identifier as NSString)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: String?) {
|
||||||
|
|
||||||
|
self.appendItems(identifiers, toSection: sectionIdentifier as NSString?)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal mutating func appendSections(_ identifiers: [String]) {
|
||||||
|
|
||||||
|
self.appendSections(identifiers as [NSString])
|
||||||
|
}
|
||||||
|
|
||||||
|
internal mutating func insertSections(_ identifiers: [String], beforeSection toIdentifier: String) {
|
||||||
|
|
||||||
|
self.insertSections(identifiers as [NSString], beforeSection: toIdentifier as NSString)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal mutating func insertSections(_ identifiers: [String], afterSection toIdentifier: String) {
|
||||||
|
|
||||||
|
return self.insertSections(identifiers as [NSString], afterSection: toIdentifier as NSString)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal mutating func deleteSections(_ identifiers: [String]) {
|
||||||
|
|
||||||
|
self.deleteSections(identifiers as [NSString])
|
||||||
|
}
|
||||||
|
|
||||||
|
internal mutating func moveSection(_ identifier: String, beforeSection toIdentifier: String) {
|
||||||
|
|
||||||
|
self.moveSection(identifier as NSString, beforeSection: toIdentifier as NSString)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal mutating func moveSection(_ identifier: String, afterSection toIdentifier: String) {
|
||||||
|
|
||||||
|
self.moveSection(identifier as NSString, afterSection: toIdentifier as NSString)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal mutating func reloadSections(_ identifiers: [String]) {
|
||||||
|
|
||||||
|
self.reloadSections(identifiers as [NSString])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import AppKit
|
|||||||
|
|
||||||
internal protocol FetchedDiffableDataSourceSnapshotHandler: AnyObject {
|
internal protocol FetchedDiffableDataSourceSnapshotHandler: AnyObject {
|
||||||
|
|
||||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangContentWith snapshot: Internals.DiffableDataSourceSnapshot)
|
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: DiffableDataSourceSnapshotProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -75,14 +75,14 @@ extension Internals {
|
|||||||
|
|
||||||
internal func initialFetch() {
|
internal func initialFetch() {
|
||||||
|
|
||||||
// #if canImport(UIKit) || canImport(AppKit)
|
#if canImport(UIKit) || canImport(AppKit)
|
||||||
//
|
|
||||||
// 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, *) {
|
||||||
//
|
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// #endif
|
#endif
|
||||||
|
|
||||||
guard let fetchedResultsController = self.fetchedResultsController else {
|
guard let fetchedResultsController = self.fetchedResultsController else {
|
||||||
|
|
||||||
@@ -94,26 +94,26 @@ extension Internals {
|
|||||||
|
|
||||||
// MARK: NSFetchedResultsControllerDelegate
|
// MARK: NSFetchedResultsControllerDelegate
|
||||||
|
|
||||||
// #if canImport(UIKit) || canImport(AppKit)
|
#if canImport(UIKit) || canImport(AppKit)
|
||||||
//
|
|
||||||
// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||||
// @objc
|
@objc
|
||||||
// dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
||||||
//
|
|
||||||
// self.handler?.controller(
|
self.handler?.controller(
|
||||||
// controller,
|
controller,
|
||||||
// didChangContentWith: snapshot as NSDiffableDataSourceSnapshot<NSString, NSManagedObjectID>
|
didChangeContentWith: snapshot as NSDiffableDataSourceSnapshot<NSString, NSManagedObjectID>
|
||||||
// )
|
)
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// #endif
|
#endif
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||||
|
|
||||||
self.handler?.controller(
|
self.handler?.controller(
|
||||||
controller,
|
controller,
|
||||||
didChangContentWith: Internals.DiffableDataSourceSnapshot(
|
didChangeContentWith: Internals.DiffableDataSourceSnapshot(
|
||||||
sections: controller.sections ?? []
|
sections: controller.sections ?? []
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,32 +25,83 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Internal
|
// MARK: - Internal
|
||||||
|
|
||||||
extension Internals {
|
extension Internals {
|
||||||
|
|
||||||
// MARK: - SharedNotificationObserver
|
// MARK: - SharedNotificationObserver
|
||||||
|
|
||||||
internal final class SharedNotificationObserver {
|
internal final class SharedNotificationObserver<T> {
|
||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
let observer: NSObjectProtocol
|
internal init(notificationName: Notification.Name, object: Any?, queue: OperationQueue? = nil, sharedValue: @escaping (_ note: Notification) -> T) {
|
||||||
|
|
||||||
init(notificationName: Notification.Name, object: Any?, queue: OperationQueue? = nil, closure: @escaping (_ note: Notification) -> Void) {
|
|
||||||
|
|
||||||
self.observer = NotificationCenter.default.addObserver(
|
self.observer = NotificationCenter.default.addObserver(
|
||||||
forName: notificationName,
|
forName: notificationName,
|
||||||
object: object,
|
object: object,
|
||||||
queue: queue,
|
queue: queue,
|
||||||
using: closure
|
using: { [weak self] (notification) in
|
||||||
|
|
||||||
|
guard let self = self else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let value = sharedValue(notification)
|
||||||
|
self.notifyObservers(value)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
|
||||||
NotificationCenter.default.removeObserver(self.observer)
|
self.observer.map(NotificationCenter.default.removeObserver(_:))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func addObserver<U: AnyObject>(_ observer: U, closure: @escaping (T) -> Void) {
|
||||||
|
|
||||||
|
self.observers.setObject(Closure(closure), forKey: observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private var observer: NSObjectProtocol!
|
||||||
|
private let observers: NSMapTable<AnyObject, Closure> = .weakToStrongObjects()
|
||||||
|
|
||||||
|
private func notifyObservers(_ sharedValue: T) {
|
||||||
|
|
||||||
|
guard let enumerator = self.observers.objectEnumerator() else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for closure in enumerator {
|
||||||
|
|
||||||
|
(closure as! Closure).invoke(with: sharedValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Closure
|
||||||
|
|
||||||
|
fileprivate final class Closure {
|
||||||
|
|
||||||
|
// MARK: FilePrivate
|
||||||
|
|
||||||
|
fileprivate init(_ closure: @escaping (T) -> Void) {
|
||||||
|
|
||||||
|
self.closure = closure
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func invoke(with argument: T) {
|
||||||
|
|
||||||
|
self.closure(argument)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let closure: (T) -> Void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
public subscript<S: Sequence>(indices indices: S) -> [LiveObject<O>] where S.Element == Index {
|
public subscript<S: Sequence>(indices indices: S) -> [LiveObject<O>] where S.Element == Index {
|
||||||
|
|
||||||
let context = self.context!
|
let context = self.context!
|
||||||
let itemIDs = self.diffableSnapshot.allItemIDs
|
let itemIDs = self.diffableSnapshot.itemIdentifiers
|
||||||
return indices.map { position in
|
return indices.map { position in
|
||||||
|
|
||||||
let itemID = itemIDs[position]
|
let itemID = itemIDs[position]
|
||||||
@@ -57,7 +57,7 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
public subscript(section sectionID: SectionID) -> [LiveObject<O>] {
|
public subscript(section sectionID: SectionID) -> [LiveObject<O>] {
|
||||||
|
|
||||||
let context = self.context!
|
let context = self.context!
|
||||||
let itemIDs = self.diffableSnapshot.itemIDs(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>(id: $0, context: context)
|
||||||
@@ -67,7 +67,7 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
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 {
|
||||||
|
|
||||||
let context = self.context!
|
let context = self.context!
|
||||||
let itemIDs = self.diffableSnapshot.itemIDs(inSection: sectionID)
|
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||||
return itemIndices.map { position in
|
return itemIndices.map { position in
|
||||||
|
|
||||||
let itemID = itemIDs[position]
|
let itemID = itemIDs[position]
|
||||||
@@ -85,14 +85,14 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
return self.diffableSnapshot.numberOfSections
|
return self.diffableSnapshot.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
public var sectionIDs: [SectionID] {
|
public var sectionIdentifiers: [SectionID] {
|
||||||
|
|
||||||
return self.diffableSnapshot.allSectionIDs
|
return self.diffableSnapshot.sectionIdentifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
public var itemIdentifiers: [ItemID] {
|
public var itemIdentifiers: [ItemID] {
|
||||||
|
|
||||||
return self.diffableSnapshot.allItemIDs
|
return self.diffableSnapshot.itemIdentifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
public func numberOfItems(inSection identifier: SectionID) -> Int {
|
public func numberOfItems(inSection identifier: SectionID) -> Int {
|
||||||
@@ -102,28 +102,28 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
|
|
||||||
public func itemIdentifiers(inSection identifier: SectionID) -> [ItemID] {
|
public func itemIdentifiers(inSection identifier: SectionID) -> [ItemID] {
|
||||||
|
|
||||||
return self.diffableSnapshot.itemIDs(inSection: identifier)
|
return self.diffableSnapshot.itemIdentifiers(inSection: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func itemIdentifiers(inSection identifier: SectionID, atIndices indices: IndexSet) -> [ItemID] {
|
public func itemIdentifiers(inSection identifier: SectionID, atIndices indices: IndexSet) -> [ItemID] {
|
||||||
|
|
||||||
let itemIDs = self.diffableSnapshot.itemIDs(inSection: identifier)
|
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: identifier)
|
||||||
return indices.map({ itemIDs[$0] })
|
return indices.map({ itemIDs[$0] })
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sectionIdentifier(containingItem identifier: ItemID) -> SectionID? {
|
public func sectionIdentifier(containingItem identifier: ItemID) -> SectionID? {
|
||||||
|
|
||||||
return self.diffableSnapshot.sectionIDs(containingItem: identifier)
|
return self.diffableSnapshot.sectionIdentifier(containingItem: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func indexOfItem(_ identifier: ItemID) -> Index? {
|
public func indexOfItem(_ identifier: ItemID) -> Index? {
|
||||||
|
|
||||||
return self.diffableSnapshot.indexOfItemID(identifier)
|
return self.diffableSnapshot.indexOfItem(identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func indexOfSection(_ identifier: SectionID) -> Int? {
|
public func indexOfSection(_ identifier: SectionID) -> Int? {
|
||||||
|
|
||||||
return self.diffableSnapshot.indexOfSectionID(identifier)
|
return self.diffableSnapshot.indexOfSection(identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
public mutating func appendItems(_ identifiers: [ItemID], toSection sectionIdentifier: SectionID? = nil) {
|
public mutating func appendItems(_ identifiers: [ItemID], toSection sectionIdentifier: SectionID? = nil) {
|
||||||
@@ -163,7 +163,7 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
|
|
||||||
public mutating func reloadItems(_ identifiers: [ItemID]) {
|
public mutating func reloadItems(_ identifiers: [ItemID]) {
|
||||||
|
|
||||||
self.diffableSnapshot.reloadItems(identifiers, nextStateTag: .init())
|
self.diffableSnapshot.reloadItems(identifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
public mutating func appendSections(_ identifiers: [SectionID]) {
|
public mutating func appendSections(_ identifiers: [SectionID]) {
|
||||||
@@ -198,7 +198,7 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
|
|
||||||
public mutating func reloadSections(_ identifiers: [SectionID]) {
|
public mutating func reloadSections(_ identifiers: [SectionID]) {
|
||||||
|
|
||||||
self.diffableSnapshot.reloadSections(identifiers, nextStateTag: .init())
|
self.diffableSnapshot.reloadSections(identifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -211,18 +211,18 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
|
|
||||||
public var startIndex: Index {
|
public var startIndex: Index {
|
||||||
|
|
||||||
return self.diffableSnapshot.allItemIDs.startIndex
|
return self.diffableSnapshot.itemIdentifiers.startIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
public var endIndex: Index {
|
public var endIndex: Index {
|
||||||
|
|
||||||
return self.diffableSnapshot.allItemIDs.endIndex
|
return self.diffableSnapshot.itemIdentifiers.endIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscript(position: Index) -> Element {
|
public subscript(position: Index) -> Element {
|
||||||
|
|
||||||
let context = self.context!
|
let context = self.context!
|
||||||
let itemID = self.diffableSnapshot.allItemIDs[position]
|
let itemID = self.diffableSnapshot.itemIdentifiers[position]
|
||||||
return LiveObject<O>(id: itemID, context: context)
|
return LiveObject<O>(id: itemID, context: context)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,11 +254,11 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
|
|
||||||
internal init() {
|
internal init() {
|
||||||
|
|
||||||
self.diffableSnapshot = .init()
|
self.diffableSnapshot = Internals.DiffableDataSourceSnapshot()
|
||||||
self.context = nil
|
self.context = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
internal init(diffableSnapshot: Internals.DiffableDataSourceSnapshot, context: NSManagedObjectContext) {
|
internal init(diffableSnapshot: DiffableDataSourceSnapshotProtocol, context: NSManagedObjectContext) {
|
||||||
|
|
||||||
self.diffableSnapshot = diffableSnapshot
|
self.diffableSnapshot = diffableSnapshot
|
||||||
self.context = context
|
self.context = context
|
||||||
@@ -270,5 +270,5 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
private let id: UUID = .init()
|
private let id: UUID = .init()
|
||||||
private let context: NSManagedObjectContext?
|
private let context: NSManagedObjectContext?
|
||||||
|
|
||||||
private var diffableSnapshot: Internals.DiffableDataSourceSnapshot
|
private var diffableSnapshot: DiffableDataSourceSnapshotProtocol
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,9 +63,9 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
return self.snapshot.numberOfSections
|
return self.snapshot.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
public var sections: [SectionID] {
|
public var sectionIdentifiers: [SectionID] {
|
||||||
|
|
||||||
return self.snapshot.sectionIDs
|
return self.snapshot.sectionIdentifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscript(section sectionID: SectionID) -> [LiveObject<O>] {
|
public subscript(section sectionID: SectionID) -> [LiveObject<O>] {
|
||||||
@@ -302,7 +302,7 @@ extension LiveList: FetchedDiffableDataSourceSnapshotHandler {
|
|||||||
|
|
||||||
// MARK: FetchedDiffableDataSourceSnapshotHandler
|
// MARK: FetchedDiffableDataSourceSnapshotHandler
|
||||||
|
|
||||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangContentWith snapshot: Internals.DiffableDataSourceSnapshot) {
|
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: DiffableDataSourceSnapshotProtocol) {
|
||||||
|
|
||||||
self.snapshot = .init(
|
self.snapshot = .init(
|
||||||
diffableSnapshot: snapshot,
|
diffableSnapshot: snapshot,
|
||||||
|
|||||||
@@ -114,36 +114,23 @@ public final class LiveObject<O: DynamicObject>: Identifiable, Hashable {
|
|||||||
|
|
||||||
self.rawObjectWillChange = nil
|
self.rawObjectWillChange = nil
|
||||||
}
|
}
|
||||||
self.observer = NotificationCenter.default.addObserver(
|
|
||||||
forName: .NSManagedObjectContextObjectsDidChange,
|
|
||||||
object: context,
|
|
||||||
queue: .main,
|
|
||||||
using: { [weak self] (notification) in
|
|
||||||
|
|
||||||
guard let self = self, let userInfo = notification.userInfo else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let updatedObjects = (userInfo[NSUpdatedObjectsKey] as! NSSet? ?? [])
|
|
||||||
let mergedObjects = (userInfo[NSRefreshedObjectsKey] as! NSSet? ?? [])
|
|
||||||
guard mergedObjects.contains(where: { ($0 as! NSManagedObject).objectID == id })
|
|
||||||
|| updatedObjects.contains(where: { ($0 as! NSManagedObject).objectID == id }) else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.$lazySnapshot.reset({ initializer(id, context) })
|
|
||||||
self.willChange()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.$lazySnapshot.initialize({ initializer(id, context) })
|
self.$lazySnapshot.initialize({ initializer(id, context) })
|
||||||
|
|
||||||
|
context.objectsDidChangeObserver(for: self).addObserver(self) { [weak self] (objectIDs) in
|
||||||
|
|
||||||
|
guard let self = self else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.$lazySnapshot.reset({ initializer(id, context) })
|
||||||
|
self.willChange()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let context: NSManagedObjectContext
|
private let context: NSManagedObjectContext
|
||||||
private var observer: NSObjectProtocol?
|
|
||||||
|
|
||||||
@Internals.LazyNonmutating(uninitialized: ())
|
@Internals.LazyNonmutating(uninitialized: ())
|
||||||
private var lazySnapshot: ObjectSnapshot<O>
|
private var lazySnapshot: ObjectSnapshot<O>
|
||||||
|
|||||||
@@ -89,7 +89,10 @@ extension NSManagedObjectContext {
|
|||||||
@nonobjc
|
@nonobjc
|
||||||
internal func liveObject<D: DynamicObject>(id: NSManagedObjectID) -> LiveObject<D> {
|
internal func liveObject<D: DynamicObject>(id: NSManagedObjectID) -> LiveObject<D> {
|
||||||
|
|
||||||
let cache = self.liveObjectsCache(D.self)
|
let cache: NSMapTable<NSManagedObjectID, LiveObject<D>> = self.userInfo(for: .liveObjectsCache(D.self)) {
|
||||||
|
|
||||||
|
return .strongToWeakObjects()
|
||||||
|
}
|
||||||
return Internals.with {
|
return Internals.with {
|
||||||
|
|
||||||
if let liveObject = cache.object(forKey: id) {
|
if let liveObject = cache.object(forKey: id) {
|
||||||
@@ -103,27 +106,38 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
private func liveObjectsCache<D: DynamicObject>(_ objectType: D.Type) -> NSMapTable<NSManagedObjectID, LiveObject<D>> {
|
internal func objectsDidChangeObserver<U: AnyObject>(for observer: U) -> Internals.SharedNotificationObserver<Set<NSManagedObjectID>> {
|
||||||
|
|
||||||
let key = Internals.typeName(objectType)
|
return self.userInfo(for: .objectsChangeObserver(U.self)) { [unowned self] in
|
||||||
if let cache = self.userInfo[key] {
|
|
||||||
|
|
||||||
return cache as! NSMapTable<NSManagedObjectID, LiveObject<D>>
|
return .init(
|
||||||
|
notificationName: .NSManagedObjectContextObjectsDidChange,
|
||||||
|
object: self,
|
||||||
|
queue: .main,
|
||||||
|
sharedValue: { (notification) -> Set<NSManagedObjectID> in
|
||||||
|
|
||||||
|
guard let userInfo = notification.userInfo else {
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
var updatedObjectIDs: Set<NSManagedObjectID> = []
|
||||||
|
if let updatedObjects = userInfo[NSUpdatedObjectsKey] as? Set<NSManagedObjectID> {
|
||||||
|
|
||||||
|
updatedObjectIDs.formUnion(updatedObjects)
|
||||||
|
}
|
||||||
|
if let mergedObjects = userInfo[NSRefreshedObjectsKey] as? Set<NSManagedObjectID> {
|
||||||
|
|
||||||
|
updatedObjectIDs.formUnion(mergedObjects)
|
||||||
|
}
|
||||||
|
return updatedObjectIDs
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
let cache = NSMapTable<NSManagedObjectID, LiveObject<D>>.strongToWeakObjects()
|
|
||||||
self.userInfo[key] = cache
|
|
||||||
return cache
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private struct PropertyKeys {
|
|
||||||
|
|
||||||
static var observerForWillSaveNotification: Void?
|
|
||||||
static var shouldCascadeSavesToParent: Void?
|
|
||||||
}
|
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
private var observerForWillSaveNotification: Internals.NotificationObserver? {
|
private var observerForWillSaveNotification: Internals.NotificationObserver? {
|
||||||
|
|
||||||
@@ -143,5 +157,47 @@ extension NSManagedObjectContext {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func userInfo<T>(for key: UserInfoKeys, initialize: @escaping () -> T) -> T {
|
||||||
|
|
||||||
|
let keyString = key.keyString
|
||||||
|
if let value = self.userInfo[keyString] {
|
||||||
|
|
||||||
|
return value as! T
|
||||||
|
}
|
||||||
|
let value = initialize()
|
||||||
|
self.userInfo[keyString] = value
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - PropertyKeys
|
||||||
|
|
||||||
|
private struct PropertyKeys {
|
||||||
|
|
||||||
|
static var observerForWillSaveNotification: Void?
|
||||||
|
static var shouldCascadeSavesToParent: Void?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - UserInfoKeys
|
||||||
|
|
||||||
|
private enum UserInfoKeys {
|
||||||
|
|
||||||
|
case liveObjectsCache(DynamicObject.Type)
|
||||||
|
case objectsChangeObserver(AnyObject.Type)
|
||||||
|
|
||||||
|
var keyString: String {
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
|
||||||
|
case .liveObjectsCache(let objectType):
|
||||||
|
return "CoreStore.liveObjectsCache(\(Internals.typeName(objectType)))"
|
||||||
|
|
||||||
|
case .objectsChangeObserver:
|
||||||
|
return "CoreStore.objectsChangeObserver"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user