@@ -9,9 +9,9 @@ import CoreData
99public typealias ResultsControllerMutableType = NSManagedObject & ReadOnlyConvertible
1010
1111
12- // MARK: - ResultsController
12+ // MARK: - GenericResultsController (Core Implementation)
1313//
14- public class ResultsController < T: ResultsControllerMutableType > {
14+ public class GenericResultsController < T: ResultsControllerMutableType , Output > {
1515
1616 /// The `StorageType` used to fetch objects.
1717 ///
@@ -79,7 +79,7 @@ public class ResultsController<T: ResultsControllerMutableType> {
7979
8080 /// Closure to be executed whenever an Object is updated.
8181 ///
82- public var onDidChangeObject : ( ( _ object: T . ReadOnlyType , _ indexPath: IndexPath ? , _ type: ChangeType , _ newIndexPath: IndexPath ? ) -> Void ) ?
82+ public var onDidChangeObject : ( ( _ object: Output , _ indexPath: IndexPath ? , _ type: ChangeType , _ newIndexPath: IndexPath ? ) -> Void ) ?
8383
8484 /// Closure to be executed whenever an entire Section is updated.
8585 ///
@@ -94,19 +94,25 @@ public class ResultsController<T: ResultsControllerMutableType> {
9494 ///
9595 private let fetchLimit : Int ?
9696
97+ /// Transformer closure to convert T to Output type.
98+ ///
99+ private let transformer : ( T ) -> Output
100+
97101 /// Designated Initializer.
98102 ///
99103 public init ( viewStorage: StorageType ,
100104 sectionNameKeyPath: String ? = nil ,
101105 matching predicate: NSPredicate ? = nil ,
102106 fetchLimit: Int ? = nil ,
103- sortedBy descriptors: [ NSSortDescriptor ] ) {
107+ sortedBy descriptors: [ NSSortDescriptor ] ,
108+ transformer: @escaping ( T ) -> Output ) {
104109
105110 self . viewStorage = viewStorage
106111 self . sectionNameKeyPath = sectionNameKeyPath
107112 self . predicate = predicate
108113 self . fetchLimit = fetchLimit
109114 self . sortDescriptors = descriptors
115+ self . transformer = transformer
110116
111117 setupResultsController ( )
112118 setupEventsForwarding ( )
@@ -119,13 +125,15 @@ public class ResultsController<T: ResultsControllerMutableType> {
119125 sectionNameKeyPath: String ? = nil ,
120126 matching predicate: NSPredicate ? = nil ,
121127 fetchLimit: Int ? = nil ,
122- sortedBy descriptors: [ NSSortDescriptor ] ) {
128+ sortedBy descriptors: [ NSSortDescriptor ] ,
129+ transformer: @escaping ( T ) -> Output ) {
123130
124131 self . init ( viewStorage: storageManager. viewStorage,
125132 sectionNameKeyPath: sectionNameKeyPath,
126133 matching: predicate,
127134 fetchLimit: fetchLimit,
128- sortedBy: descriptors)
135+ sortedBy: descriptors,
136+ transformer: transformer)
129137 }
130138
131139
@@ -139,14 +147,14 @@ public class ResultsController<T: ResultsControllerMutableType> {
139147 ///
140148 /// Prefer to use `safeObject(at:)` instead.
141149 ///
142- public func object( at indexPath: IndexPath ) -> T . ReadOnlyType {
143- return controller. object ( at: indexPath) . toReadOnly ( )
150+ public func object( at indexPath: IndexPath ) -> Output {
151+ return transformer ( controller. object ( at: indexPath) )
144152 }
145153
146154 /// Returns the fetched object at the given `indexPath`. Returns `nil` if the `indexPath`
147155 /// does not exist.
148156 ///
149- public func safeObject( at indexPath: IndexPath ) -> T . ReadOnlyType ? {
157+ public func safeObject( at indexPath: IndexPath ) -> Output ? {
150158 guard !isEmpty else {
151159 return nil
152160 }
@@ -160,7 +168,7 @@ public class ResultsController<T: ResultsControllerMutableType> {
160168 return nil
161169 }
162170
163- return controller. object ( at: indexPath) . toReadOnly ( )
171+ return transformer ( controller. object ( at: indexPath) )
164172 }
165173
166174 /// Returns the Plain ObjectIndex corresponding to a given IndexPath. You can use this index to map the
@@ -194,23 +202,24 @@ public class ResultsController<T: ResultsControllerMutableType> {
194202 }
195203
196204 /// Returns an array of all of the (ReadOnly) Fetched Objects.
205+ /// Note: Avoid calling this in computed variables as the conversion of storage items can be costly.
197206 ///
198- public var fetchedObjects : [ T . ReadOnlyType ] {
199- let readOnlyObjects = controller. fetchedObjects? . compactMap { mutableObject in
200- mutableObject . toReadOnly ( )
207+ public var fetchedObjects : [ Output ] {
208+ let transformedObjects = controller. fetchedObjects? . compactMap { mutableObject in
209+ transformer ( mutableObject )
201210 }
202211
203- return readOnlyObjects ?? [ ]
212+ return transformedObjects ?? [ ]
204213 }
205214
206215 /// Returns an array of SectionInfo Entitites.
207216 ///
208217 public var sections : [ SectionInfo ] {
209- let readOnlySections = controller. sections? . compactMap { mutableSection in
210- SectionInfo ( mutableSection: mutableSection)
218+ let transformedSections = controller. sections? . compactMap { mutableSection in
219+ SectionInfo ( mutableSection: mutableSection, transformer : transformer )
211220 }
212221
213- return readOnlySections ?? [ ]
222+ return transformedSections ?? [ ]
214223 }
215224
216225 /// Returns an optional index path of the first matching object.
@@ -260,17 +269,17 @@ public class ResultsController<T: ResultsControllerMutableType> {
260269 return
261270 }
262271
263- let readOnlyObject = object . toReadOnly ( )
264- self . onDidChangeObject ? ( readOnlyObject , indexPath, type, newIndexPath)
272+ let transformedObject = transformer ( object )
273+ self . onDidChangeObject ? ( transformedObject , indexPath, type, newIndexPath)
265274 }
266275
267276 internalDelegate. onDidChangeSection = { [ weak self] ( mutableSection, sectionIndex, type) in
268277 guard let `self` = self else {
269278 return
270279 }
271280
272- let readOnlySection = SectionInfo ( mutableSection: mutableSection)
273- self . onDidChangeSection ? ( readOnlySection , sectionIndex, type)
281+ let transformedSection = SectionInfo ( mutableSection: mutableSection, transformer : transformer )
282+ self . onDidChangeSection ? ( transformedSection , sectionIndex, type)
274283 }
275284 }
276285
@@ -294,7 +303,7 @@ public class ResultsController<T: ResultsControllerMutableType> {
294303
295304// MARK: - Nested Types
296305//
297- public extension ResultsController {
306+ public extension GenericResultsController {
298307
299308 // MARK: - ResultsController.ChangeType
300309 //
@@ -322,9 +331,13 @@ public extension ResultsController {
322331 mutableSectionInfo. numberOfObjects
323332 }
324333
325- /// Returns the array of (ReadOnly) objects in the section.
334+ /// Transformer closure to convert objects in the section.
335+ ///
336+ private let transformer : ( T ) -> Output
337+
338+ /// Returns the array of transformed objects in the section.
326339 ///
327- private( set) public lazy var objects : [ T . ReadOnlyType ] = {
340+ private( set) public lazy var objects : [ Output ] = {
328341 guard let objects = mutableSectionInfo. objects else {
329342 return [ ]
330343 }
@@ -333,13 +346,47 @@ public extension ResultsController {
333346 return [ ]
334347 }
335348
336- return castedObjects. map { $0 . toReadOnly ( ) }
349+ return castedObjects. map { transformer ( $0 ) }
337350 } ( )
338351
339352 /// Designated Initializer
340353 ///
341- init ( mutableSection: NSFetchedResultsSectionInfo ) {
354+ init ( mutableSection: NSFetchedResultsSectionInfo , transformer : @escaping ( T ) -> Output ) {
342355 mutableSectionInfo = mutableSection
356+ self . transformer = transformer
343357 }
344358 }
345359}
360+
361+ // MARK: - ResultsController (Backward Compatible Specialization)
362+ //
363+ public class ResultsController < T: ResultsControllerMutableType > : GenericResultsController < T , T . ReadOnlyType > {
364+ /// Designated Initializer.
365+ ///
366+ public init ( viewStorage: StorageType ,
367+ sectionNameKeyPath: String ? = nil ,
368+ matching predicate: NSPredicate ? = nil ,
369+ fetchLimit: Int ? = nil ,
370+ sortedBy descriptors: [ NSSortDescriptor ] ) {
371+ super. init ( viewStorage: viewStorage,
372+ sectionNameKeyPath: sectionNameKeyPath,
373+ matching: predicate,
374+ fetchLimit: fetchLimit,
375+ sortedBy: descriptors,
376+ transformer: { $0. toReadOnly ( ) } )
377+ }
378+
379+ /// Convenience Initializer.
380+ ///
381+ public convenience init ( storageManager: StorageManagerType ,
382+ sectionNameKeyPath: String ? = nil ,
383+ matching predicate: NSPredicate ? = nil ,
384+ fetchLimit: Int ? = nil ,
385+ sortedBy descriptors: [ NSSortDescriptor ] ) {
386+ self . init ( viewStorage: storageManager. viewStorage,
387+ sectionNameKeyPath: sectionNameKeyPath,
388+ matching: predicate,
389+ fetchLimit: fetchLimit,
390+ sortedBy: descriptors)
391+ }
392+ }
0 commit comments