NSIncrementalStore Tutorial for iOS: Getting Started

Learn how you can use the very powerful NSIncrementalStore to provide Core Data with a completely custom data storage option. By Rony Rozen.

Leave a rating/review
Save for later
Share

Working with large amounts of data and loading it to memory can be an expensive and time-consuming operation. Wouldn’t it be great if you could bring into memory just the data your app needs to operate?

NSIncrementalStore gives you exactly that. It’s a persistent store in Core Data that allows you to read and write just the content you actually need, little by little.

In this NSIncrementalStore tutorial, you’ll take a Core Data app that uses an atomic (“regular”) persistent store and change it to use NSIncrementalStore instead.

The starter project is suspiciously similar to the finished project from the Getting Started with Core Data tutorial. So, if you feel like your Core Data expertise needs freshening up, you’re more than welcome to check out that tutorial before proceeding!

Getting Started

Download the starter project for this NSIncrementalStore tutorial from here.

Unzip it, and build and run the starter project, where you’ll see a table view with no content. The header has two buttons: Refresh on the left and Add on the right.

The Refresh button will add a random number of new “terrible” bugs to the list, while the Add button will let you add a single bug with custom text.

Terminating and relaunching the app will save the current state of bugs, since you don’t want to lose them before you know they’ve been resolved.

This is all good and well for cases when your users have a small number of bugs. But some of your users have a huge number of bugs in their apps. Loading all bugs into memory could for that reason cause your app to slow down and, in the worst case, run out of memory.

Therefore, you’ll need to upgrade the current version of the app to use NSIncrementalStore to load the huge lists of bugs little by little. Not to mention the fact that this will help prepare the app for its next version, which will have a database in the cloud instead of the local one you’re currently using. See, with a cloud-based database you would also need to retrieve bugs little by little as to not consume a lot of mobile data.

This sounds great, but before you dive into the code you should probably get a little familiar with NSIncrementalStore first.

What is NSIncrementalStore?

Core Data is divided into several layers:

This NSIncrementalStore tutorial focuses on the bottom layer: the persistent store. NSIncrementalStore is in charge of the implementation of the persistence mechanism, while the Core Data framework takes care of the managed objects in memory.

Incremental stores must perform three tasks:

  • Handle metadata the persistent store coordinator uses to manage your store.
  • Handle fetch and save requests sent by a managed object context.
  • Provide missing data when requested by a managed object.

All of these will be covered in the next sections of this NSIncrementalStore tutorial. In the meantime, what’s important for you to understand, as you’re getting into the code, is that you’ll only be changing the bottom layer of Core Data as seen in the illustration above. You won’t be changing anything in BugSquasherViewController.swift. The save/load actions will remain unchanged as far as the app is concerned – which is the whole beauty of this architecture.

Curious to learn how this is done? Time to dive right in!

Setting Up an Incremental Store

First, create a new class for your custom NSIncrementalStore. Start by creating a new file using File\New\File\Swift File. Name the new file BugSquasherIncrementalStore.swift.

Next, add the following class definition to BugSquasherIncrementalStore.swift:

import CoreData
class BugSquasherIncrementalStore : NSIncrementalStore {
  var bugsDB: [String] = []
  
  class var storeType: String {
    return String(describing: BugSquasherIncrementalStore.self)
  }
}

Your new custom class inherits from NSIncrementalStore, which is an abstract subclass of NSPersistentStore.

At this point, the implementation includes:

  • An array of bugs, represented as Strings. Since this NSIncrementalStore tutorial focuses on the main concepts of NSIncrementalStore, and not on specific underlying store implementation, the “database” is going to be extremely basic: an array of Bug objects being saved to and loaded from a file. This is the array that will hold your bugs.
  • A class variable with a string representing your new custom class. This will be used to let the persistent store coordinator know about your new custom class.

If you build and run the app now, everything will still behave exactly the same as before. You need to register your NSIncrementalStore with Core Data in order to use it in your app.

In BugSquasherAppDelegate.swift, add this line to application:didFinishLaunchingWithOptions:

let storeType = containerName + "." + BugSquasherIncrementalStore.storeType
NSPersistentStoreCoordinator.registerStoreClass(BugSquasherIncrementalStore.self, forStoreType: storeType)

This will ensure that registration happens before you attempt to add your custom incremental store to your persistent store coordinator. The persistent store coordinator creates instances of your class as needed based on the store type you provide it with.

Now you’re ready to use this store by enabling the store type on the persistent container. Still in BugSquasherAppDelegate.swift, add the following code right after initializing container inside the persistentContainer scope:

var bugSquasherStoreDescription = NSPersistentStoreDescription()
bugSquasherStoreDescription.type = container.name + "." + BugSquasherIncrementalStore.storeType
container.persistentStoreDescriptions = [bugSquasherStoreDescription]

All you do in this code block is let the container know that it needs to use your new custom class as a persistent store when relevant. Since this is the only persistent store you provide it with, this will be the one used whenever the managed object context will attempt to load or save an object.

When your persistent store coordinator creates an instance of your custom incremental store, it needs to perform basic validation and setup.

To do this, open BugSquasherIncrementalStore.swift and add the following method:

override func loadMetadata() throws {
  // 1
  let uuid = "Bugs Database"
  self.metadata = [NSStoreTypeKey: BugSquasherIncrementalStore.storeType, 
                   NSStoreUUIDKey: uuid]
  // 2
  if let dir = FileManager.default.urls(for: .documentDirectory, 
                                        in: .userDomainMask).first {
    let path = dir.appendingPathComponent("bugs.txt")
    let loadedArray = NSMutableArray(contentsOf: path)

    if loadedArray != nil {
      bugsDB = loadedArray as! [String]
    }
  }
}

Your loadMetadata: implementation needs to include the following:

  1. Creating the store object’s metadata dictionary, with (at least) these two key-value pairs:
    • NSStoreUUIDKey: A unique identifier for the store at the given URL. It must be uniquely and reproducibly derivable, such that multiple instances of your store return the same UUID.
    • NSStoreTypeKey: The string identifier you used to register the store with the persistent store coordinator.
  2. Loading metadata from the backing data store if it already exists. For the purposes of this NSIncrementalStore tutorial, you load the content saved to a text file on disk into memory so that you can continue working with the in-memory representation of the bugs data in bugsDB.

The last thing to do so you can run the app without crashing is to satisfy Core Data in loading and saving data from the underlying persistent store.

In BugSquasherIncrementalStore.swift, add the following function implementation:

override func execute(_ request: NSPersistentStoreRequest,
                      with context: NSManagedObjectContext?) throws -> Any {
  return []
}

This is still just a skeleton. You’ll add the actual fetching and saving in the next couple of sections.

Build and run your app. Your table view should now contain no content, no matter how many bugs you had there from playing around with your starter project. This makes sense, since the method in charge of fetching and loading data currently doesn’t do much. Time to fix, and then load, some bugs!