NSEntityDescription.entity crashes app (SIGABRT) - ios

The line containing NSEntityDescription.entity is crashing and giving me the error
Thread 1: SIGABRT
I have seen other people ask this question, the recommended answer is to simply delete and remake the entity from scratch. I have done this many times, I have also "cleaned" the code thoroughly, and imported CoreData in both my AppDelegate.swift files and this ViewController file. Does anyone have any advice?
override func viewDidLoad() {
super.viewDidLoad()
addGesture()
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let stringModelEntity = NSEntityDescription.entity(forEntityName: "StringModels", in: managedContext)!
let stringBundle = NSManagedObject(entity: stringModelEntity, insertInto: managedContext)
self.getJSON(stringBundle)
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
EDIT
I've found a solution thanks to finally opening the debugger, the following link's 'best answer' describes and solves this issue: Core data: Failed to load model

This will mean that it cannot find the entity with the name "StringModels". In my experience, the error SIGABRT is caused when something that the program thinks should exist does not.
I would check capitalization and spelling.

Related

Core Data on Rare Occasions Randomly Deletes Everything

Very rarely (<1% of users), a user will inform me that they open my app, and all their data is gone. The problem I'm having is that I am unable to replicate the issue because it happens so rarely. I don't even know how to begin fixing this problem. It seems like this does not happen while a user is using the app. They seem to just open it one day and the data is no longer there.
On one occasion, a user was able to recover the data by turning off and on the iCloud switch for my app (in the phone settings). However, this did not work for another user.
I was reading that iOS may delete the cache when the storage is full. However, to my understanding, the default location for the container is in Application Support.
Here is my Core Data implementation in App Delegate:
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentCloudKitContainer(name: "Bench")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
print("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
context.perform {
do {
try context.save()
} catch {
let nserror = error as NSError
print(nserror.localizedDescription)
}
}
}
}
I access it globally like this:
let ad = UIApplication.shared.delegate as! AppDelegate
let context = ad.persistentContainer.viewContext

iOS data saved in Core Data doesn't survive launch

EDIT *********
7-09-2020 1:39 PM PST
I've got what I believe will suffice as a minimal working reproducible version of the app available at:
https://github.com/Rattletrap99/penny-game-test
EDIT *********
I'm building a game in which users create coins as rewards for various achievements. The coins are saved as managed objects in Core Data, with their various attributes. They are retrieved for various reasons, have their attributes modified, etc., during game play.
Everything saves and retrieves perfectly until I quit and relaunch, at which point no data is present in the persistent store. This is the case whether using a simulator or a device.
My usual means of saving to Core Data is:
func saveIt(){
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
appDelegate.saveContext()
}
Which calls:
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
savedFlag += 1
let coinFetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Coin")
let savedCoins = try context.fetch(coinFetchRequest) as! [Coin]
print("In appDelegate, saveContext, after context.save, there are \(savedCoins.count) coins.")
print("Successfully saved in appDelegate \(String(describing: savedFlag)) times")
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
Every print() statement I put in the code confirms the save, but when I retrieve (upon relaunch), via code similar to:
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let issuerFetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Issuer")
let coinFetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Coin")
do {
let issuers = try context.fetch(issuerFetchRequest) as! [Issuer]
print(" ### Upon startup, there are \(issuers.count) Issuers in CD")
let coins = try context.fetch(coinFetchRequest) as! [Coin]
print(" #### Upon startup, there are \(coins.count) Coins in CD")
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
I get:
### Upon startup, there are 0 Issuers in CD
#### Upon startup, there are 0 Coins in CD
Also, I try to save in applicationWillTerminate to be certain the data is saved before quitting:
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
let issuerFetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Issuer")
let coinFetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Coin")
do {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let issuers = try context.fetch(issuerFetchRequest) as! [Issuer]
let coins = try context.fetch(coinFetchRequest) as! [Coin]
print("\\\\ Upon quit, there are \(issuers.count) Issuers in CD")
print("\\\\ Upon quit, there are \(coins.count) Coins in CD")
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
However, the print() statements are not printed, leading me to believe applicationWillTerminate is never fired.
I should mention that Issuer has a to many relationship with Coin, and I ensure that there are Issuers present before creating and saving a Coin.
Any help greatly appreciated!
After beating myself half to death for longer than I care to admit, I found the fault in these three lines in AppDelegate.swift:
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true
container.persistentStoreDescriptions = [description]
Once these were removed, everything went back to normal. I'd like to say I understand why, but honestly, removing these lines were an act of desperation. And the irony is that I had added these lines in an attempt to rectify an earlier problem with fetching from Core Data.
Many thanks to all who contributed!
Remember to load up the persistentContainer in your AppDelegate. You can create a project in Xcode with CoreData built-in.
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded 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.
*/
let container = NSPersistentContainer(name: "CoreDataModelName")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}

best way to store and update Data on device

Every time, when user launches application, app sends request to API and receive Data from it. I have to storage old data in a file on device and update them with every launch. When size of Data will be over than 3-5 mb, reading can be very long, isn't it?
My questions is: if i will store more than 5 mb of Data in file, can i quickly fetchs them, or only use database?
If yes, how can i create directory and put file to it?
And should i archive them?
I had a similar task and used CoreData with background fetch via DispatchQueue. I think this should help.
EDIT:
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
DispatchQueue.global(qos: .background).async {
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "ENTITY")
do {
self."NSManagedObject ARRAY" = try managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
DispatchQueue.main.async {
//MAKING CHANGES TO VIEW
}
}
P.S. I don't know how fast this fetch is, though it helped a lot in my task.

Dismiss View Controller Bad Access

I am getting a Bad Access error when I try to dismiss my view controller:
(This code is in a view controller named NewTaskViewController)
self.delegate?.newTask(name: taskName!, date:date!,image:self.image, location:self.location) //line 1
self.dismiss(animated: true, completion: nil) //line2
If I remove line 1 I do not get the error.
self.delegate is the view controller (ViewController) that presented this view controller (NewTaskViewController) that I am attempting to dismiss with line 2.
The newTask: method is saving the arguments with Core Data: (inside ViewController)
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
let entity =
NSEntityDescription.entity(forEntityName: "Task",
in: managedContext)!
let task = NSManagedObject(entity: entity,
insertInto: managedContext)
task.setValue(name, forKeyPath: "name")
do {
try managedContext.save()
tasks.append(task)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
Any ideas on why I do not get the bad access error when I remove the newTask: method that saves with Core Data. Is it that the newTask: method does not finish executing before self.dmismiss is called? But I don't understand how this is possible unless it has something to do with the way Core Data saves? Any ideas?
EDIT:
The error occurs in ViewController on the line:
newTaskViewController.delegate = self
Which is before the line that presents the newTaskViewController. How is this possible that the bad access error happens on code that has already been executed?

Issue after updating to Swift 3 [duplicate]

This question already has answers here:
How to apply the type to a NSFetchRequest instance?
(10 answers)
Closed 6 years ago.
I opened one of my projects and Xcode asked me for updating source to Swift 3. After clicking save I got as expected an error. The error occurs while creating a fetchRequest. (Error with error message in line 8)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let appDelegate = UIApplication.shared().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName:"Sessions") //<- Error error message: generic parameter 'ResultType' could not be inferred
do {
sessions = try managedContext.fetch(fetchRequest) as! [Sessions]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
print("fetched")
self.tableView.reloadData()
}
}
Also got a solution for this by myself. I just changed from
let fetchRequest = NSFetchRequest(entityName:"Sessions")
to
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName:"Sessions")

Resources