У нас есть приложение iOS, которое использует Core Data для сохранения записей, извлеченных из частного веб-API. Один из наших запросов API выбирает список Project
записей, каждая из которых имеет несколько связанных Location
записей. ObjectMapper используется для десериализации ответа JSON, и у нас есть собственный преобразователь, который назначает вложенные атрибуты Location
для ассоциации базовых данных на объекте Project
.
Соответствующая часть кода выглядит следующим образом. Он выполняется в обещании PromiseKit (отсюда seal
), и мы сначала сохраняем его в фоновом контексте, а затем распространяем в основной контекст, который используется в потоке пользовательского интерфейса.
WNManagedObjectController.backgroundContext.perform {
let project = Mapper<Project>().map(JSONObject: JSON(json).object)!
try! WNManagedObjectController.backgroundContext.save()
WNManagedObjectController.managedContext.performAndWait {
do {
try WNManagedObjectController.managedContext.save()
seal.fulfill(project.objectID)
} catch {
seal.reject(error)
}
}
}
Проблема, с которой мы столкнулись, заключается в том, что этот процесс вставки сохраняет каждую Location
запись в базу данных дважды . Как ни странно, дублированные записи Location
не имеют никакой связи с родительской записью Project
. То есть, если Location
записи ищутся с NSFetchRequest
, или если я запускаю запрос к базовой базе данных SQLite, я вижу, что есть две записи для каждого Location
, но только project.locations
возвращает одну копию каждого Location
. Тот же (или очень похожий) процесс, применяемый к другим типам записей с такой же структурой, также приводит к дублированию.
Я уже пробовал несколько вещей, чтобы сузить проблему:
- Проверено API JSON - без дубликатов.
- Проверено состояние свойства
project.locations
непосредственно перед записью Core Data. До сохранения объектов нет повторяющихся записей, что указывает на корректную работу десериализатора и преобразователя пользовательских вложенных атрибутов. - Удален блок, который распространяет изменения в контексте управляемого объекта основного потока, если это было в результате чего вставка происходит дважды. Все еще получайте дубликаты только с записью в фоновый контекст.
- Запустите приложение с установленным
com.apple.CoreData.ConcurrencyDebug 1
. В этом процессе не возникает исключений, подтверждающих, что это не проблема безопасности потоков. - Запустите приложение с установленным
com.apple.CoreData.SQLDebug 1
. В журналах видно, что Core Data вставляет ровно в два раза больше строк Location
, чем ожидалось, в базовую базу данных SQLite. - Реализовано ограничение уникальности для объекта. Это решает проблему с точки зрения того, какие данные сохраняются, но все равно выдаст ошибку, если не установлено
NSMergePolicy
.
Последний элемент в этом списке эффективно решает проблему, но он лечит симптом, а не причину. Целостность данных важна для нашего приложения, и я пытаюсь понять, в чем может быть основная проблема, или другие варианты, которые я мог бы использовать для дальнейшего ее изучения.
Спасибо!