# Working with Core Data Reference

# Working with Core Data

This guide provides details on how to work with Core Data and `IGListKit`.

## Background

The main difference in the setup and architecture of a Core Data and `IGListKit` application is the configuration of the model layer. Core Data operates with a mutable model layer, where objects are always passed by reference and the same instance is modified when an object is edited.

`IGListKit` requires an immutable model in order to correctly calculate the diffing between model snapshots and to correctly animate the `UICollectionView`.

In order to satisfy these prerequisites, Core Data `NSManagedObject`s should not be used directly as `ListDiffable` objects. Instead, a view model (or some sort of token object) should be used to mimic (or act as a placeholder for) the data that will be displayed in the collection view.

## Further discussion

There are further discussions on this topic at #460, #461, #407.

## Basic Setup

The basic setup for Core Data and `IGListKit` is the same as the normal setup that is found in the Getting Started Guide. The main difference will be in the setup of the model used in the `IGListAdapterDataSource`.

## Working with view model

### Creating a view model

Suppose the Core Data model consist of:

    extension User {
        @NSManaged var firstName: String
        @NSManaged var lastName: String
        @NSManaged var address: String
        @NSManaged var someVariableNotNeededInUI: String
    }

A `ViewModel` object will contain only the necessary information needed to build UI. The properties of the `ViewModel` will be immutable:

    class UserViewModel: NSObject {
        let firstName: String
        let lastName: String
        let address: String
    }

We recommend writing a helper method to translate Core Data objects into `ViewModel` objects:

    extension UserViewModel {
        static func fromCoreData(user: User) -> UserViewModel {
            // - Note: For avoiding Core Data threading violation, the following code should be wrapped in a
            // user.managedObjectContext?.performAndWait {}
            return UserViewModel(firstName: user.firstName, lastName: user.lastName, address: user.lastName)
        }
    }

The `IGListDiffable` protocol is implemented on the `ViewModel` layer:

    extension UserViewModel: ListDiffable {

        public func diffIdentifier() -> NSObjectProtocol {
            return NSString(string: firstName + lastName)
        }

        public func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
            guard let toObject = object as? UserViewModel else { return false }

            return self.firstName == toObject.firstName
                && self.lastName == toObject.lastName
                && self.address == toObject.address
        }
    }

## Setting up the view model in the adapter data source

Steps to configure the `UICollectionView` with the `ViewModel`:

- Retrieve Core Data objects
- Transform Core Data objects into ViewModel objects and return them
- Track changes to Core Data objects and update the datasource with them

### Retrieve Core Data objects

The way objects are retrieved from Core Data is depends on the project.

Example: Suppose there is a delegate `Provider` class with the role of fetching Core Data objects and checking for updates. It can use an `NSFetchedResultsController` to leverage on the Core Data framework and rely on automatic notifications for updates.

    final class UserProvider: NSObject {

        private lazy var userFetchResultController: NSFetchedResultsController<User> = {
            let fetchRequest: NSFetchRequest<User> = NSFetchRequest(entityName: "User")

            // sort descriptors and predicates
            // ...

            let fetchResultController = NSFetchedResultsController(
               fetchRequest: tripsFetchRequest,
               managedObjectContext: self.coreDataStack.mainQueueManagedObjectContext,
               sectionNameKeyPath: nil,
               cacheName: nil)

            // Set delegate to track CoreData changes
            fetchResultController.delegate = self

            return fetchResultController
        }()

        init(coreDataStack: CoreDataStack) {
            self.coreDataStack = coreDataStack
            super.init()
            do {
                try userFetchResultController.performFetch()
            }
            catch {
                fatalError("Cannot Fetch! \(error)")
            }
        }
    }

### Transform Core Data objects into view models

    func getUsers() -> [UserViewModel]? {
        guard let users = self.userFetchResultController.fetchedObjects else { return nil }
        // Here we transform and return ViewModel objects!
        return users.flatMap { UserViewModel.fromCoreData(user: $0) }
    }

### Track changes to Core Data

The `Provider` will track changes to the Core Data model by listening to the `NSFetchedResultsController` methods and inform the application about this changes via KVO, notifications, delegation, etc.

    extension UserProvider: NSFetchedResultsControllerDelegate {
        func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
            self.delegate?.performUpdatesForCoreDataChange(animated: true)
        }
    }

### Configure the datasource

The data source retrieves ViewModels and configures the `IGListSectionController` with them:

    func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
        return self.userProvider.getUsers()
    }

### Reacting to Core Data changes in UI

The `UIViewController` containing the `UICollectionView`, will react to the `NSFetchedResultController` messages by updating the UI:

    func performUpdatesForCoreDataChange(animated: Bool) {
        // Updating contents of collection view
        self.adapter.performUpdates(animated: animated)
    }

© 2024 Instagram. All rights reserved. (Last updated: 2024-05-13)

Generated by jazzy ♪♫ v0.15.0, a Realm project.