Как стать автором
Обновить

Стартуем с Core Data! Сложное простыми словами [Часть 2]

Время на прочтение 4 мин
Количество просмотров 8K
В этой статье я бы хотел еще немного раскрыть возможности Core Data. Данная статья является продолжением. Я очень надеюсь что у меня получится донести мысль о том, что Core Data не одна из множества реляционных баз данных, а очень мощный инструмент который сможет стать неотъемлемым оружием в арсенале любого уважающего себя IOS-разработчика.

Кто не знаком с первой частью, настоятельно рекомендую прочитать первую часть

Ну что ж, начнем!

Сегодня мы посмотрим на работу с двумя NSManageObjectContext и NSFetchedResultsController.

Два NSManageObjectContext и зачем это надо?


Если ваше приложение активно задействует какой-нибудь API через которое оно получает данные, и вы хотите эти данные где-то сохранять, то отличный выбор — Core Data. Какие у вас могут появиться проблемы с этим? Первое и самое очевидное — количество данных будет так велико, что при попытке сохранить их, наше приложение зависнет на какое-то время, что скажется впечатлении пользователя. Обычным GCD тут не обойтись так как независимо, наш Core Data Stack, о котором говорилось тут, работает синхронно и при любом обращении к нашему NSManageObjectContext нам придется ждать до конца выполнения цикла.

Но тут на помощь нам приходит приватный NSManageObjectContext который работает в background потоке. Для того чтобы его вызвать требуется сначала обратиться к нашему NSPersistentContainer.

Инициализация будет выглядеть следующим образом:

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


Тут вы указываем политику слияния наших NSManageObjectContext. Тут мы явно указываем как должна себя повести Core Data в случае конфликта данных.

Варианты MergePolicy:

  1. NSRollbackMergePolicy — В случае появления конфликта, отбрасывает все изменения до его появления
  2. NSOverwriteMergePolicy — Сохранит новые значения независимо от данных
  3. NSMergeByPropertyStoreTrumpMergePolicy — Сохраняет измененные объекты свойство за свойством, в данном случае преобладать будут сохраненные данные
  4. NSMergeByPropertyObjectTrumpMergePolicy — Сохраняет измененные объекты свойство за свойством, в данном случае преобладать будут новые данные

AutomaticallyMergesChangesFromParent — говорит о том будет ли наш контекст автоматически объединять данные
После чего создаем новый контекст:

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

Теперь у нас имеется два NSManageObjectContext. Первый служит для работы с UI и работает на главном потоке, а второй имеет privateQueueConcurrencyType для работы в фоне.

Мы будем использовать его для скачивания данных.

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

Тут мы создаем наше Entity и далее можем присвоить ему необходимые свойства, после чего вызываем метод сохранения, выглядит он следующим образом:

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

Есть 2 метода на выбор:

  • performAndWait — выполняет действия на потоке контекста синхронно
  • perform — выполняет действия на потоке контекста асинхронно


NSFetchedResultsController


NSFetchedResultsController — контроллер который выполняет определенные запросы и показывает необходимые данные пользователю.

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 имеет очень большое количество конфигураций, разберем парочку из них:

FetchLimit
FetchLimit — указывает лимит по выборке объектов

FetchOffset
FetchOffset — указывает отступ. Если указать значение 2, то показываться элементы будут со второго элемента.

FetchBatchSize
FetchBatchSize — указывает какое количество элементов будет подгружено за раз. Если вы отображаете элементы в Table/CollectionView и у вас за раз показывается всего 5 элементов, в целях производительности лучше подгружать не больше 7 элементов за раз, чем брать сразу все данные и держать их в памяти.

SortDescriptor
SortDescriptor — указывает по какому ключу следует отсортировать запрос, а так же по возрастанию либо наоборот.

ReturnsObjectsAsFaults
ReturnsObjectsAsFaults — указывает, что наши значения могут придти пустыми, и когда мы к ним обратимся, они будут подгружены с нашего PersistentStore которые в данный момент находятся там в виде RawCache

На данный момент мы имеем NSFetchedResultsController который должен отобразить наши данные в таблице. Для того чтобы обновить данные нужно вызвать метод делегата:

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

Данный метод делегата срабатывает когда у нас происходят какие-то изменения в нашем контексте. В данной реализации это происходит после того как мы сохраняем данные в privateContext. В этот момент у нас срабатывает метод делегата и данные обновляются.

Всего пару действий и Core Data из обычной базы данных превращается в мощное оружие любого IOS разработчика.

Happy Coding!
Теги:
Хабы:
+4
Комментарии 4
Комментарии Комментарии 4

Публикации

Истории

Работа

Swift разработчик
38 вакансий
iOS разработчик
23 вакансии

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн