Core Data is Apple's widely used persistence framework. It has been around for more than a decade and has earned its stripes. Getting stared with Core Data isn't as difficult as many developers make you believe as long as you take the time to learn the basics. In this episode, I show you how to add Core Data to an existing Swift project.

Starter Project

Download the starter project of this episode if you want to follow along. The project is named Books and allows the user to manage a collection of books. When you launch the application, you see a label that displays the number of books you have stored in the application.

How to Add Core Data to an Existing Swift Project

Tapping the + button in the top right takes the user to a form with two text fields. The user can enter a title and an author. To add the book to their collection of books, the user taps the Add Book button. It doesn't get any simpler than this.

How to Add Core Data to an Existing Swift Project

There is one problem. The data the user enters isn't stored in any way. That is where Core Data comes into play. Let me show you how to integrate Core Data into an existing Swift project, making sure the user is able to create a collection of books that persists.

Adding a Data Model

The first task we need to complete is adding a data model. The data model is an integral component of every Core Data application. As the name suggests, it describes the data your application stores and works with. I explain this in detail in Core Data Fundamentals.

Create a new group in the Project Navigator on the left and name the group Core Data. Add a file to the Core Data group and choose the Data Model template from the iOS > Core Data section. Name the data model Books.xcdatamodeld and click Create.

Adding a Data Model

Add an Entity

Open Books.xcdatamodeld in the Core Data group. We need to add an entity to the data model. Core Data isn't a database, but you can think of an entity as a table in a database. Entities have a name and properties. A property is either an attribute or a relationship.

Let me show you how this works. Click the Add Entity button at the bottom to add an entity to the data model. The data model editor is split into a navigator on the left and a detail view on the right. In the Entities section of the navigator, you should see an entity named Entity.

Select the entity, open the Utilities pane on the right, and select the Data Model Inspector. The inspector shows the details of the entity. Set the name of the entity to Book.

Adding an Entity to the Data Model

We need to add two attributes to the entity. If you think of an entity as a table in a database, then you can think of an attribute as a column of a table. Select the Book entity and click the + button at the bottom of the Attributes table to add the first attribute to the Book entity.

We need to give the attribute a name and define its type. Select the attribute and open the Data Model Inspector on the right. Set Name to title and Type to String.

Adding an Attribute to an Entity

We repeat these steps for the second attribute. Click the + button at the bottom of the Attributes table to add the second attribute to the Book entity. Select the attribute and open the Data Model Inspector on the right. Set Name to author and Type to String.

Adding an Attribute to an Entity

Creating the Persistent Container

To work with the data model, we need to create a persistent container. A persistent container creates and manages the Core Data stack. I explain this in detail in Core Data Fundamentals. You can manually set up the Core Data stack, which I always do, but that is a bit more advanced. That is also covered in Core Data Fundamentals.

Open BooksViewController.swift and add an import statement for the Core Data framework at the top.

import UIKit
import CoreData

final class BooksViewController: UIViewController {

    ...

}

Define a private, variable, lazy property, persistentContainer, of type NSPersistentContainer. We use a self-executing closure to lazily instantiate the persistent container. The initializer accepts a single argument of type String. That is the name of the data model.

import UIKit
import CoreData

final class BooksViewController: UIViewController {

    // MARK: - Properties

    @IBOutlet private var booksLabel: UILabel!

    // MARK: -

    private lazy var persistentContainer: NSPersistentContainer = {
        NSPersistentContainer(name: "Books")
    }()

}

Before we can use the persistent container, we need to add a persistent store to it. As the name suggests, a persistent store stores the data. This doesn't mean the data is persisted to disk, though. Core Data supports a number of persistent store types, including an in-memory store and a SQLite database.

In viewDidLoad(), we call loadPersistentStores(completionHandler:) on the persistent container. The loadPersistentStores(completionHandler:) method accepts a completion handler, a closure, as its only argument. Loading the persistent store(s) is an asynchronous operation because it can take some time to complete. The closure is executed when the persistent container has finished loading the persistent store(s).

The closure accepts two arguments, an NSPersistentStoreDescription instance and an optional Error object. We print the error to the console to debug any problems that may pop up. The persistent store description contains information about the persistent store, such as its type and location. Let's print those details to the console to make sure everything is set up correctly.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    persistentContainer.loadPersistentStores { [weak self] persistentStoreDescription, error in
        if let error = error {
            print("Unable to Add Persistent Store")
            print("\(error), \(error.localizedDescription)")

        } else {
            print(persistentStoreDescription.url, persistentStoreDescription.type)
        }
    }
}

Build and run the application to see the result. This is what the output should look like. The location of the persistent store may be different.

Optional(file:///Users/Bart/Library/Developer/CoreSimulator/Devices/54FE7403-6F0A-4736-B0AE-3626C340F753/data/Containers/Data/Application/68208972-F5C9-49ED-82C1-571CEC5E8A38/Library/Application%20Support/Books.sqlite) SQLite

The output shows us that the persistent store is a SQLite database. That is the default persistent store type. Let's replace the print statement with a helper method, fetchBooks(). This helper method is responsible for fetching the collection of books the persistent store stores.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    persistentContainer.loadPersistentStores { [weak self] persistentStoreDescription, error in
        if let error = error {
            print("Unable to Add Persistent Store")
            print("\(error), \(error.localizedDescription)")

        } else {
            self?.fetchBooks()
        }
    }
}

In fetchBooks(), we print the value of the viewContext property of the persistent container. The viewContext property is of type NSManagedObjectContext. A Core Data application has one or more managed object contexts. Each managed object context manages a collection of model objects, instances of the NSManagedObject class.

// MARK: - Helper Methods

private func fetchBooks() {
    print(persistentContainer.viewContext)
}

The managed object context is the object you interact with most. It creates, reads, updates, and deletes model objects. From a developer's perspective, the NSManagedObjectContext class is the workhorse of the Core Data framework.

Build and run the application one more time and inspect the output in the console. The output should look something like this.

<NSManagedObjectContext: 0x600001f140d0>

What's Next?

We have set up a Core Data stack with the help of the NSPersistentContainer class and we are ready to use the framework in the project. In the next episode of this series, we add the ability to store books in the persistent store.