DataSource object for UITableView / UICollectionView in Swift with Core Data

It’s amazing how quickly I now have become a part of the Swift Fan Club.  I recently worked on some old Objective-C code of mine and was amazed how quickly one learns to stop typing semi-colons.  🙂

Today’s post is all about a pattern I use more often in my projects, and it’s one that prefers composition over inheritance.  All that really means in this case is that on any UITableViewController (or similarly, UICollectionViewController), I prefer to create separate Data Source objects that keep all that code separate from the View Controller itself.  (I personally don’t find that MVC stands for Massive View Controller if you don’t let it.)

The issue here is that I pretty much don’t do projects any more without using Core Data.  It is the best solution in my opinion because of the code you oftentimes *don’t* have to write.  Also, with NSFetchedResultsController, I like how you can further separate your data layer (think Networking and importing) from you View Controllers.  View Controllers concern themselves with *what* they want to display, and not with how it is acquired.

Anyway, the strictly typed language of Swift sometimes makes old approaches not straightforward, and I would like to share what I determined today.  It will become a staple in my future Swift projects.

I create a DataSource class that takes a generic type, so that this generic type can be used for Core Data related activities.  By default, a NSFetchedResultsController also takes a generic type of NSFetchRequestResult. But sometimes that is simply not enough. More on this later. To even make a Generic Data source, we have:

class BasicFetchedResultsDataSource: NSObject, NSFetchedResultsControllerDelegate where T:NSManagedObject {
    
    let managedObjectContext: NSManagedObjectContext!
    let tableView: UITableView!
    
    init(context: NSManagedObjectContext!, tableView: UITableView!) {
        
        self.managedObjectContext = context
        self.tableView = tableView
    }
    
    private var _fetchedResultsController: NSFetchedResultsController? = nil
    var fetchedResultsController: NSFetchedResultsController {
        
        if _fetchedResultsController != nil {
            return _fetchedResultsController!
        }
        
        let request = T.fetchRequest()
        request.predicate = self.searchPredicateForFetchRequest
        request.sortDescriptors = self.sortDescriptorsForFetchRequest
        let controller = NSFetchedResultsController(fetchRequest: request as! NSFetchRequest,
                                                    managedObjectContext: self.managedObjectContext,
                                                    sectionNameKeyPath: self.sectionNameKeyPath,
                                                    cacheName: self.resultsControllerCacheName)
        
        controller.delegate = self
        _fetchedResultsController = controller
        return _fetchedResultsController!
    }
    
    
    func updateRequestAndFetch() throws {
        
        self.fetchedResultsController.fetchRequest.predicate = self.searchPredicateForFetchRequest
        self.fetchedResultsController.fetchRequest.sortDescriptors = self.sortDescriptorsForFetchRequest
        
        do {
            try self.fetchedResultsController.performFetch()
            
            self.tableView.reloadData()
        }
        catch {
            throw error
        }
    }
    
    // allows your subclass to override and change this
    var sectionNameKeyPath: String? {
        return nil
    }
    
    // allows your subclass to override and change this
    var resultsControllerCacheName: String? {
        return nil
    }
    
    // allows your subclass to override and change this according to state
    var sortDescriptorsForFetchRequest: [NSSortDescriptor]! {
        return []
    }
    
    // allows your subclass to override and change this according to state
    var searchPredicateForFetchRequest: NSPredicate? {
        return nil
    }
    
    // ... Typical NSFetchedResultsController and UITableViewDataSource code here.
}

That’s it for the basics, but what if my data model is a bit more interesting? In my current project I want my data to be sortable, filterable, searchable, and possibly groupable.

So I define the following:

import CoreData
@objc protocol Sortable: NSFetchRequestResult {
    static func defaultSortDescriptors() -> [NSSortDescriptor]!
}

@objc protocol Groupable: NSFetchRequestResult {
    var groupIndex: String! { get }
}

@objc protocol RelationshipFilterable: NSFetchRequestResult {
    static func relationshipFilterPredicate(for constraintObject:NSManagedObject?) -> NSPredicate?
}

@objc protocol TextSearchable: NSFetchRequestResult {
    static func searchPredicate(for searchTerm:String?) -> NSPredicate?
}

@objc protocol MyGenericDataObject: Sortable, Groupable, RelationshipFilterable, TextSearchable {
    // combines them
}

Then the cool stuff. Subclass the Basic view controller above:

class GenericFetchedResultsDataSource: BasicFetchedResultsDataSource where T:NSManagedObject {
    
    let allowsGrouping: Bool
    let allowsTextSearching: Bool
    
