An error occurred during persistent store migration (Cannot migrate store in-place) Core Data - ios

I added new model version and select it like default one. Then I added new attribute to existing entity in new model version. To test migration I installed previous version of app and add filled with all data in app. Then I install new changes and it work on simulator. If I move that build to TestFlight and test via real device then I get this error:
Fatal error: ###persistentContainer: Failed to load persistent stores:Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={sourceURL=file:///private/var/mobile/Containers/Shared/AppGroup/DBB80C75-1B07-4318-8BA3-3F4FFC14FBD7/AppName.sqlite, reason=Cannot migrate store in-place: near "null": syntax error, destinationURL=file:///private/var/mobile/Containers/Shared/AppGroup/DBB80C75-1B07-4318-8BA3-3F4FFC14FBD7/AppName.sqlite, NSUnderlyingError=0x281958180 {Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={reason=near "null": syntax error, NSSQLiteErrorDomain=1, NSUnderlyingException=near "null": syntax error}}}
I need to use CoreData for Widget's target as well. Code is here :
final class CoreDataStack {
static let shared = CoreDataStack()
var context: NSManagedObjectContext { persistentContainer.viewContext }
var container: NSPersistentContainer { persistentContainer }
private let containerName = "AppName"
private var persistentContainer: NSPersistentContainer!
// MARK: - Setup
func setup() {
guard let storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "app_group")?.appendingPathComponent(containerName + ".sqlite") else {
fatalError("Error finding Model from File Manager")
}
let container = NSPersistentContainer(name: containerName)
let description = NSPersistentStoreDescription(url: storeURL)
description.type = NSSQLiteStoreType
description.shouldMigrateStoreAutomatically = true
description.shouldInferMappingModelAutomatically = true
container.persistentStoreDescriptions = [description]
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
container.viewContext.transactionAuthor = appTransactionAuthorName
container.viewContext.automaticallyMergesChangesFromParent = true
container.loadPersistentStores(completionHandler: { (_, error) in
if let error = error as NSError? {
let crashlitycService: CrashlitycUseCase = CrashlitycService()
crashlitycService.record(error: .errorForAnalytic(error), file: #file, function: #function, line: #line)
fatalError("###\(#function): Failed to load persistent stores:\(error)")
}
})
self.persistentContainer = container
}
}
iOS 16.2 Xcode 14.1
Similar issues I found here, but without any success:
An error occurring during Core Data persistent store migration in iOS 13
iOS app crashing on launch screen due to core data migration
Please help me to figure out how can I do this migration?

Related

App Group not working to fetch Core Data with WidgetKit

