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

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
}()

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?

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.

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)

Cannot create an SQL store with a nil URL

So I am trying to create a today widget extension using the data stored by the parent app.
I am trying to do so by creating a Persistent Store Coordinator as you do in parent app and using the URL for the main app database.
However when doing so I get the following error;
2016-03-13 12:59:02.155 TodayWidget[10942:1456153] Failed to inherit
CoreMedia permissions from 5079: (null) 2016-03-13 12:59:02.336
TodayWidget[10942:1456112] *** Terminating app due to uncaught
exception 'NSInvalidArgumentException', reason: 'Cannot create an SQL
store with a nil URL.'
Here's what I am doing in the Today widget view controller. The containerPath is the same as the bundle identifier for the today extension target. The containerPath?.URLByAppendingPathComponent sql file name is the same as the parent app core data stack sql file name as well as the the modelURL is the same as it is in the parent app.
var context: NSManagedObjectContext!
let fileManager = NSFileManager.defaultManager()
var containerPath = fileManager.containerURLForSecurityApplicationGroupIdentifier("com.company.APPNAME.TodayWidget")
containerPath = containerPath?.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
let modelURL = NSBundle.mainBundle().URLForResource("APPNAME", withExtension: "mom")
let model = NSManagedObjectModel(contentsOfURL: modelURL!)
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model!)
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: containerPath, options: nil)
} catch {
print("yellow")
}
context = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
context.persistentStoreCoordinator = coordinator
Does anybody know how I can resolve the error ?
PS: I have enabled and created app groups and added the today widget as a target for the core data model and the subclasses.
"com.company.APPNAME.TodayWidget" looks suspicious. It looks like the bundle ID for your today widget. The argument to containerURLForSecurityApplicationGroupIdentifier() should be the string you specified when you set up the com.apple.security.application-groups capability.

SQLite data lost between Simulator App launches

This feels strange and I somehow could not find an answer through search. This is an issue on Xcode iOS Simulator.
I am write an data persistence code. When instantiate the data store, I pretty much copied the following code fragment from Apple (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/InitializingtheCoreDataStack.html#//apple_ref/doc/uid/TP40001075-CH4-SW1).
Now, the issue seems - storeURL (when in debugging) is a URL containing a long numeric string and when relaunching the simulator, that long numeric string was changed. So the sqlite file is not reachable any more. I do believe I saved the data correctly because I debugged through the code (and context save api called with no error), I retrieved data after saving (without relaunching) and I see the data using command line sqlite tool against the sqlite file.
import UIKit
import CoreData
class DataController: NSObject {
var managedObjectContext: NSManagedObjectContext
init() {
// This resource is the same name as your xcdatamodeld contained in your project.
guard let modelURL = NSBundle.mainBundle().URLForResource("DataModel", withExtension:"momd") else {
fatalError("Error loading model from bundle")
}
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else {
fatalError("Error initializing mom from: \(modelURL)")
}
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
self.managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
self.managedObjectContext.persistentStoreCoordinator = psc
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let docURL = urls[urls.endIndex-1]
/* The directory the application uses to store the Core Data store file.
This code uses a file named "DataModel.sqlite" in the application's documents directory.
*/
let storeURL = docURL.URLByAppendingPathComponent("DataModel.sqlite")
do {
try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil)
} catch {
fatalError("Error migrating store: \(error)")
}
}
}
}
This is what basically Matt said in the comment section. This issue is related to where to place the .sqlite file.

Resources