Настройка Core Data в iOS-приложении
Core Data — не просто обёртка над SQLite. Это граф объектов с ленивой загрузкой, кэшированием, отслеживанием изменений и возможностью синхронизации через CloudKit. При правильной настройке он ускоряет работу с локальными данными. При неправильной — вызывает дедлоки и crashes на NSFetchedResultsController.
Как настраиваем стек
Начиная с iOS 10 рекомендуемый способ — NSPersistentContainer. Он инкапсулирует NSManagedObjectModel, NSPersistentStoreCoordinator и основной NSManagedObjectContext.
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "DataModel")
container.loadPersistentStores { _, error in
if let error { fatalError("Core Data store failed: \(error)") }
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return container
}()
automaticallyMergesChangesFromParent = true — критично. Без этого изменения, сохранённые в background context, не попадают автоматически в viewContext, и NSFetchedResultsController не обновляет UI.
Многопоточность — главная ловушка
NSManagedObject не thread-safe. Передавать объект между потоками нельзя — только objectID через NSManagedObjectID. В background context получаем копию объекта:
let backgroundContext = persistentContainer.newBackgroundContext()
backgroundContext.perform {
let objectInBg = backgroundContext.object(with: objectID)
// изменяем objectInBg
try? backgroundContext.save()
}
Самый частый краш: EXC_BAD_ACCESS или NSInternalInconsistencyException при доступе к NSManagedObject не в его потоке. Instruments → Core Data template показывает, где это происходит.
performAndWait vs perform. perform — асинхронный, performAndWait — синхронный и может вызвать дедлок, если вызвать из main thread с ожиданием background context, который в свою очередь ждёт main. Используем perform для фонового сохранения.
NSFetchedResultsController и diffable data source
NSFetchedResultsController отслеживает изменения в Core Data и сообщает делегату. Связка с UICollectionViewDiffableDataSource работает через controllerDidChangeContent:
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
var snapshot = NSDiffableDataSourceSnapshot<Section, NSManagedObjectID>()
snapshot.appendSections([.main])
snapshot.appendItems(controller.fetchedObjects?.map(\.objectID) ?? [])
dataSource.apply(snapshot, animatingDifferences: true)
}
Используем objectID в snapshot, не сам NSManagedObject — иначе diffable source не может корректно сравнивать объекты.
Миграции
При изменении модели данных нужна миграция. Лёгкая миграция (NSInferMappingModelAutomatically) работает для добавления/удаления атрибутов. Для переименований, изменения типов — кастомная migration policy через NSEntityMigrationPolicy. Без правильной миграции loadPersistentStores вернёт ошибку NSMigrationError, и приложение не запустится.
В конфигурации добавляем:
container.persistentStoreDescriptions.first?.shouldMigrateStoreAutomatically = true
container.persistentStoreDescriptions.first?.shouldInferMappingModelAutomatically = true
CloudKit синхронизация
NSPersistentCloudKitContainer вместо NSPersistentContainer включает синхронизацию через iCloud CloudKit. Требует: iCloud Entitlement, CloudKit capability в Xcode, модель без некоторых типов атрибутов (Binary Data с External Storage не синхронизируется автоматически).
Конфликты при синхронизации разрешаются через mergePolicy — NSMergeByPropertyObjectTrumpMergePolicy обычно правильный выбор.
Что входит в работу
- Создание
.xcdatamodeldс entity и relationships - Настройка
NSPersistentContainerс правильными параметрами контекста - Background context для импорта и записи данных
-
NSFetchedResultsControllerдля отображения данных в UI - Стратегия миграции для будущих изменений модели
- Опционально: CloudKit синхронизация
Сроки
Базовый стек с одной-двумя сущностями и NSFetchedResultsController: 1 день. Со сложной моделью, миграциями, background sync и CloudKit интеграцией: 2–3 дня. Стоимость рассчитывается после анализа требований к данным.







