@@ -2,14 +2,16 @@ import Foundation
22import Storage
33import CoreData
44
5+
6+
57// MARK: - MutableType: Storage.framework Type that will be retrieved (and converted into ReadOnly)
68//
79public typealias ResultsControllerMutableType = NSManagedObject & ReadOnlyConvertible
810
911
10- // MARK: - ResultsController
12+ // MARK: - GenericResultsController (Core Implementation)
1113//
12- public class ResultsController < T: ResultsControllerMutableType > {
14+ public class GenericResultsController < T: ResultsControllerMutableType , Output > {
1315
1416 /// The `StorageType` used to fetch objects.
1517 ///
@@ -77,7 +79,7 @@ public class ResultsController<T: ResultsControllerMutableType> {
7779
7880 /// Closure to be executed whenever an Object is updated.
7981 ///
80- 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 ) ?
8183
8284 /// Closure to be executed whenever an entire Section is updated.
8385 ///
@@ -92,19 +94,25 @@ public class ResultsController<T: ResultsControllerMutableType> {
9294 ///
9395 private let fetchLimit : Int ?
9496
97+ /// Transformer closure to convert T to Output type.
98+ ///
99+ private let transformer : ( T ) -> Output
100+
95101 /// Designated Initializer.
96102 ///
97103 public init ( viewStorage: StorageType ,
98104 sectionNameKeyPath: String ? = nil ,
99105 matching predicate: NSPredicate ? = nil ,
100106 fetchLimit: Int ? = nil ,
101- sortedBy descriptors: [ NSSortDescriptor ] ) {
107+ sortedBy descriptors: [ NSSortDescriptor ] ,
108+ transformer: @escaping ( T ) -> Output ) {
102109
103110 self . viewStorage = viewStorage
104111 self . sectionNameKeyPath = sectionNameKeyPath
105112 self . predicate = predicate
106113 self . fetchLimit = fetchLimit
107114 self . sortDescriptors = descriptors
115+ self . transformer = transformer
108116
109117 setupResultsController ( )
110118 setupEventsForwarding ( )
@@ -117,13 +125,15 @@ public class ResultsController<T: ResultsControllerMutableType> {
117125 sectionNameKeyPath: String ? = nil ,
118126 matching predicate: NSPredicate ? = nil ,
119127 fetchLimit: Int ? = nil ,
120- sortedBy descriptors: [ NSSortDescriptor ] ) {
128+ sortedBy descriptors: [ NSSortDescriptor ] ,
129+ transformer: @escaping ( T ) -> Output ) {
121130
122131 self . init ( viewStorage: storageManager. viewStorage,
123132 sectionNameKeyPath: sectionNameKeyPath,
124133 matching: predicate,
125134 fetchLimit: fetchLimit,
126- sortedBy: descriptors)
135+ sortedBy: descriptors,
136+ transformer: transformer)
127137 }
128138
129139
@@ -137,14 +147,14 @@ public class ResultsController<T: ResultsControllerMutableType> {
137147 ///
138148 /// Prefer to use `safeObject(at:)` instead.
139149 ///
140- public func object( at indexPath: IndexPath ) -> T . ReadOnlyType {
141- return controller. object ( at: indexPath) . toReadOnly ( )
150+ public func object( at indexPath: IndexPath ) -> Output {
151+ return transformer ( controller. object ( at: indexPath) )
142152 }
143153
144154 /// Returns the fetched object at the given `indexPath`. Returns `nil` if the `indexPath`
145155 /// does not exist.
146156 ///
147- public func safeObject( at indexPath: IndexPath ) -> T . ReadOnlyType ? {
157+ public func safeObject( at indexPath: IndexPath ) -> Output ? {
148158 guard !isEmpty else {
149159 return nil
150160 }
@@ -158,7 +168,7 @@ public class ResultsController<T: ResultsControllerMutableType> {
158168 return nil
159169 }
160170
161- return controller. object ( at: indexPath) . toReadOnly ( )
171+ return transformer ( controller. object ( at: indexPath) )
162172 }
163173
164174 /// Returns the Plain ObjectIndex corresponding to a given IndexPath. You can use this index to map the
@@ -179,21 +189,6 @@ public class ResultsController<T: ResultsControllerMutableType> {
179189 return output
180190 }
181191
182- /// Returns an array of all of the transformed fetched objects.
183- /// Note: Avoid calling this in computed variables as the conversion of storage items can be costly.
184- ///
185- public func transformedObjects< U> ( using transformation: ( T ) -> U ) -> [ U ] {
186- controller. fetchedObjects? . compactMap { mutableObject in
187- transformation ( mutableObject)
188- } ?? [ ]
189- }
190-
191- /// Returns a transformed object at a given index
192- ///
193- public func transformedObject< U> ( at indexPath: IndexPath , using transformation: ( T ) -> U ) -> U {
194- transformation ( controller. object ( at: indexPath) )
195- }
196-
197192 /// Indicates if there are any Objects matching the specified criteria.
198193 ///
199194 public var isEmpty : Bool {
@@ -209,22 +204,22 @@ public class ResultsController<T: ResultsControllerMutableType> {
209204 /// Returns an array of all of the (ReadOnly) Fetched Objects.
210205 /// Note: Avoid calling this in computed variables as the conversion of storage items can be costly.
211206 ///
212- public var fetchedObjects : [ T . ReadOnlyType ] {
213- let readOnlyObjects = controller. fetchedObjects? . compactMap { mutableObject in
214- mutableObject . toReadOnly ( )
207+ public var fetchedObjects : [ Output ] {
208+ let transformedObjects = controller. fetchedObjects? . compactMap { mutableObject in
209+ transformer ( mutableObject )
215210 }
216211
217- return readOnlyObjects ?? [ ]
212+ return transformedObjects ?? [ ]
218213 }
219214
220215 /// Returns an array of SectionInfo Entitites.
221216 ///
222217 public var sections : [ SectionInfo ] {
223- let readOnlySections = controller. sections? . compactMap { mutableSection in
224- SectionInfo ( mutableSection: mutableSection)
218+ let transformedSections = controller. sections? . compactMap { mutableSection in
219+ SectionInfo ( mutableSection: mutableSection, transformer : transformer )
225220 }
226221
227- return readOnlySections ?? [ ]
222+ return transformedSections ?? [ ]
228223 }
229224
230225 /// Returns an optional index path of the first matching object.
@@ -274,17 +269,17 @@ public class ResultsController<T: ResultsControllerMutableType> {
274269 return
275270 }
276271
277- let readOnlyObject = object . toReadOnly ( )
278- self . onDidChangeObject ? ( readOnlyObject , indexPath, type, newIndexPath)
272+ let transformedObject = transformer ( object )
273+ self . onDidChangeObject ? ( transformedObject , indexPath, type, newIndexPath)
279274 }
280275
281276 internalDelegate. onDidChangeSection = { [ weak self] ( mutableSection, sectionIndex, type) in
282277 guard let `self` = self else {
283278 return
284279 }
285280
286- let readOnlySection = SectionInfo ( mutableSection: mutableSection)
287- self . onDidChangeSection ? ( readOnlySection , sectionIndex, type)
281+ let transformedSection = SectionInfo ( mutableSection: mutableSection, transformer : transformer )
282+ self . onDidChangeSection ? ( transformedSection , sectionIndex, type)
288283 }
289284 }
290285
@@ -308,7 +303,7 @@ public class ResultsController<T: ResultsControllerMutableType> {
308303
309304// MARK: - Nested Types
310305//
311- public extension ResultsController {
306+ public extension GenericResultsController {
312307
313308 // MARK: - ResultsController.ChangeType
314309 //
@@ -336,9 +331,13 @@ public extension ResultsController {
336331 mutableSectionInfo. numberOfObjects
337332 }
338333
339- /// 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.
340339 ///
341- private( set) public lazy var objects : [ T . ReadOnlyType ] = {
340+ private( set) public lazy var objects : [ Output ] = {
342341 guard let objects = mutableSectionInfo. objects else {
343342 return [ ]
344343 }
@@ -347,13 +346,47 @@ public extension ResultsController {
347346 return [ ]
348347 }
349348
350- return castedObjects. map { $0 . toReadOnly ( ) }
349+ return castedObjects. map { transformer ( $0 ) }
351350 } ( )
352351
353352 /// Designated Initializer
354353 ///
355- init ( mutableSection: NSFetchedResultsSectionInfo ) {
354+ init ( mutableSection: NSFetchedResultsSectionInfo , transformer : @escaping ( T ) -> Output ) {
356355 mutableSectionInfo = mutableSection
356+ self . transformer = transformer
357357 }
358358 }
359359}
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