CoreData using NSBatchInsertRequest - ios

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

Related

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

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?

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.

Swift 2 to Swift 3 Core Data in Xcode 9 - How can I port over existing Core Data "data" already saved?

I have an older project/app that I want to bring up to a 3.x version of Swift from Swift 2. In the course of porting the code over I recreated a new project with the same bundle id to start fresh and imported the code over.
Everything is working now in Swift 3.2 in Xcode 9 except for Core Data. Any device running an older version of the app crashes when accessing Core Data.
THe specific error is in the loading of the NSManagedObjectModel.
OLD SWIFT 2 WAY:
My CoreData File is called: "MyApp.xcdatamodeld"
lazy var managedObjectModel: NSManagedObjectModel = {
let modelURL = NSBundle.mainBundle().URLForResource("MyApp", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
NEW SWIFT 3 WAY:
My new CoreData File is called: "MyApp.xcdatamodel"
lazy var managedObjectModel: NSManagedObjectModel = {
if let modelURL = Bundle.main.url(forResource: "MyApp", withExtension: "xcdatamodel") {
return NSManagedObjectModel(contentsOf: modelURL)!
} else {
let modelURLOld = Bundle.main.url(forResource: "MyApp", withExtension: "momd")
return NSManagedObjectModel(contentsOf: modelURLOld!)!
}
}
When I run the code I get the following errors:
CoreData: annotation: Failed to load optimized model at path '/var/containers/Bundle/Application/50367482-FC2C-4553-B04B-68AD922B8128/MyApp.app/MyApp.momd/MyApp.omo'
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSFetchRequest could not locate an NSEntityDescription for entity name 'UMyAppModel''
So my new app code is not finding the the files to load for CoreData.
How can I can load these older CoreData files into my app? If need be I can set up a migration but the issue is that I can't see the older Core data files.
One way I can get the app to run is to add the following code:
let objectModel = NSManagedObjectModel.mergedModel(from: [Bundle.main])
return objectModel!
However none of the previous historical data is loaded into the app.
How can I migrate or get access to the original Coredata data to migrate into the new Coredata files on devices that have the old code version (swift 2) of the app?
Fixed the issue. It was related to the way the NSPersistentContainer was being initialized in my app. The swift 3 boiler plate code was not migrating to the new data store nor looking for the old sqlite db (which I suspect was ultimately the main cause of the data not being found).
My code:
lazy var persistentContainer: NSPersistentContainer = {
let modelURL = Bundle.main.url(forResource: "MyApp", withExtension: "momd")!
let container = NSPersistentContainer(name: "MyApp", managedObjectModel: NSManagedObjectModel(contentsOf: modelURL)!)
let documentsDirectory = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask)
let storeUrl = self.applicationDocumentsDirectory.appendingPathComponent("MyApp.sqlite")
let description = NSPersistentStoreDescription()
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true
container.persistentStoreDescriptions = [description]
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeUrl)]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()

how to fix attempt to recursively call -save error with coredata?

I am getting this error randomly while saving in core data
Unresolved error Error Domain=NSCocoaErrorDomain Code=132001 "(null)" UserInfo={message=attempt to recursively call -save: on the context aborted, stack trace=(
Everything is working fine for last 3 month but recently I due to change in app I have to call a lot of fetch and save request and some of them are in loop and some in closure after making these changes I faced this error.
Here is code for coredata manager
import Foundation
import CoreData
class CoreDataStack {
private init() {
}
class func getContext () -> NSManagedObjectContext {
return CoreDataStack.managedObjectContext
}
// MARK: - Core Data stack
static var managedObjectContext: NSManagedObjectContext = {
var applicationDocumentsDirectory: URL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "com.cadiridris.coreDataTemplate" in the application's documents Application Support directory.
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()
var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = Bundle.main.url(forResource: "Thyssenkrupp", withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
let url = applicationDocumentsDirectory.appendingPathComponent("Thyssenkrupp.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
let options = [ NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption:true ]
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
print("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
//abort()
}
return coordinator
}()
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
class func saveContext () {
DispatchQueue.main.async {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
print("Unresolved error \(nserror), \(nserror.userInfo)")
//abort()
}
}
}
}
}
Please provide any suggestion why this error coming
The problem was saving data to CoreData to frequently, Yes you can CoreData as Frequently as you want but it will through this error on console if you add/delete/update a data and save it in a loop doing this way will cause this error, not always but it's better to save CoreData after loop is complete. As Saving to Core Data is important in case where we perform Create,Update, Delete operation and we didn't save the CoreData app crashes/closed down for some reason then data will be lost from the point we last save the CoreData. Saving to CoreData is like a checkpoint everything is saved. So saving in a loop is not efficient way to do.

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