@@ -81,10 +81,32 @@ class Notification: NSManagedObject {
8181 ///
8282 fileprivate var cachedHeaderAndBodyContentGroups : [ FormattableContentGroup ] ?
8383
84+ private var cachedAttributesObserver : NotificationCachedAttributesObserver ?
85+
8486 /// Array that contains the Cached Property Names
8587 ///
8688 fileprivate static let cachedAttributes = Set ( arrayLiteral: " body " , " header " , " subject " , " timestamp " )
8789
90+ override func awakeFromFetch( ) {
91+ super. awakeFromFetch ( )
92+
93+ if cachedAttributesObserver == nil {
94+ let observer = NotificationCachedAttributesObserver ( )
95+ for attr in Notification . cachedAttributes {
96+ addObserver ( observer, forKeyPath: attr, options: [ . prior] , context: nil )
97+ }
98+ cachedAttributesObserver = observer
99+ }
100+ }
101+
102+ deinit {
103+ if let observer = cachedAttributesObserver {
104+ for attr in Notification . cachedAttributes {
105+ removeObserver ( observer, forKeyPath: attr)
106+ }
107+ }
108+ }
109+
88110 func renderSubject( ) -> NSAttributedString ? {
89111 guard let subjectContent = subjectContentGroup? . blocks. first else {
90112 return nil
@@ -99,26 +121,6 @@ class Notification: NSManagedObject {
99121 return formatter. render ( content: snippetContent, with: SnippetsContentStyles ( ) )
100122 }
101123
102- /// When needed, nukes cached attributes
103- ///
104- override func willChangeValue( forKey key: String ) {
105- super. willChangeValue ( forKey: key)
106-
107- // Note:
108- // Cached Attributes are only consumed on the main thread, when initializing UI elements.
109- // As an optimization, we'll only reset those attributes when we're running on the main thread.
110- //
111- guard managedObjectContext? . concurrencyType == . mainQueueConcurrencyType else {
112- return
113- }
114-
115- guard Swift . type ( of: self ) . cachedAttributes. contains ( key) else {
116- return
117- }
118-
119- resetCachedAttributes ( )
120- }
121-
122124 /// Nukes any cached values.
123125 ///
124126 func resetCachedAttributes( ) {
@@ -412,3 +414,25 @@ extension Notification: Notifiable {
412414 return notificationId
413415 }
414416}
417+
418+ private class NotificationCachedAttributesObserver : NSObject {
419+ override func observeValue( forKeyPath keyPath: String ? , of object: Any ? , change: [ NSKeyValueChangeKey : Any ] ? , context: UnsafeMutableRawPointer ? ) {
420+ guard let keyPath, let notification = object as? Notification , Notification . cachedAttributes. contains ( keyPath) else {
421+ return
422+ }
423+
424+ guard ( change ? [ . notificationIsPriorKey] as? NSNumber ) ? . boolValue == true else {
425+ return
426+ }
427+
428+ // Note:
429+ // Cached Attributes are only consumed on the main thread, when initializing UI elements.
430+ // As an optimization, we'll only reset those attributes when we're running on the main thread.
431+ //
432+ guard notification. managedObjectContext? . concurrencyType == . mainQueueConcurrencyType else {
433+ return
434+ }
435+
436+ notification. resetCachedAttributes ( )
437+ }
438+ }
0 commit comments