Skip to content

BLResultsController is not a drop-in replacement for the `NSFetchedResultsController` to be used with Realm. It's better.

License

Notifications You must be signed in to change notification settings

BellAppLab/BLResultsController

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

66 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

BLResultsController Version License

Platforms Swift support CocoaPods Compatible Carthage compatible SwiftPM compatible Twitter

BLResultsController

Contrary to popular belief, BLResultsController is not a drop-in replacement for the NSFetchedResultsController to be used with Realm. Oh no. It's better.

A ResultsController takes a Realm.Results and divides its objects into sections based on the sectionNameKeyPath and the first sortDescriptor. It then calculates the relative positions of those objects and generates section indices and IndexPaths that are ready to be passed to UITableViews and UICollectionViews.

But no expensive calculations are made on the main thread. That's right. Everything is done in the background, so your UI will remain as smooth and responsive as always.

As with Realm.Results, the ResultsController is a live, auto-updating container that will keep notifying you of changes in the dataset for as long as you hold a strong reference to it. You register to receive those changes by calling setChangeCallback(_:) on your controller.

Changes to the underlying dataset are calculated on a background queue, therefore the UI thread is not impacted by the ResultsController's overhead.

Note: As with Realm itself, the ResultsController is not thread-safe. You should only call most of its methods from the main thread.

Features

  • Calculates everything on a background thread. 🏎
  • No objects are retained, so memory footprint is minimal. πŸ‘Ύ
  • Calculates section index titles. 😲
  • Allows for user-initiated search. πŸ•΅οΈβ€β™€οΈπŸ•΅οΈβ€β™‚οΈ
  • Most methods return in O(1). 😎
  • Well documented. πŸ€“
  • Well tested. πŸ‘©β€πŸ”¬πŸ‘¨β€πŸ”¬

Specs

  • RealmSwift 10.0.0+
  • iOS 13+
  • tvOS 13+
  • macOS 10.13+
  • Swift 5.0+

BLResultsController also uses the amazing BackgroundRealm. Have a look!

Example

ResultsController

Install the ResultsControllerElement protocol on your RealmObject subclass:

public final class Foo: Object, ResultsControllerElement
{
    //If your class doesn't have a unique identifier yet, do this
    public dynamic var resultsControllerId: String = UUID().uuidString
    
    //If it does, you can do this
    public var resultsControllerId: String {
        return <#id#>
    }
}

Then:

import UIKit
import RealmSwift
import BLResultsController

class ViewController: UITableViewController {
    let controller: ResultsController<<#SectionType#>>, <#ElementType#>> = {
        do {
            let realm = <#instantiate your realm#>
            let keyPath = <#the key path to your Element's property to be used as a section#>
            return try ResultsController(
                realm: realm,
                sectionNameKeyPath: keyPath,
                sortDescriptors: [
                    SortDescriptor(keyPath: keyPath)
                ]
            )
        } catch {
            assertionFailure("\(error)")
            //do something about the error
        }
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        controller.setChangeCallback { [weak self] change in
            switch change {
            case .reload(_):
                self?.tableView.reloadData()
            case .sectionUpdate(_, let insertedSections, let deletedSections):
                self?.tableView.beginUpdates()
                insertedSections.forEach { self?.tableView.insertSections($0, with: .automatic) }
                deletedSections.forEach { self?.tableView.deleteSections($0, with: .automatic) }
                self?.tableView.endUpdates()
            case .rowUpdate(_, let insertedItems, let deletedItems, let updatedItems):
                self?.tableView.beginUpdates()
                self?.tableView.insertRows(at: insertedItems, with: .automatic)
                self?.tableView.deleteRows(at: deletedItems, with: .automatic)
                self?.tableView.reloadRows(at: updatedItems, with: .automatic)
                self?.tableView.endUpdates()
            }
        }
        
        controller.setFormatSectionIndexTitleCallback { (section, _) -> String in
            return section
        }
        
        controller.setSortSectionIndexTitles { (sections, _) in
            sections.sort(by: { $0 < $1 })
        }

        controller.start()
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return controller.numberOfSections()
    }

    override func tableView(_ tableView: UITableView,
                            numberOfRowsInSection section: Int) -> Int
    {
        return controller.numberOfItems(in: section)
    }

    override func tableView(_ tableView: UITableView,
                            cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: <#identifier#>) else {
            fatalError("Did we configure the cell correctly on IB?")
        }
        <#code#>
        return cell
    }
    
    override func tableView(_ tableView: UITableView,
                            titleForHeaderInSection section: Int) -> String?
    {
        return controller.section(at: section)
    }
    
    override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        return controller.indexTitles()
    }
    
    override func tableView(_ tableView: UITableView,
                            sectionForSectionIndexTitle title: String,
                            at index: Int) -> Int
    {
        return controller.indexPath(forIndexTitle: title).section
    }
}

Boom πŸ’₯

Installation

Cocoapods

pod 'BLResultsController', '~> 3.0'

Then import BLResultsController where needed.

Carthage

github "BellAppLab/BLResultsController" ~> 3.0

Then import BLResultsController where needed.

Swift Package Manager

.package(url: "https://github.com/BellAppLab/BLResultsController.git", from: "3.0")

Git Submodules

cd toYourProjectsFolder
git submodule add -b submodule --name BLResultsController https://github.com/BellAppLab/BLResultsController.git

Then drag the BLResultsController folder into your Xcode project.

Author

Bell App Lab, apps@bellapplab.com

Contributing

Check this out.

Credits

License

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

About

BLResultsController is not a drop-in replacement for the `NSFetchedResultsController` to be used with Realm. It's better.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published