What Is a Core Data Fault

This tutorial zooms in on an important topic for anyone working with Core Data, faulting. Faulting is a concept that often confuses developer new to Core Data. Before I explain what faulting is, I want to show it to you.

Meet Notes

For this tutorial, I use the application we build in Mastering Core Data With Swift 3. Clone or download the repository from GitHub and open the project in Xcode 8 or higher.

Before we run our experiment, open the data model of the project by selecting Notes.xcdatamodeld. Switch to the graph style of the data model editor to get an idea of the entities, attributes, and relationships defined in the data model.

The Data Model of Notes

Notes is an application that manages the user's notes. A note has a title and contents. It also knows when it was created and last updated. A note can be associated with a category and a category can contain multiple notes. In other words, the Note and Category entities share a one-to-many relationship. A note can also have tags and a tag can be linked to multiple notes. The Note and Tag entities share a many-to-many relationship.

Build and run the application to give it a try. Make sure Notes contains a few notes, a few categories, and a few tags. That is important for our experiment.

Meet Notes

Faulting In Action

Open NotesViewController.swift and navigate to the fetchNotes() method. Replace the current implementation and create a fetch request for the Note entity in a performAndWait(_:) closure of the main managed object context. Execute the fetch request and print the first note to the console. Run the application to see the result.

private func fetchNotes() {
    coreDataManager.mainManagedObjectContext.performAndWait {
        do {
            let fetchRequest: NSFetchRequest<Note> = Note.fetchRequest()

            let notes = try fetchRequest.execute()

            if let note = notes.first {
                print(note)
            }

        } catch {
            print(error)
        }
    }
}

Do you see anything unusual in the console? We fetched a record of the Note entity. But notice that the data associated with the record is missing. Instead we see the word fault.