    override init(context: NSManagedObjectContext!, tableView: UITableView!) {
        self.allowsGrouping = true
        self.allowsTextSearching = true
        super.init(context: context, tableView: tableView)
    }
    
    var currentSearchTerm: String? {
        didSet {
            if self.allowsTextSearching {
                do {
                    try self.updateRequestAndFetch()
                }
                catch {
                    print("Fetch Error: \(error)")
                }
            }
        }
    }
    
    override var sectionNameKeyPath: String? {
        return self.allowsGrouping ? #keyPath(MyGenericDataObject.groupIndex) : nil
    }
    
    override var resultsControllerCacheName: String? {
        return nil
    }
    
    override var sortDescriptorsForFetchRequest: [NSSortDescriptor]! {
        return T.defaultSortDescriptors()
    }
    
    override var searchPredicateForFetchRequest: NSPredicate? {
        return self.allowsTextSearching ? T.searchPredicate(for: self.currentSearchTerm) : nil
    }
}

And that’s how you can work with generics and their subclasses. It’s why protocol oriented programming and swift go together nicely!

Multi-Threaded Core Data Solution

I’ve been wanting to revamp some old code that wasn’t performing as I like.  I had come up with something a bit too complicated involving NSOperationQueue, fetching remote data, parsing it all in the background, then saving that to my Core Data’s persistent store.

I always thought my solution a bit too complicated, and not sure it was entirely correct / robust.

I don’t know about you, but I regularly have 6-7 Google Chrome windows open with tons of tabs.  I have some articles that sit there for months that I don’t want to forget about.  One of which was written by Marcus Zarra, a prominent source of Core Data info.  He made it look so easy.

I’ve come to enjoy Core Data as a framework.  I think there is no other way at this point.  And I’m sure I’ve only scratched the surface of what it can do.  Along with mogenerator in your build pipeline, and especially the associated controller (NSFetchedResultsController) this framework is indispensable.

I basically took Marcus Zarra’s post and extended it to allow for doing data model work in the background, and also for making “scratch pad” contexts.  That is, consider editing forms but then the user hits “cancel”.  No rollbacks needed.  Just discard the “editor” context.  Or, consider data imports that run in the background, and you are on a view controller using an editor context.  You can even tell that editor context to update itself with those changes that occurred in the meantime.

Anyway, all a bit vague, so I’d like to just refer you to my repository that demonstrates what I’m talking about at http://github.com/horseshoe7

Please clone it and run it.  So a search for scenarioToExamine and change that value.

UPDATE:  I just found and read this article by Florian Kugler.  This solution above is an implementation of his so called “Stack #2”.  I really want to have a solution that’s going to be versatile and fast, so expect my repository’s implementation to change to be “Stack #3”.  Will update again after this happens.

UPDATE 2:  So I found my solution.  It’s in the Repo.  It’s called HSHybridThreeStack.  It has Marcus Zarra’s asynchronous background saving context, it has Florian Kugler’s separate context for speedy importing, it has main thread editing contexts that can optionally keep themselves updated to changes resulting from these import contexts, and an API that should be pretty straightforward.  You could refactor the HSCoreDataStack protocol and just incorporate it into one baseclass, but I kept it as such so that my various implementations were completely separate from one another.  So there is a lot of code repeated across implementations.  It is a Sandbox project after all and doesn’t represent an incredible approach to architecture.

Incredibly Useful Way to Print Your Core Data Model

So, I love a good bit of old fashioned Pen and Paper to get my thoughts out.  Maybe it’s that I enjoy just using a pen, the flow of the ink on the page.  (I’m, accordingly, a total pen snob).

Sometimes when I want to analyze a data model, I’d love to have it on paper, just so I can scribble, cross things out, etc, etc.

Until now, I found it difficult to have to deal with the CoreData model editor in Xcode because I couldn’t easily print it out, then I found this gem on stackoverflow.com

Which can be copy-pasted here:

I defined a 1m by 1m paper size, used it to create a PDF, cropped and then printed it:

  • Go to “File”->”Page Setup…”
  • Go to “Paper Size”->”Manage Custom Sizes…”
  • Define a new paper size with 1000×1000 mm and no borders
  • Go to “File”->”Print…”
  • Choose “PDF”->”Open PDF in Preview”
  • Go to “Tools”->”Rectangular Selection”, select the area to crop
  • Do “Tools”->”Crop”
  • Go to “File”->”Print…”, print

Sounds complicated, but works. Instead of cropping, you could use the scale factor in the Preview print dialog.

Core Data and UITableView – Re-ordering Rows Magic

