Core data not retaining older values - swift

When I save to Core Data and then try to read from it, only the most recently saved value is retained.
The rest are nil when I try to print them out.
In my .xcdatamodeld, my entity is named CD_Cookbook and it has an attribute of name.
I'm not sure what I'm doing wrong.
func newCookbook(cookbook: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "CD_Cookbook", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
item.setValue(cookbook, forKey: "name")
do {
try managedContext.save()
}
catch {
print("did not save cookbook name to core data", error)
}
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "CD_Cookbook")
do {
let cd = try managedContext.fetch(fetchRequest)
print(cd)
}
catch {
print("Failed to fetch cookbook names from Core Data", error)
}
}

Instead of:
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "CD_Cookbook")
Do this instead:
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CD_Cookbook")

Related

Empty Core Data Table

I would like to remove all data from a selected Core Data Entity. I'm using the below code but getting the following error:
Unresolved identifier managedObjectContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "UserAccountTbl")
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try managedObjectContext.executeRequest(batchDeleteRequest)
} catch {
// Error Handling
}
First of all – as already mentioned in the other answers – you have to get the NSManagedObjectContext from AppDelegate
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedObjectContext = appDelegate.persistentContainer.viewContext
Force unwrapping is perfectly fine, the application wouldn't even launch if AppDelegate was missing.
But there is a caveat:
If you call execute on the managed object context you have to merge the changes into the context because the execute operation doesn't modify the content of the context.
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "UserAccountTbl")
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
batchDeleteRequest.resultType = .resultTypeObjectIDs
do {
let result = try managedObjectContext.execute(batchDeleteRequest) as! NSBatchDeleteResult
let changes = [NSDeletedObjectsKey : result.result as! [NSManagedObjectID]]
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [managedObjectContext])
} catch {
print(error)
}
However you can avoid the merge step if you call execute on the persistent store coordinator
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "UserAccountTbl")
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
let persistentStoreCoordinator = managedObjectContext.persistentStoreCoordinator!
try persistentStoreCoordinator.execute(batchDeleteRequest, with: managedObjectContext)
} catch {
print(error)
}
Seems like you haven't declared your managedObjectCotext and trying to access it. Try this:
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "UserAccountTbl")
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try managedObjectContext.executeRequest(batchDeleteRequest)
} catch {
// Error Handling
}
As from your error, you have not declare context from container which is in appDelegate. You can try with following code:
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
//We need to create a context from this container
let managedObjectContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "UserAccountTbl")
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try managedObjectContext.executeRequest(batchDeleteRequest)
} catch {
// Error Handling
}

function not saving string on textfield to core data

My function below when called is not saving whatever is on the textfield to core data. When appearing in the debugg area what appears is "". THis code when used in the view did load function it does work but when I call it it does not work.
func timTebow(){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Item", in: context)
let newUser = NSManagedObject(entity: entity!, insertInto: context)
newUser.setValue(playName.text, forKey: "atBATS")
do {
try context.save()
} catch {
print("Failed saving")
}
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Item")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
print(data.value(forKey: "atBATS") as? String)
}
} catch {
print("Failed")
}
}

Using CoreData in swift from a tableview selection

