In the previous episode of Adding Core Data to an Existing Swift Project, we added a data model to the project and set up the Core Data stack, using the NSPersistentContainer class. In this episode, you learn how to save data to a persistent store.

Injecting the Managed Object Context

The AddBookViewController class is responsible for converting the form data to a book and adding the book to the persistent store. For that to work, the AddBookViewController class needs access to a managed object context, an instance of the NSManagedObjectContext class. We cover this class in detail in Core Data Fundamentals because it is a key component of the Core Data framework.

Open AddBookViewController.swift, add an import statement for the Core Data framework, and define a variable property, managedObjectContext, of type NSManagedObjectContext?.

import UIKit
import CoreData

final class AddBookViewController: UIViewController {

    // MARK: - Properties

    @IBOutlet private var titleTextField: UITextField!
    @IBOutlet private var authorTextField: UITextField!

    // MARK: -

    var managedObjectContext: NSManagedObjectContext?

    // MARK: - Actions

    @IBAction func addBook(_ sender: Any) {

    }

}

Open BooksViewController.swift and override the prepare(for:sender:) method. This method is invoked before a segue is executed. We cast the destination view controller of the segue to an instance of AddBookViewController and pass the managed object context of the persistent container to the view controller.

// MARK: - Navigation

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let viewController = segue.destination as? AddBookViewController {
        viewController.managedObjectContext = persistentContainer.viewContext
    }
}

Property injection is nothing more than passing a dependency to an object by setting a property. You can learn more about dependency injection in Nuts and Bolts of Dependency Injection in Swift.

Creating a Managed Object

With the managed object context injected, the AddBookViewController class can create a NSManagedObject instance and save it to the persistent store. Open AddBookViewController.swift and navigate to the addBook(_:) method. The view controller first safely unwraps the managed object context. We need the managed object context to create a managed object, an instance of the NSManagedObject class. A managed object is always tied to a managed object context.

// MARK: - Actions

@IBAction func addBook(_ sender: Any) {
    guard let managedObjectContext = managedObjectContext else {
        fatalError("No Managed Object Context Available")
    }
}

The next step is creating a Book instance. Because we defined the Book entity, in the data model, Xcode generated a Book class for us. The initializer of the Book class accepts a NSManagedObjectContext instance as its only argument.

// MARK: - Actions

@IBAction func addBook(_ sender: Any) {
    guard let managedObjectContext = managedObjectContext else {
        fatalError("No Managed Object Context Available")
    }

    // Create Book
    let book = Book(context: managedObjectContext)
}

We populate the title and author properties of the Book instance by reading the text property of titleTextField and authorTextField.

// MARK: - Actions

@IBAction func addBook(_ sender: Any) {
    guard let managedObjectContext = managedObjectContext else {
        fatalError("No Managed Object Context Available")
    }

    // Create Book
    let book = Book(context: managedObjectContext)

    // Populate Book
    book.title = titleTextField.text
    book.author = authorTextField.text
}

To save the book to the persistent store, we invoke save() on the managed object context. The save() method is a throwing method so we prefix the invocation with the try keyword and wrap it in a do-catch statement. We dismiss the view controller if the save operation is successful.

// MARK: - Actions

@IBAction func addBook(_ sender: Any) {
    guard let managedObjectContext = managedObjectContext else {
        fatalError("No Managed Object Context Available")
    }

    // Create Book
    let book = Book(context: managedObjectContext)

    // Populate Book
    book.title = titleTextField.text
    book.author = authorTextField.text

    do {
        // Save Book to Persistent Store
        try managedObjectContext.save()

        // Dismiss View Controller
        dismiss(animated: true)
    } catch {
        print("Unable to Save Book, \(error)")
    }
}

That's it. It is time to save your first book. Build and run the application, tap the + button in the top right, and fill out the form. Tap the Add Book button to save the book to the persistent store.

Saving Data to a Persistent Store

Inspecting the Persistent Store

How do we know if the book was successfully saved to the persistent store? We can inspect the persistent store, the SQLite database. Let's print the location of the persistent store to the console. I showed you in the previous episode how to do that. Open BooksViewController.swift and add a print statement to the completion handler of loadPersistentStores(completionHandler:).

// 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)

            self?.fetchBooks()
        }
    }
}

Build and run the application and inspect the output in the console. If you run the application in the simulator, then you can find the persistent store somewhere in the /Library/Developer directory.

Browsing the Persistent Store

The SQLite database is named Books.sqlite. I use Base to inspect the contents of the SQLite database. The data of the ZBOOK table confirms that we successfully saved the book to the persistent store.

Browsing the Persistent Store

What's Next?

In the next episode, we display the number of books in the books view controller by performing a fetch request.