<Note: 0x1700ae460> (entity: Note; id: 0xd000000000040000 <x-coredata://F5DFD06E-C22A-47C1-9C1F-ED4F0D357B92/Note/p1> ; data: <fault>)

Now that you have seen faulting in action, it is time to explain what it is and why it is important for Core Data.

What Is Faulting

Core Data is a framework that is incredibly performant thanks to the hard work of the Core Data team at Apple. As you know, Core Data can only operate on records of the persistent store once they are loaded into memory. This is only possible because Core Data is heavily optimized to keep its memory footprint as low as possible. One of the techniques Core Data uses to accomplish this is faulting.

But the team at Apple didn't invent faulting. Several other frameworks use a similar strategy to accomplish similar goals. Ruby on Rails and Ember come to mind.

Even though faulting may look mysterious at first, the idea is simple. Core Data only fetches the data it absolutely needs to satisfy the needs of your application. That is faulting in a nutshell.

The idea of faulting is simple, but the underlying implementation is an advanced bit of programming. Fortunately, we don't have to worry about that. That is the responsibility of the framework. Faulting just works. Let me show you how it works with another example.

Below the print statement, we safely unwrap the value of the title property and print it to the console and we add another print statement for the note.

private func fetchNotes() {
    coreDataManager.mainManagedObjectContext.performAndWait {
        do {
            let fetchRequest: NSFetchRequest<Note> = Note.fetchRequest()

            let notes = try fetchRequest.execute()

            if let note = notes.first {
                print(note)

                if let title = note.title {
                    print(title)
                }

                print(note)
            }

        } catch {
            print(error)
        }
    }
}

What is happening here? We print the note to the console, ask the value of one of the properties of the note, and print the note again. Why we do that becomes clear when we inspect the results in the console. Run the application and take a look at the output in the console.

<Note: 0x1700b1ee0> (entity: Note; id: 0xd000000000040000 <x-coredata://F5DFD06E-C22A-47C1-9C1F-ED4F0D357B92/Note/p1> ; data: <fault>)
What Is Stopping You From Shipping
<Note: 0x1700b1ee0> (entity: Note; id: 0xd000000000040000 <x-coredata://F5DFD06E-C22A-47C1-9C1F-ED4F0D357B92/Note/p1> ; data: {
    category = "0xd000000000100002 <x-coredata://F5DFD06E-C22A-47C1-9C1F-ED4F0D357B92/Category/p4>";
    contents = "Last month, I wrote about building and shipping something, putting something into people\U2019s hands. I understand that life gets in the way from time to time, but I am genuinely wondering if you are crea";
    createdAt = "2016-11-02 18:05:18 +0000";
    tags = "<relationship fault: 0x1700321e0 'tags'>";
    title = "What Is Stopping You From Shipping";
    updatedAt = "2016-11-05 08:55:23 +0000";
})

The first print statement shows the fault we discussed earlier. Despite this fault, we can access the value of the title property and print it to the console. This is confirmed by the third print statement in which we print the note again. What is happening here?

Firing a Core Data Fault

Core Data gives us what we ask for, exactly that and nothing more. We first asked for the user's notes and Core Data diligently gave us the list of notes. But, as you can see in the console, it is a list of empty records.

From the moment we ask for the value of a property of one of the records, Core Data jumps into action and fetches the data from the persistent store. This is better known as firing a Core Data fault. But it doesn't just fetch the value of the title property. As you can see in the console, Core Data fetches the values of every property of the note with the exception of relationships.

Notice that the value of the tags property is missing. Instead, Xcode displays relationship fault. This means that the tags of the note haven't been fetched yet. We don't even know the number of tags that are linked to the note.

And the same is true for the category property. Even though it seems as if Core Data has fetched the data for the category of the note, it hasn't. This becomes clear if we print the value of the category property.

private func fetchNotes() {
    coreDataManager.mainManagedObjectContext.performAndWait {
        do {
            let fetchRequest: NSFetchRequest<Note> = Note.fetchRequest()

            let notes = try fetchRequest.execute()

            if let note = notes.first {
                print(note)

                if let title = note.title {
                    print(title)
                }

                print(note)

                if let category = note.category {
                    print(category)
                }
            }

        } catch {
            print(error)
        }
    }
}
<Category: 0x1740ad9e0> (entity: Category; id: 0xd000000000100002 <x-coredata://F5DFD06E-C22A-47C1-9C1F-ED4F0D357B92/Category/p4> ; data: <fault>)

I hope it is starting to become clear that Core Data is very lazy ... but in a good way. It fetches the minimum amount of data to satisfy the needs of your application.

Faulting and Relationships

Let's now print the value of the tags property to the console.

private func fetchNotes() {
    coreDataManager.mainManagedObjectContext.performAndWait {
        do {
            let fetchRequest: NSFetchRequest<Note> = Note.fetchRequest()

            let notes = try fetchRequest.execute()

            if let note = notes.first {
                if let tags = note.tags as? Set<Tag> {
                    print(tags)

                    for tag in tags {
                        print(tag.name ?? "")
                    }
                }
            }

        } catch {
            print(error)
        }
    }
}
[<Tag: 0x1700b11c0> (entity: Tag; id: 0xd0000000000c0004 <x-coredata://F5DFD06E-C22A-47C1-9C1F-ED4F0D357B92/Tag/p3> ; data: <fault>), <Tag: 0x1700b10a0> (entity: Tag; id: 0xd000000000040004 <x-coredata://F5DFD06E-C22A-47C1-9C1F-ED4F0D357B92/Tag/p1> ; data: <fault>)]
Colorful
Opinion

Core Data hands us a set of objects, but it hasn't actually fetched the tags itself. The data of each tag is a fault. The data is fetched the moment we access it.

Colorful
Opinion

There are several reasons why it is important that you know about and understand Core Data faulting. For example, what happens if a fault is fired for data that no longer exists? This is a problem every developer runs into at some point.

When Core Data tries to fetch data from the persistent store that no longer exists, it tells you it is unable to fulfill the fault. In earlier versions of the framework, Core Data would throw an exception, resulting in a crash of the application.

Fortunately, Core Data has evolved over the years and the framework has become better at handling issues like this. As of iOS 9, the NSManagedObjectContext class defines a new property, shouldDeleteInaccessibleFaults. This property is set to true by default.

But let me show you what happens if this property is set to false. This is the old behavior of the framework. Open the CoreDataManager class and set the shouldDeleteInaccessibleFaults property to false for both the main managed object context and the private managed object context.

private(set) lazy var mainManagedObjectContext: NSManagedObjectContext = {
    // Initialize Managed Object Context
    let managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)

    // Configure Managed Object Context
    managedObjectContext.shouldDeleteInaccessibleFaults = false
    managedObjectContext.parent = self.privateManagedObjectContext

    return managedObjectContext
}()

private lazy var privateManagedObjectContext: NSManagedObjectContext = {
    // Initialize Managed Object Context
    let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)

    // Configure Managed Object Context
    managedObjectContext.shouldDeleteInaccessibleFaults = false
    managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator

    return managedObjectContext
}()

Run the application and assign a category to a note. Push the application to the background to save the changes and stop the application.

Add the note to a category.

To show you the problem, I am going to modify the persistent store. Keep in mind that I only do this to show you what could happen in production. We delete the category record from the database.

Delete the category of the note in the persistent store.

When we run the application again, an exception is thrown. If we inspect the output in the console, we see the reason for the exception. The reason is not surprising. Core Data is unable to fulfill a fault. It is not able to fetch the category for the note.

2016-11-17 07:08:24.888309 Notes[7356:3196839] *** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0xd000000000100002 <x-coredata://F5DFD06E-C22A-47C1-9C1F-ED4F0D357B92/Category/p4>''

To show the color of the category in the notes view controller, Core Data needs to fetch the category for the note. But the category no longer exists since we removed it from the SQLite database.

Even though we deliberately tampered with the database, this problem can also occur in production. It used to drive developers unfamiliar with the framework crazy.

As of iOS 9, Core Data gives developers the option to fail silently by deleting any faults that are inaccessible. Even though this avoids the exception from being thrown, your application could en up in a state it doesn't know how to handle.

Fortunately, there is a better solution that was recently introduced, query generations. But that is a topic for another tutorial.