I have 2 functions, one which should save the string to a CoreData attribute and another to fetch it. And this was written by copying working code from elsewhere in the project, so I can't figure out why when I print the contents of 'currentSetting' I get nil.
The save function is called from didSelectRowAtIndexPath and it sends in indexPath.row as the parameter and this fetches a String from an array.
func getImageSetting() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Settings")
do {
let fetcher = try context.fetch(fetchRequest)
let data = fetcher as? [NSManagedObject]
if let results = data {
let settingResult = results[0]
currentSetting = settingResult.value(forKey: "imageQuality") as? String
print(currentSetting as Any)
} else { print ("error") }
}
catch let error as NSError {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
func saveSetting(selection: Int) {
let coreSelection = choices[selection]
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.persistentContainer.viewContext
let itemEntity = NSEntityDescription.entity(forEntityName: "Settings", in: managedContext)!
let item = NSManagedObject(entity: itemEntity, insertInto: managedContext)
item.setValue(coreSelection, forKey: "imageQuality")
do {
try managedContext.save()
} catch let error as NSError {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}

NSPredicate example of one to many relationship query

This is my core data model
The relationship is as follows:
enter image description here
The relationship is one Route to many Locations. First question is should there be a name attribute (routeName) in Location? In DB2 for example, you always name your primary keys, but most examples I have seen in Core Data they handle primary keys under the covers in the relationship definition.
Here are Table excerpts from DB Browser for sqlite.
Table Excerpts
Here is the code:
final class CoreDataManager {
static let sharedInstances = CoreDataManager()
let managedContext: NSManagedObjectContext
private init(){
let application = UIApplication.shared.delegate as! AppDelegate
managedContext = application.persistentContainer.viewContext
}
func createRoute(name: String) -> Route {
let routeEntity = NSEntityDescription.entity(forEntityName: "Route", in: managedContext)!
let route = NSManagedObject(entity: routeEntity, insertInto: managedContext)
route.setValue(name, forKeyPath: "name")
route.setValue(Date().timeIntervalSince1970, forKey: "ts")
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
return route as! Route
}
func createLocation(loc: CLLocation, route: Route) {
let locationEntity = NSEntityDescription.entity(forEntityName: "Location", in: managedContext)!
let location = NSManagedObject(entity: locationEntity, insertInto: managedContext)
location.setValue(loc.coordinate.latitude, forKeyPath: "latitude")
location.setValue(loc.coordinate.longitude, forKeyPath: "longitude")
location.setValue(loc.altitude, forKeyPath: "altitude")
location.setValue(loc.timestamp, forKeyPath: "ts")
location.setValue(route, forKeyPath: "route")
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
func getAllRoutes () -> [Route] {
let routeFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Route")
let routeResults = try! managedContext.fetch(routeFetch)
return routeResults as! [Route]
}
func getLocationsByRoute (name: String) -> [Location] {
let locFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Location")
//select * from ZLOCATION, ZROUTE where ZLOCATION.ZROUTE = ZROUTE.Z_PK and ZROUTE.ZNAME = ?
locFetch.predicate = NSPredicate(format: "locations.route = %#", name)
locFetch.sortDescriptors = [NSSortDescriptor.init(key: "ts", ascending: false)]
let locations = try! managedContext.fetch(locFetch)
return locations as! [Location]
}
}
If no primary key in Location Table, how do I create a query to get all Locations for a given Route? You can see in the code I have a getLocationsByRoute method, but cannot get the query to work. Any help would be appreciated.
If you have the Route object, get the locations from the locations property
If you have to fetch the objects use the predicate format
locFetch.predicate = NSPredicate(format: "route.name = %#", name)
The key path is always <relationship name>.<attribute name> – in this case get all Location objects whose name of the related route is equal to the given name.
Stop thinking in database terms, Core Data is an ORM.
You get all locations for a route by accessing the locations property of the Route class since it corresponds to the one to many relationship between Route and Location
let locations = someRoute.locations

Saving multiple objects for an attribute in CoreData

CoreData Structure:
Entity: ZooAnimals, Attributes: animal (String type) and count (Integer 16 type)
I want to save in some animal names and the quantity (count) of each animal. I can do this by hard coding the multiple objects needed to do this – see code below – but how could I do it more flexibly so that I might just give it a couple of arrays of any length and have them save into CoreData (e.g. var animalArray = ["Donkey","Horse","Zebra","Okapi"], var animalCountArray = [7,3,9,2])
import UIKit
import CoreData
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let animal1 = NSEntityDescription.insertNewObject(forEntityName: "ZooAnimals", into: managedContext)
let animal2 = NSEntityDescription.insertNewObject(forEntityName: "ZooAnimals", into: managedContext)
let animal3 = NSEntityDescription.insertNewObject(forEntityName: "ZooAnimals", into: managedContext)
let animal4 = NSEntityDescription.insertNewObject(forEntityName: "ZooAnimals", into: managedContext)
animal1.setValue("Donkey", forKey: "animal")
animal1.setValue(7, forKey: "count")
animal2.setValue("Horse", forKey: "animal")
animal2.setValue(3, forKey: "count")
animal3.setValue("Zebra", forKey: "animal")
animal3.setValue(9, forKey: "count")
animal4.setValue("Okapi", forKey: "animal")
animal4.setValue(2, forKey: "count")
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
}
An easy solution is a for loop
let animalArray = ["Donkey","Horse","Zebra","Okapi"]
let animalCountArray = [7,3,9,2]
assert(animalArray.count == animalCountArray.count, "The number of items in the arrays must be equal")
for i in 0..<animalArray.count {
let animal = NSEntityDescription.insertNewObject(forEntityName: "ZooAnimals", into: managedContext)
animal.setValue(animalArray[i], forKey: "animal")
animal.setValue(animalCountArray[i], forKey: "count")
}
do {
try managedContext.save()
} catch {
print("Could not save. \(error)")
}
Side note: guarding AppDelegate is pointless. The app wouldn't even launch if the application delegate class is missing.
Simply you need to use a for loop
let employeeNames = ["Donkey", "Horse", "Zebra","Okapi"]
for i in 0..<employeeNames.count
{
let employeeEntry = NSEntityDescription.insertNewObjectForEntityForName("ZooAnimals", inManagedObjectContext: managedObject)
employeeEntry.setValue(employeeNames[i], forKey: "employeename")
employees.setValue(index, forKey: "animal")
do {
try managedObject.save()
} catch {
print("problem saving")
}
}