Latest 1.1.1
License MIT
Platforms ios 8.0, requires ARC

A Swift framework for consuming and displaying feeds in iOS

FeedCache is an alternative to using CoreData to manage paginated feed data. Architecturally, it replaces an NSFetchedResultsController, while caching data with NSCoding so the feed can load quickly from a cold start.


FeedCache handles:

  • Insertions
  • Deletions (through first-page cache deletion)
  • Caching

pull to refresh

First-page cache deletion

When you first load your table view or collection view, FeedCache loads the first page of your feed. If the first page has changed from the last time it was loaded, the cache is cleared and replaced with the new items. This handles deletions and keeps the data on your phone from getting stale.

To Use

Define your model

First, you must make whatever items you want to view conform to the FeedItem protocol, which ensures it conforms to NSCoding and is Hashable. It is important to override isEqual(), as this is what FeedCache uses to determine which items should be inserted or deleted. By default isEqual() compares objects’ memory addresses, but we want it to compare hash values that are computed from properties:

class TestItem: NSObject, FeedItem{
    var name: String? = nil

    init(name: String){ = name

    @objc required  init(coder aDecoder: NSCoder){
        name = aDecoder.decodeObjectForKey("name") as? String

    @objc  func encodeWithCoder(aCoder: NSCoder){
        aCoder.encodeObject(name, forKey: "name")

    // It's important to override isEqual so it compares properties
    override func isEqual(object: AnyObject?) -> Bool {
        if let object = object as? TestItem {
            return hashValue == object.hashValue
        return false

    // Override hashValue to compute a value from the object's properties
    override var hashValue : Int{
        var h: Int = 0
        if let name = name { h ^= name.hash }
        return h

Define your fetch request

The FeedFetchRequest protocol requires you to implement the fetchItems method. This is called by FeedCache.

struct TestFeedRequest: FeedFetchRequest {
    var clearStaleDataOnCompletion: Bool
    var pageNumber: Int
    var itemsPerPage: Int

    init(clearStaleDataOnCompletion: Bool, pageNumber: Int, itemsPerPage: Int){
        self.clearStaleDataOnCompletion = clearStaleDataOnCompletion
        self.pageNumber = pageNumber
        self.itemsPerPage = itemsPerPage

    func fetchItems(success success: (newItems: [TestItem]) -> (), failure: (NSError) -> ()) {
        MockService.fetchItems(pageNumber, itemsPerPage: itemsPerPage, parameters: nil, success: { (newItems) -> () in
            success(newItems: newItems)
        }) { (error) -> () in


Create your own cache preferences as an enum. This example is for many feeds.

enum MyCachePreferences : CachePreferences{
    case TestItems
    case Friends
    case Photos
    case PhotosNoCache

    var cacheOn: Bool {
        switch self {
        case .TestItems
            return true
        case .Friends:
            return true
        case Photos:
            return true
            return false

    var cacheName: String {
        switch self {
        case .TestItem
            return "TestItems"
        case .Friends:
            return "Friends"
        case .Photos:
            return "Photos"
          return ""

Create a FeedController

Now create a FeedController in your UITableViewController or UICollectionViewController, specifying the type of FeedItem it will handle.

class MyTableViewController: UITableViewController, FeedControllerDelegate {

    var feedController: FeedController<PeopleFeedItem>!
    var items = [TestItem]()

    override func viewDidLoad() {
      feedController = FeedController<PeopleFeedItem>(cachePreferences: MyCachePreferences.TestItems, section: 0)
      feedController.delegate = self
      //Defensively copy items to prevent race conditions
      items = feedController.items

      self.currentPage = 1
      let request = PeopleFeedRequest(currentPage, clearStaleDataOnCompletion: true, count: itemsPerPage)

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)

        let item = items[indexPath.row]
        cell.textLabel?.text =

        if indexPath.row == (itemsPerPage * currentPage) - 1 {
        return cell

    func loadMoreItems() {
        let request = PeopleFeedRequest(currentPage, clearStaleDataOnCompletion: false, count: itemsPerPage)

    //MARK: ***** FeedControllerDelegate Methods *****

    func feedController(feedController: FeedControllerGeneric, itemsCopy: [AnyObject], itemsAdded: [NSIndexPath], itemsDeleted: [NSIndexPath]) {
        //Defensively copy items to prevent race conditions
        items = itemsCopy  

        tableView.insertRowsAtIndexPaths(itemsAdded, withRowAnimation: UITableViewRowAnimation.Automatic)
        tableView.deleteRowsAtIndexPaths(itemsDeleted, withRowAnimation: UITableViewRowAnimation.Automatic)


You can use Cocoapods to install FeedCacheby adding it to your Podfile:

platform :ios, '8.0'

target 'MyApp' do
    pod 'FeedCache'

Latest podspec

    "name": "FeedCache",
    "version": "1.1.1",
    "summary": "A Swift framework for consuming and displaying feeds in iOS.",
    "description": "FeedCache is an alternative to using CoreData to manage paginated feed data. Architecturally, it replaces an NSFetchedResultsController, while caching data with NSCoding so the feed can load quickly from a cold start.",
    "homepage": "",
    "license": "MIT",
    "authors": {
        "Rob Seward": "[email protected]"
    "source": {
        "git": "",
        "tag": "1.1.1"
    "social_media_url": "",
    "platforms": {
        "ios": "8.0"
    "requires_arc": true,
    "source_files": "Source/*.swift",
    "pushed_with_swift_version": "3.0"

Pin It on Pinterest

Share This