improved caching in utility methods

This commit is contained in:
John Estropia
2019-10-12 10:02:00 +09:00
parent 5af0d17de4
commit 12c58e3955
10 changed files with 307 additions and 112 deletions

View File

@@ -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>

View File

@@ -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

View File

@@ -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])
} }

View File

@@ -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

View File

@@ -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 ?? []
) )
) )

View File

@@ -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
} }
} }
} }

View File

@@ -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
} }

View File

@@ -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,

View File

@@ -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>

View File

@@ -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"
}
}
}
} }