This is as much for my reference as it is for yours. I have a situation where I needed to re-order rows in a UITableView. The models in table view are part of a Core Data to-many relationship, so I have an attribute on the model called position.

Full disclosure: I am basically re-hashing this post on stack overflow.

This is basically what was causing me problems: “NSFetchedResultsController and its delegate are not meant to be used for user-driven model changes.” and I was trying to re-assign position values, which would in turn update the managedObjectContext, which would in turn update the NSFetchedResultsController and there would be a lot of mayhem and incorrectly set data.

To make this brief, basically on any UITableViewController, or any UIViewController that implements UITableViewDataSource and UITableViewDelegate along with NSFetchedResultsControllerDelegate, add a private property called:

@property (nonatomic, assign, getter=isManuallyReordering) BOOL manuallyReordering;

and use it in your delegate methods as such:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
 self.manuallyReordering = YES;

 //...[UPDATE THE MODEL then SAVE CONTEXT]...

 self.manuallyReordering = NO;
}

and in this time ignore change messages sent to the NSFetchedResultsControllerDelegate

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
 if (self.isManuallyReordering) return;
 //...
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
 if (self.isManuallyReordering) return;
 //...
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
 if (self.isManuallyReordering) return;
 //...
}

There it is.

I’m slowly beginning to like Core Data

Core Data is by far the worst framework I have ever had to work with. Or is it?

It has a steep learning curve. Though once you’ve got your head around it, it doesn’t seem so bad after all.

I’m really starting to get accustomed to it. There are just some principles one needs to know in order to really not hate it.

I use the MagicalRecord library on top of it, and the mogenerator tool to make everything manageable.

I’ve got multi-threaded code and it cooperates (so far).

My approach is to keep most update operations off of the main thread, and saving that background context will update the main thread context, which in turn updates all the NSFetchedResultsControllers, and now my table views just animate those changes nicely!

I think, after numerous attempts, I am finally getting to be fit with CoreData and now can use it as my main persistence framework from now on. (Until they scrap it and move for something more Swift-style…)

Transformable Attributes in Core Data

The way I tend to use transformable attributes in Core Data is to just make use of the standard NSValueTransformer that gets used to encode/decode any object that conforms to the NSCoding Protocol, then in my NSManagedObject subclass, cast this to the type it’s expected to be (I use mogenerator to generate the subclasses by the way):

name it with suffix Object since it will have type id

name it with suffix Object since it will have type id

  @interface MyObject : _MyObject
  
  @property (nonatomic, strong) NSDictionary *metadata;
  
  @end

And then in the .m file, the accessors

  @implementation MyObject
  
  + (NSSet*)keyPathsForValuesAffectingMetadata
  {
      return [NSSet setWithObjects:@"metadataObject", nil];
  }
  
  - (void)setMetadata:(NSDictionary *)metadata
  {
      [self willChangeValueForKey:@"metadata"];
      self.metadataObject = metadata;
      [self didChangeValueForKey:@"metadata"];
  }
  
  - (NSDictionary*)metadata
  {
      [self willAccessValueForKey:@"metadata"];
      NSDictionary *metadata = (NSDictionary*)self.metadataObject;
      [self didAccessValueForKey:@"metadata"];
      return metadata;
  }
  
  // the rest of your implementation here...
  @end

Summary: Saving your custom object classes in CoreData is pretty straightforward when supporting the NSCoding protocol. It’s then easy to ensure typing by declaring properties that just wrap these Core Data attributes. This post is basically something of a recipe so I can remind myself later.

UPDATE:  See this post.  You can actually get mogenerator to set the data type for you and you don’t have to write these accessors at all.

Taking the pain out of CoreData

So far, if I have to make an App that uses CoreData, I will without question be using mogenerator and MagicalRecord.

Here are some links for mogenerator:

https://github.com/rentzsch/mogenerator

And then for MagicalRecord:

http://yannickloriot.com/2012/03/magicalrecord-how-to-make-programming-with-core-data-pleasant/

http://ablfx.com/blog/article/2

https://github.com/magicalpanda/MagicalRecord

They’re worth reading and checking out. CoreData does not seem like such a pain anymore.

Just remember:
* One NSManagedObjectContext per thread. So, if you are doing asynchronous work, save your context to the store on that other thread, then in your main thread callback, make sure you’ve passed an array of objectId’s (NSNumbers) so you can fetch those objects on the main thread.

I’m assuming you’re using CoreData for fairly simple purposes in mobile apps. Not really talking about editing apps with undo managers, etc. Just basic persistence and fetching and so on.