Starting with Core Data! Difficult in simple words [Part 2]

In this article, I would like to further reveal the capabilities of Core Data. This article is a continuation . I really hope that I can get the idea that Core Data is not one of the many relational databases, but a very powerful tool that can become an integral weapon in the arsenal of any self-respecting IOS developer.

Well, let's get started!

Today we look at working with two NSManageObjectContext and NSFetchedResultsController.

Two NSManageObjectContext and why is this needed?


If your application actively uses any API through which it receives data, and you want to save this data somewhere, then Core Data is an excellent choice. What problems might you have with this? The first and most obvious - the amount of data will be so large that when you try to save them, our application will freeze for some time, which will affect the user's experience. An ordinary GCD can not be dispensed with since it is independent, our Core Data Stack, which was mentioned here , works synchronously and for any access to our NSManageObjectContext we have to wait until the end of the loop.

But here the private NSManageObjectContext that works in the background thread comes to the rescue. In order to call it, you must first contact our NSPersistentContainer.

Initialization will look like this:

lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "Preset")
    container.loadPersistentStores { (persistent, error) in
        if let error = error {
            fatalError("Error: " + error.localizedDescription)
        }
    }
    container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
    container.viewContext.shouldDeleteInaccessibleFaults = true
    container.viewContext.automaticallyMergesChangesFromParent = true
    return container
}()

Mergepolicy


Here you specify the merge policy of our NSManageObjectContext. Here we explicitly indicate how Core Data should behave in the event of a data conflict.

MergePolicy Options:

  1. NSRollbackMergePolicy - In the event of a conflict, discards all changes until it occurs
  2. NSOverwriteMergePolicy - Saves new values ​​regardless of data
  3. NSMergeByPropertyStoreTrumpMergePolicy - Saves changed objects property by property, in this case, saved data will prevail
  4. NSMergeByPropertyObjectTrumpMergePolicy - Saves changed objects property by property, in this case new data will prevail

AutomaticallyMergesChangesFromParent - says whether our context will automatically merge data.
Then create a new context:

let context = persistentContainer.viewContext
let context = persistentContainer.newBackgroundContext()

Now we have two NSManageObjectContext. The first one is for working with the UI and runs on the main thread, and the second one has privateQueueConcurrencyType for working in the background.

We will use it to download data.

let object = NSEntityDescription.insertNewObject(forEntityName: "Name", into: context)
saveChanges(with: context)

Here we create our Entity and then we can assign the necessary properties to it, after which we call the save method, it looks like this:

func saveChanges(with context: NSManagedObjectContext) {
        context.performAndWait {
            if context.hasChanges {
                do {
                    try context.save()
                } catch {
                    context.rollback()
                }
            }
            context.reset()
        }
    }

There are 2 methods to choose from:

  • performAndWait - performs actions on the context thread synchronously
  • perform - performs actions on the context stream asynchronously


NSFetchedResultsController


NSFetchedResultsController - a controller that performs certain requests and displays the necessary data to the user.

lazy var fetchedResultsController: NSFetchedResultsController<Pack> = {
        let fetchRequest = NSFetchRequest<Pack>(entityName:"Pack")
        fetchRequest.fetchBatchSize = 20
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending:true)]
        let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)

        controller.delegate = self
        do {
            try controller.performFetch()
        } catch {
            print(error.localizedDescription)
        }
        return controller
    }()

NSFetchedResultsController has a very large number of configurations, we will analyze a couple of them:

Fetchlimit
FetchLimit β€”

Sketchoffset
FetchOffset β€” . 2, .

FetchbatchSize
FetchBatchSize β€” . Table/CollectionView 5 , 7 , .

FetchbatchSize
FetchBatchSize β€” .

SortDescriptor
SortDescriptor β€” , .

ReturnsObjectsAsFaults
ReturnsObjectsAsFaults β€” , , , PersistentStore RawCache

At the moment, we have an NSFetchedResultsController which should display our data in a table. In order to update the data you need to call the delegate method:

extension ViewController: NSFetchedResultsControllerDelegate {
    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
       collectionView.reloadData()
    }
}

This delegate method works when we have any changes in our context. In this implementation, this happens after we save the data in a privateContext. At this moment, the delegate method is triggered and the data is updated.

Just a couple of actions and Core Data from a regular database turns into a powerful weapon of any iOS developer.

Happy coding!

All Articles