Здравствуйте! Эта статья нацелена на разработчиков, у которых есть минимальный навык работы с Core Data Framework. Напомню, что Core Data — это фреймворк для хранения данных на устройстве и взаимодействия с ними. На эту тему есть куча русскоязычных статей на хабре и в сети, поэтому не вижу необходимости повторять их содержание. Зачастую начинающие особенно Stack Overflow разработчики пугаются использовать Core Data Framework, потому что он кажется им сложным, или используют лишь малую часть его возможностей. В реальности знание базовых функций классов данного фреймворка позволяет разработчику с удобством работать с моделью. В статье я хочу акцентировать внимание на следующих моментах: Демо-проект, на котором мы будем «ставить опыты», весьма примитивен. Он включает Model и ViewController, в котором находится UITableView.
Для конкретики будем использовать банальный список продуктов с наименованием и ценой. Модель будет содержать в себе две сущности: Products с атрибутами name и price и FavoriteProducts, наследующую эти атрибуты.  Для примера заполним нашу базу данных некоторым количеством продуктов с рандомными ценой (до 1000) и именем продукта из списка: ”Молоко”, “Квас”, “Булочка”, “Банан”, “Колбаса «Молочная»”, “Колбаса «Краковская»”, “Рис”, “Греча”. В контроллере с помощью кода инициализируем и размещаем таблицу во весь экран. Извлечение данных из модели осуществляется методом NSManagedObjectContext executeFetchRequest(_:). Аргументом метода является запрос выборки NSFetchRequest — главный герой этой статьи. Возвращаемым типом метода executeFetchRequest(_:) является массив объектов класса NSManagedObject по умолчанию. Для наглядности распечатаем извлечённые из нашей модели элементы, преобразовав вывод. Как я уже сказал выше, класс NSFetchRequest используется в качестве запроса выборки данных из модели. Этот инструмент позволяет задавать правила фильтрации и сортировки объектов на этапе извлечения их из базы данных. Данная операция становится во много раз эффективнее и производительнее, чем если бы мы сначала делали извлечение всех объектов (а если их 10000 и больше?), а потом вручную сортировали или производили фильтрацию интересующих нас данных. Зная основные свойства этого класса, можно с лёгкостью оперировать запросами и получать конкретную выборку без разработки дополнительных алгоритмов и костылей — всё уже реализовано в Core Data. Приступим. Начать обзор свойств класса хочется с sortDesctriptors, который представляет собой массив объектов класса NSSortDescriptor. Именно с помощью них осуществляется механизм сортировки. С инструкциями по использованию дескрипторов сортировки можно познакомиться на портале Apple. Данное свойство принимает массив дескрипторов сортировки, что позволяет нам использовать несколько правил сортировки. Приоритеты при таком использовании равносильны правилам очереди (FIFO, First In-First Out): чем меньше индекс, по которому находится объект в массиве, тем выше приоритет сортировки. Вывод всех объектов, которые мы рассмотрели ранее, хаотичный и не особо читаемый, Для удобства отсортируем этот список сначала по названию продукта, указав ключом сортировки имя атрибута name, а потом отсортируем по цене (price). Имена хотим отсортировать по алфавиту, а цену по возрастанию. Для этого значение ascending обоих предикатов устанавливаем как булево true (булево false используется для сортировки по убыванию). Следующее рассматриваемое свойство — predicate класса NSPredicate, которое является мощным и быстрым инструментом для фильтрации данных. По использованию предиката существует отличный гайд от Apple (Перевод). Фильтрация данных при запросе осуществляется благодаря особому строковому синтаксису предиката, который описан в вышеупомянутом гайде. Начнём с простого примера: мы — страстные любители молочной колбасы и хотим узнать цены на неё, представленные в списке продуктов. Для этого в предикате укажем, что мы хотим получить объекты, у которых имя атрибута name равно строке "Колбаса «Молочная»". Заметьте, что для правильного составления предиката с использованием оператора равенства == необходимо точно указывать строковое значение с учётом регистра. А если мы хотим посмотреть цены не только молочной, а всех видов колбас? Для этого обратимся к оператору CONTAINS (левое выражение СОДЕРЖИТ правое) и добавим ключевые слова , которые указывают на нечувствительность к регистру и диакритическим символам. Также мы можем использовать несколько условий, в чём нам поможет оператор AND. Ограничим результаты по стоимости — до 500 денежных единиц. Свойство fetchLimit позволяет ограничивать количество извлекаемых объектов. Для демонстрации получим 12 самых дешёвых товаров из списка продуктов. Для этого добавим сортировку по цене и ограничение на количество извлекаемых объектов — 12. С помощью данного свойства можно сместить результаты выборки на заданное количество объектов. Чтобы показать работу данного свойства, воспользуемся предыдущим запросом и добавим к нему смещение на два объекта. В результате мы получим 12 объектов, где первые два пропущены, а в конец добавлены следующие, как будто мы сместили таблицу с результатами на две ячейки. Для наглядности я ограничил извлечённые объекты пунктиром. С помощью fetchBatchSize регулируется, сколько объектов за раз будет извлечено из базы данных (Persistent Store), с которой работает Core Data Framework (SQLite, XML и др.). Правильно установленное значение для конкретных кейсов может как ускорить работу с базой, так и, наоборот, замедлить. Допустим, мы работаем с UITableView. В нашей модели более 10000 объектов. Потребуется некоторое время, чтобы извлечь все эти элементы за раз. Но таблица у нас вмещает 20 ячеек на экран, и для отображения нам потребуются только 20 объектов. В таком кейсе целесообразно использовать fetchBatchSize равное 20. Сначала Core Data запросит из базы данных 20 объектов, которые мы отобразим в таблице, а при скролле будет запрошена следующая пачка из 20 элементов. Такой подход значительно оптимизирует взаимодействие с постоянным хранилищем. Но не стоит использовать слишком маленький размер пачки, например, равное 1 — это только нагрузит базу постоянными запросами по одному элементу. При извлечении данных, метод executeFetchRequest(_:) по умолчанию возвращает массив объектов класса NSManagedObject и его наследников. Изменение свойства resultType позволяет выбрать тип извлечённых объектов. Рассмотрим их (Objective-C с префиксом NS чередуется со Swift через слэш): Данное свойство позволяет извлекать из сущности только необходимые атрибуты. Но обязательное условием является, что resultType должен быть словарём (NSDictionaryResultType / DictionaryResultType). В качестве примера извлечём только значения атрибута name и для вывода распечатаем все значения по всем существующим ключам словаря в формате (key: value). Для демонстрации этого свойства необходимо добавить объект в сущность FavoriteProducts, которая является наследником Products. Присвоим этому объекту имя "ЯБЛОКО" и цену 999. Обратимся к запросу сущности Products, с помощью которого мы выполняли сортировку по имени и цене. В конце списка заметим объект, который мы только что добавили для сущности FavoriteProducts. Что же он тут делает? Дело в том, что у запроса значение свойства includesSubentities по умолчанию равно булевому true, что означает извлечение объектов не только текущей сущности, но и сущностей-наследников. Чтобы избежать этого, изменим его на булево false. Контроллер класса NSFetchedResultsController условно можно расположить между Core Data и ViewController, в котором нам нужно отобразить данные из базы. Методы и свойства этого контроллера позволяют с удобством взаимодействовать, представлять и управлять объектами из Core Data в связке с таблицами UITableView, для которых он наиболее адаптирован. Этот контроллер умеет преобразовывать извлечённые объекты в элементы таблицы — секции и объекты этих секций. FRC имеет протокол NSFetchedResultsControllerDelegate, который при делегировании позволяет отлавливать изменения происходящих с объектами заданного запроса NSFetchRequest в момент инициализации контроллера. Разберём параметры инициализации: Следующим шагом мы должны вызвать метод контроллера performFetch для того, чтобы сделать извлечение выборки из базы данных. Метод возвращает булево значение. Если извлечение произведено успешно, то вернётся булево true, а в противном случае — false. После извлечения объекты находятся в свойстве контроллера fetchedObjects. Рассмотрим работу с таблицей. Несмотря на то, что извлеченные объекты находятся в свойстве fetchedObject, для работы с ними следует обращаться к свойству контроллера sections. Это массив объектов, которые подписаны на протокол NSFetchedResultsSectionInfo, в котором описаны следующие свойства: Чтобы нам было удобно, добавим метод для конфигурации ячейки таблицы configureCell. Используя запрос NSFetchRequest с сортировкой по названию и цене и указав sectionNameKeyPath для FRC как имя атрибута "name", получим таблицу с группировкой наших продуктов по названию.  FRC может работать в нескольких режимах работы: NSFetchedResultsControllerDelegate предоставляет механизмы, с помощью которых можно отлавливать изменения, происходящие с объектами нашего NSFetchRequest запроса в модели. На примере с UITableView рассмотрим, как без ущерба для UI-представления отобразить изменения, произошедшие в модели. sectionIndexTitleForSectionName — по умолчанию названия секций получают своё значение из значения атрибута, по которому происходит группировка объектов. Имплементируя этот метод, мы можем изменить название, преобразовав значение по умолчанию (аргумент sectionName) или написав любое другое. Возвращаемая строка является новым значением. Мы просто вернём значение по умолчанию. controllerWillChangeContent — метод оповещает делегат о начале изменения объектов по запросу, с которым работает наш контроллер. Вызовем в нём метод UITableView таблицы — beginUpdates. didChangeSection — метод отлавливается делегатом, когда происходит обновление данных в модели, повлиявших на изменения в секциях. Метод принимает аргументы: sectionInfo — описывающий секцию, с которой происходит изменение, sectionIndex — индекс этой секции и type NSFetchedResultsChangeType, который описывает тип изменения (Insert, Delete, Move, Update). В этом методе опишем добавление и удаление секции с использованием анимации. didChangeObject — данный метод работает по аналогии с предыдущим, только вместо аргумента, описывающего секцию sectionInfo, приходит аргумент anObject, который является модифицируемым объектом, а также вместо sectionIndex — старый индекс, который был у объекта до изменений indexPath и новый newIndexPath, который он получил после изменений. Используя методы UITableView, обработаем добавление, удаление, перемещение, и обновление объектов с использованием анимаций. Для демонстрации работы добавим два новых объекта — "Банан" и "Апельсин" цена которых 1.  Поговорим о кэшировании. Контроллер умеет кэшировать объекты в целях избежания повторения одних и тех же задач по запросу данных. Кэширование целесообразно использовать для неизменяемых в процессе работы приложения запросов. При необходимости изменить запрос мы вызываем метод (deleteCacheWithName:), чтобы избежать ошибок при использовании одного и того же кэша для разных запросов. Запросы кэшируются в файл Core Data с именем, назначаемым в cacheName при инициализации контроллера. Как же работает кэш? Описанные в этой статье свойства класса NSFetchRequest и примеры их использования показывают, что представленный фреймворком Core Data функционал запросов весьма гибок, и с помощью него можно эффективно извлекать данные из базы данных вашего приложения. Инструмент Fetched Results Controller позволит отслеживать изменения, произошедшие с объектами модели, а также удобно преобразует извлеченные данные в элементы UITableView. В комментариях приветствуются ваши вопросы, замечания или примеры из личного опыта, дополняющие статью. iOS Developer