I'm trying to access my shared Core Data store from WidgetKit. I created an App Group and added both targets to it. In my PersistenceController, I started with just the default code that comes with a Core Data project, but updated it to use my App Group url as the storage path.
Here's my PersistenceController:
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let samplePokemon = Pokemon(context: viewContext)
samplePokemon.id = 0
samplePokemon.name = "bulbasaur"
samplePokemon.types = ["grass", "poison"]
samplePokemon.hp = 45
samplePokemon.attack = 49
samplePokemon.defense = 49
samplePokemon.specialAttack = 65
samplePokemon.specialDefense = 65
samplePokemon.speed = 45
samplePokemon.sprite = URL(string: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png")
samplePokemon.shiny = URL(string: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/1.png")
samplePokemon.favorite = false
do {
try viewContext.save()
} catch {
let nsError = error as NSError
print(nsError)
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
init(inMemory: Bool = false) {
let sharedStoreURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.MyAppGroup")!.appending(path: "Poke.sqlite")
let description = NSPersistentStoreDescription(url: sharedStoreURL)
container = NSPersistentContainer(name: "Poke")
if inMemory {
container.persistentStoreDescriptions = [description]
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
This works just fine running in my main app, and the data gets stored in the database just fine too, but the problem I run into is when I try to fetch data for my widget.
Here's the code that deals with Core Data in my widget Provider:
var randomPokemon: Pokemon {
let context = PersistenceController.shared.container.viewContext
let fetchRequest: NSFetchRequest<Pokemon> = Pokemon.fetchRequest()
var results: [Pokemon] = []
do {
results = try context.fetch(fetchRequest)
} catch {
print("Couldn't fetch: \(error)")
}
print(results)
if let randomPokemon = results.randomElement() {
return randomPokemon
}
return SamplePokemon.samplePokemon
}
It always returns samplePokemon, because the results are always empty.
Here's my console log:
2022-08-06 20:55:49.029944-0600 RandomFavoriteExtension[46125:627790] [error] warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'Pokemon' so +entity is unable to disambiguate.
CoreData: warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'Pokemon' so +entity is unable to disambiguate.
2022-08-06 20:55:49.030043-0600 RandomFavoriteExtension[46125:627790] [error] warning: 'Pokemon' (0x600002f280b0) from NSManagedObjectModel (0x600003b388c0) claims 'Pokemon'.
CoreData: warning: 'Pokemon' (0x600002f280b0) from NSManagedObjectModel (0x600003b388c0) claims 'Pokemon'.
2022-08-06 20:55:49.030092-0600 RandomFavoriteExtension[46125:627790] [error] warning: 'Pokemon' (0x600002f38160) from NSManagedObjectModel (0x600003b3ccd0) claims 'Pokemon'.
CoreData: warning: 'Pokemon' (0x600002f38160) from NSManagedObjectModel (0x600003b3ccd0) claims 'Pokemon'.
2022-08-06 20:55:49.030185-0600 RandomFavoriteExtension[46125:627790] [error] error: +[Pokemon entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
CoreData: error: +[Pokemon entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
Error Domain=NSCocoaErrorDomain Code=133021 "(null)" UserInfo={NSExceptionOmitCallstacks=true, conflictList=(
"NSConstraintConflict (0x600000d2f680) for constraint (\n id\n): database: 0x9147aa890f321eb4 <x-coredata://E5FB4658-9345-4245-B471-FAEE5B98F707/Pokemon/p1>, conflictedObjects: (\n \"0x9147aa890d921eb4 <x-coredata://E5FB4658-9345-4245-B471-FAEE5B98F707/Pokemon/p20>\"\n)"
)}
I have been working on this for weeks now. Any help would be very appreciated.

CoreData using NSBatchInsertRequest

I'm trying to insert Array of dictionary in CoreData using NSBatchInsertRequest according WWDC 2019 (https://developer.apple.com/videos/play/wwdc2019/230/). The insertResult is nil, and my CoreData is empty.
let modelURL = Bundle.main.url(forResource: "CoreDataPerformance", withExtension: "momd")!
let model = NSManagedObjectModel(contentsOf: modelURL)!
let container = NSPersistentCloudKitContainer(name: "CoreDataPerformance", managedObjectModel: model)
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
try container.viewContext.setQueryGenerationFrom(.current)
let moc = container.viewContext
moc.automaticallyMergesChangesFromParent = true
moc.perform {
let insertRequest = NSBatchInsertRequest(entity: Client.entity(), objects: clients)
let insertResult = try? moc.execute(insertRequest) as? NSBatchInsertRequest
let success = insertResult?.resultType
print("RESULT STATUS: \(success)")
}
This is the error I receive in the console:
2020-02-04 18:30:25.800705+0200 CoreDataPerformance[62836:778869] [error] warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'CoreDataPerformance.Client' so +entity is unable to disambiguate.
CoreData: warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'CoreDataPerformance.Client' so +entity is unable to disambiguate.
2020-02-04 18:30:25.800846+0200 CoreDataPerformance[62836:778869] [error] warning: 'Client' (0x600000c50bb0) from NSManagedObjectModel (0x600001877480) claims 'CoreDataPerformance.Client'.
CoreData: warning: 'Client' (0x600000c50bb0) from NSManagedObjectModel (0x600001877480) claims 'CoreDataPerformance.Client'.
2020-02-04 18:30:25.800940+0200 CoreDataPerformance[62836:778869] [error] warning: 'Client' (0x600000c589a0) from NSManagedObjectModel (0x600001861680) claims 'CoreDataPerformance.Client'.
CoreData: warning: 'Client' (0x600000c589a0) from NSManagedObjectModel (0x600001861680) claims 'CoreDataPerformance.Client'.
your line let insertResult = try? moc.execute(insertRequest) as? NSBatchInsertRequest
should be:
let insertResult = try? moc.execute(insertRequest) as? NSBatchInsertResult
The disambiguation issue is because you have multiple core data stacks open while the app is running. Is this because you have your app and a stack for tests? or because your app is using more than one?
The error CoreData: warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'CoreDataPerformance.Client' so +entity is unable to disambiguate. means you have multiple entities in your model using the Client NSManagedObject subclass, there should only be one.
Another issue with your code is automaticallyMergesChangesFromParent does not work with batch requests because no did save notification is generated. You have to use NSPersistentStoreRemoteChangeNotification, NSPersistentHistoryChangeRequest and mergeChangesFromContextDidSaveNotification the technique is explained in WWDC 2018 Core Data Best Practices # 21:57
I think there's an error in the WWDC talk. Try doing this:
let insertRequest = NSBatchInsertRequest(entity: Client.entity(), objects: clients)
try! moc.execute(insertRequest)
try! moc.save()
I used try! just so I can see any error. You can change it

NSUnderlyingException = "Can't add the same store twice" when migrating core data

I am setting up my apps persistent container with the following code:
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "App_Name")
let myFileManager = FileManager()
do {
let docsurl = try myFileManager.url(for:.applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let myUrl = docsurl.appendingPathComponent("App_Name")
let description = NSPersistentStoreDescription(url: myUrl)
container.persistentStoreDescriptions = [description]
let options = [NSInferMappingModelAutomaticallyOption : true,
NSMigratePersistentStoresAutomaticallyOption : true]
try container.persistentStoreCoordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: myUrl, options: options)
} catch {
fatalErrorText = error.localizedDescription
print(fatalErrorText)
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalErrorText = error.debugDescription
print(fatalErrorText)
}
})
return container
}()
However when I try and access core Data it get the following error:
2017-08-07 14:43:57.391529+0100 App Name[98764:1854740] [error] error: -addPersistentStoreWithType:SQLite configuration:(null) URL:file:///Users/Seb/Library/Developer/CoreSimulator/Devices/241E1A36-631B-4071-8357-5F551F32403F/data/Containers/Data/Application/BC35D1CD-FA17-4F1F-99A0-EB0E73A42F3C/Library/Application%20Support/App_Name.sqlite options:{
NSInferMappingModelAutomaticallyOption = 1;
NSMigratePersistentStoresAutomaticallyOption = 1;
} ... returned error Error Domain=NSCocoaErrorDomain Code=134080 "(null)" UserInfo={NSUnderlyingException=Can't add the same store twice} with userInfo dictionary {
NSUnderlyingException = "Can't add the same store twice";
}
CoreData: error: -addPersistentStoreWithType:SQLite configuration:(null) URL:file:///Users/Seb/Library/Developer/CoreSimulator/Devices/241E1A36-631B-4071-8357-5F551F32403F/data/Containers/Data/Application/BC35D1CD-FA17-4F1F-99A0-EB0E73A42F3C/Library/Application%20Support/App_Name.sqlite options:{
NSInferMappingModelAutomaticallyOption = 1;
NSMigratePersistentStoresAutomaticallyOption = 1;
} ... returned error Error Domain=NSCocoaErrorDomain Code=134080 "(null)" UserInfo={NSUnderlyingException=Can't add the same store twice} with userInfo dictionary {
NSUnderlyingException = "Can't add the same store twice";
}
I have got iCloud enabled, and did find an answer that was claiming the problem was with iCloud, but their solution didn't work for me.
I have found a few other solutions to this problem on here but haven't been able to decipher/translate the answers.
A NSPersistentContainer is a wrapper for all you need for a core-data stack. It will create a managedObjectContext with a persistentStoreCoordinator setup with a single sql store. It is simplified to find the model with they name that you give it, and name the sql file with the same name. Automatic migration is turned on by default for an NSPersistentContainer.
If you need a more custom setup, you can either create all the entities yourself (which is not that hard). Or you can set the persistentStoreDescriptions property before you call loadPersistentStores. With a persistentStoreDescription you can set the URL to save the sql file to, and set shouldMigrateStoreAutomatically.
Generally there is no reason to to as it is set to true by default see https://developer.apple.com/documentation/coredata/nspersistentstoredescription/1640566-shouldmigratestoreautomatically
TL;DR get rid of everything in the do catch, the default behavior of loadPersistentStores will work find

Setting persistentStoreDescriptions causes no CoreData objects to be saved

I am trying to ensure that all of our CoreData is protected using Data Protection. When I try to set a NSPersistentStoreDescription on my container, no CoreData objects are saved. If I comment out the line indicated below, all objects are saved (and read) just fine. If I enable the line, nothing gets saved (or perhaps the read silently fails?). There are no errors generated and there are no logs generated. I do have the Data Protection entitlement in my provisioning profile (matching completeUnlessOpen). I've gotta be missing something very basic.
This is Xcode 8.2.1 (8C1002)
Can anyone offer any insight / advice?
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "my_app")
let description = NSPersistentStoreDescription()
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true
description.setOption(FileProtectionType.completeUnlessOpen as NSObject?, forKey: NSPersistentStoreFileProtectionKey)
// *** ALLOWING THIS NEXT LINE TO EXECUTE CAUSES PROBLEM ***
container.persistentStoreDescriptions = [description]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
print("Unresolved error \(error), \(error.userInfo)")
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
I understand that this question has been asked long time back, but I'm still going to post an answer so that anyone stuck at this problem gets some help.
In the above code snippet you posted in the question, you did not initialize NSPersistentStoreDescription with SQLITE file URL.
So, it does not know what persistent container it represents. Please refer below working code.
let container = NSPersistentContainer(name: "AppName")
if let storeDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
let sqliteURL = storeDirectory.appendingPathComponent("AppName.sqlite")
//Set Protection for Core data sql file
let description = NSPersistentStoreDescription(url: sqliteURL)
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true
description.setOption(FileProtectionType.complete as NSObject, forKey: NSPersistentStoreFileProtectionKey)
container.persistentStoreDescriptions = [description]
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in...}
Let me know if this works for you and please accept the answer if it does, so that others will know.
Your error is actually because you're setting options that should relate to Core Data. File protection is done under FileManager setAttributes function calls.
However, the answer to the underlying question is that you don't need to add file protection to individual files - they're set to the default protection you declare in your entitlement.
This can be verified by printing the output of:
let attributes = try FileManager.default.attributesOfItem(atPath: url.relativePath)

