Core Data — фреймворк Apple для управления графом объектов в приложениях iOS и macOS. Он обеспечивает сохранение данных, отслеживание изменений, отмену операций и интеграцию с UI через NSFetchedResultsController. По данным документации Apple Developer (2025), Core Data не является базой данных — это уровень объектного моделирования, который по умолчанию использует SQLite в качестве постоянного хранилища для загрузки и сохранения объектов.
Главное
Core Data — это фреймворк управления объектным графом и персистентностью, входящий в состав Cocoa Touch SDK от Apple. Он предоставляет объектно-ориентированный интерфейс для работы с данными: разработчик оперирует сущностями (Entity), атрибутами и связями (Relationship), а Core Data преобразует эти объекты в записи реляционной базы данных под капотом.
Core Data был представлен в Mac OS X 10.4 Tiger (2005) для macOS и портирован в iOS 3.0 (2009). За более чем 20 лет фреймворк эволюционировал от простого уровня абстракции над SQLite до полноценного стека с поддержкой облачной синхронизации через NSPersistentCloudKitContainer, многопоточности через автоматическое управление контекстами и асинхронной загрузки через Swift Concurrency.
По данным опроса разработчиков iOS от Slack Community (2025), Core Data используется в 68% коммерческих iOS-приложений для локального хранения данных. Несмотря на критику за сложность и многослойность, фреймворк остаётся стандартом для приложений Apple благодаря тесной интеграции с системой, нулевой стоимости (встроен в SDK) и поддержке iCloud-синхронизации.
Распространённое заблуждение — считать Core Data базой данных. Фреймворк не выполняет SQL-запросы напрямую и не является СУБД. Core Data — это слой управления объектами (object graph management), который может использовать SQLite, Binary или In-Memory хранилище для персистентности. Аналогия: Core Data — это ORM вроде Hibernate или Entity Framework, но для Apple-экосистемы, а SQLite под ним — как MySQL под Hibernate.
Стек Core Data состоит из четырёх взаимосвязанных компонентов: NSManagedObjectModel (схема данных), NSPersistentStoreCoordinator (координатор хранилищ), NSManagedObjectContext (рабочий контекст) и NSPersistentContainer (единый контейнер, объединяющий все три с iOS 10). NSPersistentContainer автоматизирует создание и настройку стека.
Каждый компонент выполняет строго определённую функцию. NSManagedObjectModel загружает .xcdatamodeld-файл с описанием сущностей. NSPersistentStoreCoordinator связывает модель с физическим файлом хранилища (SQLite). NSManagedObjectContext предоставляет временную область для работы с объектами. Container объединяет всё в один вызов инициализации.
SQLite (NSSQLiteStoreType) — стандартное хранилище, используемое в большинстве приложений. Данные сохраняются в один файл .sqlite с поддержкой ACID-транзакций. Binary (NSBinaryStoreType) — хранилище в бинарном формате для небольших наборов данных (до нескольких сотен объектов). In-Memory (NSInMemoryStoreType) — временное хранилище в оперативной памяти без сохранения на диск, используется для тестов и кеша.
| Тип хранилища | Формат | Производительность | Когда использовать |
|---|---|---|---|
| SQLite | .sqlite | Высокая | Стандартный выбор для production |
| Binary | .binary | Средняя | Небольшие наборы данных |
| In-Memory | RAM | Максимальная | Тесты, кеш, временные данные |
| CloudKit | iCloud | Зависит от сети | Синхронизация между устройствами |
Выбор хранилища задаётся единственной строкой при инициализации NSPersistentStoreDescription. Разработчик может переключить хранилище с SQLite на In-Memory для unit-тестов или на CloudKit для iCloud-синхронизации без изменения кода работы с объектами — Core Data абстрагирует разницу между типами хранилищ с помощью единого API контекста.
NSManagedObject — базовый класс для всех объектов Core Data, представляющий одну запись сущности. Каждый managed object имеет уникальный NSManagedObjectID (постоянный идентификатор), привязан к контексту и отслеживает свои изменения через KVO (Key-Value Observing). Разработчик создаёт подклассы NSManagedObject для определения typed-свойств сущности.
NSManagedObjectContext — центральный компонент Core Data, предоставляющий рабочую область для всех операций с объектами. Контекст отслеживает добавления, удаления и изменения объектов (change tracking), поддерживает отмену операций через undoManager и автоматически объединяет изменения из других контекстов при получении уведомлений о сохранении.
Правило приватных очередей: NSManagedObjectContext создаётся с типом .privateQueueConcurrencyType или .mainQueueConcurrencyType. Main-контекст привязан к главному потоку UI, приватные контексты выполняются на фоновых очередях. Каждый контекст должен использоваться только на своей очереди — обращение к managed object из другого потока вызывает крэш. parentContext позволяет организовать иерархию контекстов для асинхронной записи.
struct CoreDataStack {
let container: NSPersistentContainer
init(name: String) {
container = NSPersistentContainer(name: name)
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Failed to load store: \(error)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
}
func backgroundContext() -> NSManagedObjectContext {
let context = container.newBackgroundContext()
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return context
}
}
NSPersistentContainer автоматически создаёт viewContext (main queue) и предоставляет newBackgroundContext() для фоновых операций. Свойство automaticallyMergesChangesFromParent = true заставляет viewContext автоматически подхватывать изменения из фоновых контекстов при их сохранении, обновляя UI без ручного перезапроса данных.
NSPersistentStoreCoordinator управляет физическим хранилищем данных: открывает файл, создаёт таблицы SQLite на основе модели, выполняет миграции при изменении схемы. При инициализации NSPersistentStoreDescription с типом NSSQLiteStoreType Core Data создаёт SQLite-файл со схемой, соответствующей модели .xcdatamodeld.
Core Data не использует стандартные SQL-запросы через SELECT/INSERT/UPDATE. Вместо этого он генерирует внутренние SQL-команды на основе модели и запросов через NSFetchRequest. Разработчик может включить логирование SQL через аргумент запуска -com.apple.CoreData.SQLDebug 1 для отладки производительности запросов.
Лёгкая миграция (Lightweight Migration) — автоматический процесс обновления схемы SQLite при добавлении новых атрибутов, изменении optional/required или переименовании с помощью renamingID. Тяжёлая миграция требуется при кардинальных изменениях схемы, таких как объединение или разделение сущностей, и выполняется через кастомный NSMigrationManager.
let description = NSPersistentStoreDescription()
description.url = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)
.first?
.appendingPathComponent("Model.sqlite")
description.setOption(true as NSNumber,
forKey: NSMigratePersistentStoresAutomaticallyOption)
description.setOption(true as NSNumber,
forKey: NSInferMappingModelAutomaticallyOption)
let container = NSPersistentContainer(name: "AppModel")
container.persistentStoreDescriptions = [description]
container.loadPersistentStores { _, error in
if let error = error { print("Migration error: \(error)") }
}
Настройка автоматической миграции через NSMigratePersistentStoresAutomaticallyOption и NSInferMappingModelAutomaticallyOption позволяет Core Data самостоятельно обновлять SQLite-файл при добавлении атрибутов или сущностей в новой версии модели. Если миграция невозможна, store coordinator выбрасывает ошибку с описанием причины — разработчик должен реализовать кастомную миграцию через NSMigrationManager.
NSFetchRequest — основной инструмент для выборки объектов из Core Data. Запрос содержит имя сущности, предикат (фильтр), сортировки, лимит и смещение (offset). Результат возвращается в виде массива NSManagedObject или typed-подклассов. NSPredicate поддерживает сложные условия с AND, OR, IN, LIKE и подзапросами.
NSBatchDeleteRequest — эффективный способ массового удаления объектов без загрузки каждого в память. Запрос выполняется на уровне SQLite, минуя managed object context, и только обновляет контекст после завершения. Аналогичные batch-запросы существуют для обновления (NSBatchUpdateRequest) и вставки (NSBatchInsertRequest).
CRUD (Create, Read, Update, Delete) в Core Data выполняется через методы контекста: insert, fetch, save и delete. Все изменения временные до вызова context.save() — метод сохраняет изменения в постоянное хранилище SQLite. При ошибке сохранения контекст остаётся в изменённом состоянии для повторной попытки.
let context = container.viewContext
// Create
let user = User(context: context)
user.id = 42
user.name = "Alice"
// Read
let request = User.fetchRequest()
request.predicate = NSPredicate(format: "name CONTAINS %@", "Ali")
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
let results = try context.fetch(request)
// Update
results.first?.name = "Alice Updated"
// Delete
if let first = results.first { context.delete(first) }
// Save
try context.save()
Сохранение контекста (context.save()) — критическая операция. Если сохранение не вызвано, все изменения остаются только в памяти. Контекст отслеживает состояние hasChanges, которое можно проверить перед сохранением. Для фоновых операций используйте newBackgroundContext с собственным сохранением, а для UI — viewContext с автоматическим сохранением по таймеру или при уходе приложения в фон.
Первая практика — используйте NSPersistentCloudKitContainer для синхронизации данных между устройствами пользователя через iCloud. Облачная синхронизация включается добавлением CloudKit-опции к persistent store description. Core Data автоматически управляет конфликтами при синхронизации и объединяет изменения с других устройств.
Вторая практика — избегайте fetchRequest без предиката на больших таблицах. Каждый безусловный fetch загружает все объекты сущности в память, что приводит к высокому потреблению RAM и замедлению UI. Всегда используйте предикаты и лимиты. Для пагинации применяйте fetchLimit и fetchOffset в NSFetchRequest.
Третья практика — настройте mergePolicy для разрешения конфликтов при многопоточном доступе. NSMergeByPropertyObjectTrumpMergePolicy обновляет конфликтующие свойства из последнего сохранившегося контекста. NSRollbackMergePolicy отменяет изменения текущего контекста при конфликте. Выбор политики зависит от бизнес-логики приложения.
Четвёртая практика — используйте NSFetchedResultsController для интеграции с таблицами и коллекциями. Он автоматически подписывается на уведомления NSManagedObjectContextDidSave, загружает только необходимые объекты (faulting) и уведомляет делегата о вставках, удалениях и перемещениях с соответствующими index paths для анимации UITableView.
Faulting — механизм ленивой загрузки объектов Core Data. Managed object, возвращённый fetch-запросом, находится в состоянии fault — его атрибуты загружены не полностью, а только идентификатор. Полная загрузка (fire fault) происходит при первом обращении к любому атрибуту. Relationship prefetching (setRelationshipKeyPathsForPrefetching) загружает связанные объекты заранее, избегая N+1 запросов.
extension UserRepository {
func fetchUsersWithPosts() throws -> [User] {
let request = User.fetchRequest()
request.predicate = NSPredicate(format: "isActive == YES")
request.relationshipKeyPathsForPrefetching = ["posts"]
request.returnsObjectsAsFaults = false
request.fetchBatchSize = 20
let context = container.viewContext
return try context.fetch(request)
}
}
fetchBatchSize = 20 заставляет Core Data загружать данные порциями по 20 объектов (для отображения на экране), не загружая сразу всю таблицу. Флаг returnsObjectsAsFaults = false гарантирует, что атрибуты всех пользователей загружены сразу, что полезно при прямом отображении. Prefetching relationship "posts" избегает отдельных запросов для каждого пользователя при обращении к постам.
Часто задаваемые вопросы
Нет, Core Data — это фреймворк управления объектным графом. Он предоставляет API для работы с объектами, отслеживания изменений и их сохранения. Базу данных под капотом Core Data (по умолчанию SQLite) не следует путать с самим фреймворком. Core Data — это ORM, а не СУБД.
Да, Core Data поддерживает три типа хранилищ: SQLite, Binary и In-Memory. Выбор хранилища задаётся через NSPersistentStoreDescription. In-Memory хранилище не сохраняет данные на диск и подходит для unit-тестов. Binary хранилище — устаревший формат для компактных наборов объектов.
Для лёгкой миграции включите NSMigratePersistentStoresAutomaticallyOption и NSInferMappingModelAutomaticallyOption. Для сложных изменений создайте Mapping Model (.xcmappingmodel) через Xcode. CloudKit-хранилище (NSPersistentCloudKitContainer) поддерживает миграции автоматически при синхронизации схемы с сервером iCloud.
SwiftData — новый фреймворк Apple (iOS 17+), построенный поверх Core Data с использованием Swift Macros и Swift Concurrency. SwiftData проще в синтаксисе: сущности описываются макросом @Model, контекст — @Environment(\.modelContext). Под капотом SwiftData использует тот же стек Core Data и SQLite.
Включите аргумент запуска -com.apple.CoreData.SQLDebug 1 — Core Data будет выводить все SQL-запросы и их длительность в консоль Xcode. Для профилирования используйте Instruments с шаблоном Core Data, который показывает количество fault-запросов, загрузку объектов и время сохранения контекста.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также