CoreData is Apple’s object graph and persistence framework. It provides data sources for synchronising data with view. Let’s take a look on how to use those data sources in SwiftUI views. Starting with NSFetchedResultsController what is used for list and collection views, after that observing NSManagedObject directly from SwiftUI view and lastly subscribing to managed object context notifications.
NSFetchedResultsController
NSFetchedResultsController is used for providing data in table and collection views. It supports sorting and filtering data and arranging data into sections. We can use delegate for getting change callbacks. NSFetchedResultsControllerDelegate contains several methods:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In SwiftUI we are not going to directly manipulate views. Only what we need to do is letting SwiftUI view know that data is about to change. Let’s take a look on simple app with list of items stored by CoreData. Data is represented by ColorItem where only stored value is hex string of the color (e.g. #AA22BB).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ColorItems are managed by ContentView’s view model. View model creates NSFetchedResultsController, performs fetch and provides array of fetched ColorItems to the SwiftUI’s List. In addition, view model is delegate of the NSFetchedResultsController (requires view model to be NSObject subclass). As view model is ObservableObject, we can very easily let SwiftUI view know that it should refresh. We need to do two things: firstly, implementing controllerWillChangeContent delegate method and calling send() on objectWillChange publisher. Secondly, view model property must use @ObservedObject property wrapper in SwiftUI view. Result is that SwiftUI view subscribes to objectWillChange publisher and refreshes whenever publisher emits an event.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
And finally let’s see the ContentView implementation. NSManagedObject has objectID property what we can use in List for identifying every ColorItem. Cell is custom view what we’ll take a look at next.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NSManagedObject implements ObservableObject protocol and therefore it is possible to use it together with @ObservedObject property wrapper and getting SwiftUI view refreshed automatically when any of the ColorItem properties change. NSFetchedResultsController required a little bit of code for setting up delegate but that is not the case with NSManagedObject.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In addition, if there are cases where we would like to observe specific property, then Combine provides publisher for key path. Because NSManagedObject supports key-value observing we can use the publisher and subscribe to individual property changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
When we need to observe changes in persistent store we can observe notifications sent by the framework. As NotificationCenter supports publishers, we can subscribe to it, unpack data from notification and do something with the data. For making this easier we can introduce a separate type. It will unpack the user info dictionary and filter by type. This allows to easily observe, for example, ColorItem insertions. Or, if we would like to receive every possible change, we can specify NSManagedObject as the generic type.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
We took a look at how to use NSFetchedResultsController and NSManagedObject in SwiftUI views. We saw that integrating NSFetchedResultsController requires only a little bit of code and using NSManagedObject even less. In addition, we looked at subscribing to CoreData notifications and unpacking notification payload.
CoreData is a framework for managing object graphs and storing them on disk. It is much more than just offering a persistent storage, therefore the rich API it provides, is meant to be used app-wide. In this blog post we’ll look into how to initialise CoreData storage, store some data in it and fetching it.
Core data model
First step is to add a Core Data model which will describe model objects, their relationships and properties. In Xcode we’ll go to File -> New -> File… and then select Data Model.
We are going to continue working on sample app named Planets (from previous post). It needs an entity named Planet which has 3 properties: name, url and position (defines sort order in the app). By default Core Data will autogenerate a model object for our entity Planet we just defined. Therefore all we need to do is creating an entity and adding three properties to the entity.
Initialising CoreData stack
The simplest way for initialising Core Data stack is to use NSPersistentContainer and initialising it with a name of the Core Data model file we just created in the previous step. After creating the container, it needs to be loaded which will read the Core Data model file and sets up a persistent store.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In the sample app we are going to use subclass of NSPersistentContainer named CoreDataStore which contains extra methods used in a moment.
Storing data
Adding data contains of two steps: making changes in NSManagedObjectContext and then saving it. NSManagedObjectContext is a scratch pad where to make changes in your object graph. All the changes will be stored in memory until save is called.
For adding a new entity we will use a method in our CoreDataStore.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This will add a new empty entity to managed object context. After that, we’ll fill properties with appropriate values and then call save.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In our sample app, there is a simple table view displaying a list of planets. For displaying data efficiently, Core Data has a class NSFetchedResultsController. It uses NSFetchRequest objects for fetching, sorting and filtering results. Our class CoreDataStore has a convenience method for creating fetched results controller for any type of entities and PlanetManager has a getter for returning controller for Planet entities.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Let’s see how to hook up fetched results controller to table view controller. Before fetched controller is used for fetching data, it needs to perform a fetch. For a simple list view it is pretty straight-forward: getting count and fetching an object for index path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In this blog post we looked into how to initialise Core Data stack with NSPersistentContainer, storing some data in it with NSManagedObjectContext and fetching results with NSFetchedResultsController. It is the most basic usage of CoreData and covers a tiny bit what Core Data can do. Apple has a pretty good documentation what covers much more compared to what was described here (see links to Core Data classes in the previous sections).