In this episode, I'd like to discuss a concept that often confuses developers new to Core Data, faulting. Before I explain what faulting is, I want to show it to you.

Open the starter project of this episode and run it in the simulator. Make sure the application contains a few notes, a few categories, and a few tags. That's important to illustrate the concept of faulting.

Exploring Faults

Open NotesViewController.swift and navigate to the fetchNotes() method. Replace the current implementation and create a fetch request for the Note entity in a perform(_:) closure of the main managed object context.

NotesViewController.swift

private func fetchNotes() {
    coreDataManager.mainManagedObjectContext.perform {
        do {
            // Create Fetch Request
            let fetchRequest: NSFetchRequest<Note> = Note.fetchRequest()

            // Fetch Notes
            let notes = try fetchRequest.execute()

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

        } catch {
            print(error)
        }
    }
}

We execute the fetch request and print the first note to the console. Run the application to see the result. This is what the output looks like.

<Note: 0x1c009f4f0> (entity: Note; id: 0xd000000000040000 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Note/p1> ; data: <fault>)

You may not see anything unusual. We fetched a record of the Note entity. But notice that the data associated with the record isn't present. Instead we see the word fault.

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

What Is a Fault

Core Data is a framework that's 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're loaded into memory.

This is only possible because Core Data is heavily optimized to keep its memory footprint as small as possible. One of the techniques Core Data uses to accomplish this is faulting.

Faulting wasn't invented by the Core Data team at Apple. 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 requirements of your application. In the above example, Core Data hasn't fetched the property values of the note yet. Why is that? Because we haven't asked for it. Since we don't access any of the properties of the note, Core Data hasn't bothered fetching the note's property values. Core Data is efficient and performant by being lazy.

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's the responsibility of the framework. Faulting just works.

Firing a Fault

Let me show you how faulting 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 also add another print statement for the note.

NotesViewController.swift

private func fetchNotes() {
    coreDataManager.mainManagedObjectContext.perform {
        do {
            // Create Fetch Request
            let fetchRequest: NSFetchRequest<Note> = Note.fetchRequest()

            // Fetch Notes
            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's 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 this becomes clear when we inspect the results in the console. Run the application. This is what the output looks like. Let's break it down.

<Note: 0x1c409b620> (entity: Note; id: 0xd000000000040000 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Note/p1> ; data: <fault>)
My First Note
<Note: 0x1c409b620> (entity: Note; id: 0xd000000000040000 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Note/p1> ; data: {
    category = "0xd000000000080002 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Category/p2>";
    contents = "Some text ...";
    createdAt = "2017-07-06 07:22:18 +0000";
    tags = "<relationship fault: 0x1c403e900 'tags'>";
    title = "My First Note";
    updatedAt = "2017-07-07 09:15:08 +0000";
})

The first print statement shows the fault we discussed earlier. This isn't new.

<Note: 0x1c409b620> (entity: Note; id: 0xd000000000040000 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Note/p1> ; data: <fault>)

Despite this fault, we can access the value of the title property and print it to the console. That's interesting.

My First Note

And this is confirmed by the third print statement in which we print the note again.

<Note: 0x1c409b620> (entity: Note; id: 0xd000000000040000 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Note/p1> ; data: {
    category = "0xd000000000080002 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Category/p2>";
    contents = "Some text ...";
    createdAt = "2017-07-06 07:22:18 +0000";
    tags = "<relationship fault: 0x1c403e900 'tags'>";
    title = "My First Note";
    updatedAt = "2017-07-07 09:15:08 +0000";
})

Let me explain what's happening under the hood. Core Data gives us what we ask for and exactly that. Nothing more. We first asked the framework for the user's notes and Core Data diligently gave us the list of notes. But, as you can see in the console, it's 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 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.

<Note: 0x1c409b620> (entity: Note; id: 0xd000000000040000 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Note/p1> ; data: {
    category = "0xd000000000080002 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Category/p2>";
    contents = "Some text ...";
    createdAt = "2017-07-06 07:22:18 +0000";
    tags = "<relationship fault: 0x1c403e900 'tags'>";
    title = "My First Note";
    updatedAt = "2017-07-07 09:15:08 +0000";
})

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.

tags = "<relationship fault: 0x1c403e900 'tags'>";

And the same applies to the category property. Even though it seems as if Core Data has fetched the data for the category record of the note, it hasn't.

category = "0xd000000000080002 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Category/p2>";

This becomes clear if we print the value of the category property and run the application again.

NotesViewController.swift

private func fetchNotes() {
    coreDataManager.mainManagedObjectContext.perform {
        do {
            // Create Fetch Request
            let fetchRequest: NSFetchRequest<Note> = Note.fetchRequest()

            // Fetch Notes
            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: 0x1c009f3b0> (entity: Category; id: 0xd000000000080002 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Category/p2> ; data: <fault>)

I hope it's 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 requirements of the application.

Faulting and Relationships

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

NotesViewController.swift

private func fetchNotes() {
    coreDataManager.mainManagedObjectContext.perform {
        do {
            // Create Fetch Request
            let fetchRequest: NSFetchRequest<Note> = Note.fetchRequest()

            // Fetch Notes
            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)
        }
    }
}

Core Data hands us a set of objects, but it hasn't actually fetched the tag records itself. The data of the tag records are faults.

[<Tag: 0x1c009a5e0> (entity: Tag; id: 0xd000000000040004 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Tag/p1> ; data: <fault>), <Tag: 0x1c009a540> (entity: Tag; id: 0xd000000000080004 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Tag/p2> ; data: <fault>)]

The data is fetched the moment we access it. In this example, we ask each tag for the value of its name property.

Monday
Family

Unable to Fulfill Fault

It's important that you know about and understand Core Data faulting. But the reason for including this episode in this series is because of a problem many developers working with Core Data run into, firing a fault that cannot be fulfilled by Core Data.

When Core Data tries to fetch data from the persistent store that no longer exists, it tells you it's 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 and macOS 10.11, 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.

CoreDataManager.swift

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.

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

Deleting the Category of a Note

If we run the application again, we run into an exception. If we inspect the output in the console, we see the reason of the exception. The reason isn't surprising. Core Data is unable to fulfill a fault.

Notes[1254:806546] *** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0xd000000000080002 <x-coredata://A36D0B58-5E20-4F2C-AC20-111EC9F0D0E3/Category/p2>''

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 persistent store, the SQLite database.

Even though we 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 and macOS 10.11, Core Data gives developers the option to fail elegantly by deleting any faults that are inaccessible.