Integrate Combine Into an App

Aug 5 2021 · Swift 5.4, macOS 11.3, Xcode 12.5

Part 2: Integrate with Core Data & Unit Test

07. Create the Core Data Stack

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 06. Extend, Save & Delete Core Data Objects Next episode: 08. Use @FetchRequest to Get Core Data Entries

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 07. Create the Core Data Stack

Before we actually try to fetch data from our Core Data database, we need to somehow get a reference to the Core Data stack - specifically the NSManagedObjectContext. This will allow us - as we saw in the last episode - to save and delete content from the database. Note that this isn’t using Combine per se, but instead allows us to use Combine in the next episode.

As a quick reminder, a context in this case is like a scratch space for our managed objects. Nothing happens to the underlying database until we call save() on the context. Contexts can make objects, fetch them from the database, and deal with other features of the managed object lifecycle such as faulting and validation.

In our case, we’re going to create our context in such a fashion that only the main App class has access to it. This will be done by using a private enum which effectively makes a namespace for us - we won’t ever use the enum elsewhere in the code.

Once we have that CoreDataStack, we can get the viewContext and send it to the SwiftUI Environment so that our views can use it where needed. This saves us from having to pass it around through initializers. Let’s take a look at how to set up the Core Data stack in the code.

At the top of the ChuckNorrisJokesApp.swift file in the App folder, add an import for CoreData.

import CoreData

Then define the Core Data Stack at the bottom of the file. Remember that using an enum here only defines a namespace; you don’t want to instantiate it.


private enum CoreDataStack {
  
  static var viewContext: NSManagedObjectContext = {
    let container = NSPersistentContainer(name: "ChuckNorrisJokes")

    container.loadPersistentStores { _, error in
      guard error == nil else {
        fatalError("\(#file), \(#function), \(error!.localizedDescription)")
      }
    }

    return container.viewContext
  }()

  
  static func save() {
    guard viewContext.hasChanges else { return }

    do {
      try viewContext.save()
    } catch {
      fatalError("\(#file), \(#function), \(error.localizedDescription)")
    }
  }
}

Let’s look at this code. First, the enum has a static property called viewContext - this is how we’ll initially get a hold of the generated viewContext so we can pass it to the SwiftUI environment.

There is also a static method called save() that will check to see if the viewContext has changes, and if so, will ask the viewContext to save()

Before we move on, we need to make sure the viewContext is in the SwiftUI environment, we can do by injecting it in there with the environment modifier on the JokeView

 JokeView()
        .environment(\.managedObjectContext, CoreDataStack.viewContext)

We also want to make sure that when the app goes to the background, the context gets saved. We can do this by way of the onChange modifier and look to see when the scenePhase changes (which we can get from the Environment).

First, add a sceneChange property at the top of the struct:

@Environment(\.scenePhase) var scenePhase

Now add the onChange modifier to monitor for the state change:

.onChange(of: scenePhase, perform: { newPhase in
      
      if newPhase == .background {
        CoreDataStack.save()
      }
      
      
    })

OK, now with access to the CoreDataStack, let’s look at saving, deleting and fetching from Core Data, which we’ll do in the next episode.