Latest 0.1.0
Homepage https://github.com/agisboye/RealmCollectionController
License MIT
Platforms ios 8.0, requires ARC
Dependencies RealmSwift
Authors

Icon

RealmCollectionController makes it super easy to work with Realm and UITableView/UICollectionView. Both row and section updates are computed for you, giving you smooth transitions when your data changes. All you have to do is implement a couple of delegate methods.

RealmCollectionController is designed to be simple and lightweight. Unlike other libraries, RealmCollectionController doesn’t attempt to mimic or reimplement NSFetchedResultsController. It doesn’t use it’s own observation/notification system, relying instead on the one provided in Realm.

RealmCollectionController is to NSFetchedResultsController as Realm is to Core Data. Much simpler and with a lot less boilerplate code.

A collection refers to any type that conforms to RealmCollectionType:

  • Results
  • List
  • LinkingObjects
  • AnyRealmCollection

Features

  • Easily create sections
  • Section keys are provided by a closure rather than key paths, allowing you to group by computed properties
  • Reduce boilerplate by not having to implement the Realm notification mechanism in each view controller
  • Create animated transitions when changing collections

Requirements

  • iOS 8.0+
  • Xcode 7.3+
  • Realm Cocoa 1.0+

Dependencies

The library obviously depends on Realm. Besides that, no dependencies.

Installation

CocoaPods

To integrate RealmCollectionController into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod 'RealmCollectionController'

Carthage

To integrate RealmCollectionController into your Xcode project using Carthage, specify it in your Cartfile:

github "agisboye/RealmCollectionController"

Run carthage update to build the framework and drag RealmCollectionController.framework into your Xcode project.

Usage

Start by importing the modules.

import RealmSwift
import RealmCollectionController

Implement the RealmCollectionControllerDelegate on your view controller. This will feel slightly familiar if you’ve worked with NSFetchedResultsController.
You can choose any animation style you want, but these are sane defaults.

extension ViewController: RealmCollectionControllerDelegate {

    func controllerRequestsDataReload<T: Object>(controller: RealmCollectionController<T>) {
        tableView.reloadData()
    }

    func controllerWillUpdateCollection<T: Object>(controller: RealmCollectionController<T>) {
        tableView.beginUpdates()
    }

    func controllerDidUpdateCollection<T: Object>(controller: RealmCollectionController<T>) {
        tableView.endUpdates()
    }

    func controllerDidChangeRows<T: Object>(controller: RealmCollectionController<T>, changes: [RowChange]) {

        for change in changes {
            switch change {
            case .Delete(let indexPath): tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
            case .Insert(let indexPath): tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
            case .Reload(let indexPath): tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
            }
        }

    }

    func controllerDidChangeSections<T: Object>(controller: RealmCollectionController<T>, change: SectionChange) {

        switch change {
        case .Delete(let indexSet): tableView.deleteSections(indexSet, withRowAnimation: .Fade)
        case .Insert(let indexSet): tableView.insertSections(indexSet, withRowAnimation: .Left)
        case .Reload(let indexSet): tableView.reloadSections(indexSet, withRowAnimation: .Fade)
        }

    }

}

Set your view controller as delegate and pass a collection to the collection controller.

class ViewController: UIViewController {

    let tableView = UITableView()

    let realm = try! Realm()
    let collectionController = RealmCollectionController<Word>() // Word is our Realm model object

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionController.delegate = self // Assign before setting the collection

        let collection = realm.objects(Word).sorted("word")
        collectionController.setCollection(collection, transition: false) { word -> String in
            return word.firstLetter
        }

    }

}

That’s it. No fetch requests, key paths or section caches.
Your delegate methods will now be called when the collection changes.

Providing data to a UITableViewDataSource

You should keep a reference to the collectionController, but you don’t need to hold on to the collection itself.
Instead, when your table or collection view asks for data, you simply let the collection controller provide it.

extension ViewController: UITableViewDataSource {

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return collectionController.numberOfSections()
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return collectionController.numberOfRowsInSection(section)
    }

    func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return collectionController.titleForHeaderInSection(section)
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = UITableViewCell()
        let object = collectionController.itemForIndexPath(indexPath)
        // OR:
        let object = collectionController[indexPath.row][indexPath.section]

        cell.textLabel?.text = object.word

        return cell

    }

}

Changing collections

Maybe you want to change the sort descriptor or filter some objects. No problem.
Simply create a new collection and pass it to the collection controller.
Set transition: true to animate from the previous collection to the new one.

let collection = realm.objects(Word).filter("word != %@", "Core Data").sorted("word")

collectionController.setCollection(collection, transition: true) { word -> String in
    return word.firstLetter
}

The only restriction is that you can’t change from one type of model to another. The collection controller was specialized upon instantiation.

Using RealmCollectionController without sections

You should use RealmCollectionController even if you don’t need sections.
Simply set the collection without passing in a section name callback. The controller will put everything into a single section.

let collection = realm.objects(Word).sorted("word")
collectionController.setCollection(collection, transition: true)

If you change your mind about sections later, you can just transition to a new collection and use sections with that one.
Note: You should still implement numberOfSectionsInTableView(tableView: UITableView).

Clearing collections

To stop displaying data, simply tell the controller to clear the collection.

collectionController.clearCollection(animated: true)

You can choose to have this done in an animated manner or simply having everything disappear instantly. Poof.

Retain cycle consinderations

If your section name callback has a reference to self, you need to make sure that the reference is weak or unowned.
Otherwise the controller will capture self while self captures the controller, and you have a retain cycle.

collectionController.setCollection(collection, sectionNameCallback: { [unowned self] in
    return self.dateFormatter.stringFromDate($0.startDate)
})

Example

Give the example app a try!
It’s got a list of the 1000 most common English words, from which it randomly picks up to 100 words and displays them in a table view.
You can then modify those objects in realm and watch how the collection controller updates the table view.

Icon

To do

  • Tests
  • Something like UIIndexCollation, making it easy to use localized section index titles with the controller.
  • macOS support – I have yet to develop for macOS, but I suppose this library could be made to work with NSTableView

I would also like to add support for managing multiple collections of different types. Say you have a file sharing app, in which you have a view controller that displays both directories and files. Those may represented by two different models, yet they need to be displayed in the same table.

Contributing

Contributions are very welcome. Open an issue or submit a pull request.

Acknowledgements

Made by August Heegaard.
RealmCollectionController makes use of the LCS algorithm, modified from the original Swift implementation by jflinter for his Dwifft library. If you want something similar to this but you don’t use Realm or need sections, I’d recommend checking it out.

License

RealmCollectionController is available under the MIT license. See the LICENSE file for more info.

Latest podspec

{
    "name": "RealmCollectionController",
    "version": "0.1.0",
    "summary": "RealmCollectionController makes it easy to work with Realm and UITableView/UICollectionView.",
    "homepage": "https://github.com/agisboye/RealmCollectionController",
    "license": {
        "type": "MIT",
        "file": "LICENSE"
    },
    "authors": {
        "August Heegaard": "[email protected]"
    },
    "platforms": {
        "ios": "8.0"
    },
    "source": {
        "git": "https://github.com/agisboye/RealmCollectionController.git",
        "tag": "0.1.0"
    },
    "source_files": "Source/**/*",
    "requires_arc": true,
    "dependencies": {
        "RealmSwift": [
            ">=1.0.0"
        ]
    }
}

Pin It on Pinterest

Share This