CoreData crash on migration with NSMigrationConstraintViolationError = 134111 error

I am creating a new NSPersistentStoreCoordinator for background long savings from the server. It worked fine but suddenly I see a crash in Production with the following error -
CRASH_INFO_ENTRY_0
fatal error: Background context creation failed with error Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={sourceURL=file:///var/mobile/Containers/Data/Application/8EF27C05-1755-49EE-B174-8B163CC7CC1D/Documents/App.sqlite, reason=Cannot migrate store in-place: constraint violation during attempted migration, destinationURL=file:///var/mobile/Containers/Data/Application/8EF27C05-1755-49EE-B174-8B163CC7CC1D/Documents/.App.sqlite.migrationdestination_41b5a6b5c6e848c462a8480cd24caef3, NSUnderlyingError=0x170a56290 {Error Domain=NSCocoaErrorDomain Code=134111 "(null)" UserInfo={reason=constraint violation during attempted migration, NSUnderlyingException=Constraint violation, _NSCoreDataOptimisticLockingFailureConflictsKey=( "" )}}}: file /Users/*******/Desktop/7.50/APPS/LTG/project/modelBase/db/DatabaseManager.swift, line 67
Lookalike I have a constraints issue as this is the error I get -
NSMigrationConstraintViolationError = 134111, //
migration failed due to a violated uniqueness constraint
Bu since the _NSCoreDataOptimisticLockingFailureConflictsKey is <null> I can not trace the issue.
This is my code -
lazy var backgroundSyncContext:NSManagedObjectContext = {
let psc = NSPersistentStoreCoordinator(managedObjectModel: NSManagedObjectModel.MR_defaultManagedObjectModel()!)
let context = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
context.undoManager = nil
context.persistentStoreCoordinator = psc
let mOptions = [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true]
let paths = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask)
let applicationDocumentURL = paths.last!.absoluteString?.stringByReplacingOccurrencesOfString("%20", withString: " ")
let documentPath = paths.last!
let storeUrl = documentPath.URLByAppendingPathComponent("Prep4\(AppConfig.sharedInstance.examName).sqlite")
do {
try context.persistentStoreCoordinator?.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeUrl, options: mOptions)
print("Created background context")
} catch (let error){
fatalError("Background context creation failed with error \(error)")
}
return context
}()
EDIT
OK, I have a guess - is the latest version I added constraints to the model. Is there a chance that the old version on the user device contains duplicates that are nor valid with the new constraints so the the migration fails.
Does that sounds reasonable ?
Thanks
Shani

Resources