How to Update existing objects in Core Data? [duplicate] - ios

This question already has answers here:
How to update existing object in Core Data?
(5 answers)
Closed 5 years ago.
I'd like to update an object in Core Data. This is my code:
var detailTaskModel: TaskModel!
detailTaskModel.date = dateFromDP //update value
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
appDelegate.saveContext()
I'm using a NSFetchedResultsController and Swift.
Update:
var fetchedResultsController: NSFetchedResultsController = NSFetchedResultsController()
My func for loading the CoreData data:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: TaskCell = tableView.dequeueReusableCellWithIdentifier("myCell") as TaskCell
let thisTask = fetchedResultsController.objectAtIndexPath(indexPath) as TaskModel
cell.textLabel?.text = thisTask.task
return cell
}
Saving CoreData:
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let entityDescription = NSEntityDescription.entityForName("TaskModel", inManagedObjectContext: managedObjectContext!)
let task = TaskModel(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext!)
task.task = labelText

You simply update any property of Core data object and call save on NSManagedObjectContext. You can also check for any changes with hasChanges method.
managedObject.setValue("newValue", forKey: "propertyName")
Update your object like above or direct call assignment and do the following
if let moc = self.managedObjectContext {
var error: NSError? = nil
if moc.hasChanges {
!moc.save(&error)
}
}

Updating an object in CoreData is quite similar to creating a new one.
First you have to fetch your object from CoreData using a fetchRequest. After that you can edit the fetched object and update it's attributes.
After that, just save it back using the managedObjectContext.

You can do this, works for me.
if let container = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer{
let context = container.viewContext
let fetchRequest = NSFetchRequest<Account>(entityName: "Account")
let name = self.txtAccount.text
fetchRequest.predicate = NSPredicate(format: "name == %#", name!)
do {
let results = try context.fetch(fetchRequest)
if (results.count > 0){
for result in results{
id = result.id
number = result.number
}
actualNumber = (number + 1)
//Update the account balance
let update = results[0]
update.number = actualNumber
try context.save()
return id
}
}catch let error {
print("Error....: \(error)")
}
You don't have to write 'result[0] as! NSManagedObject' because you have the NSFetchRequest of type Account and the constant "update" knows that.

Related

Swift: CoreData call to specific object ID

I'm discovering new concepts as a fresh developer, I have been trying to understand core data, and run into an issue with a tutorial I've been walking through. I am getting an error when I call an item using the Object ID. The error is - Type 'Person.Type' has no subscript members. it may be because I am just not doing it correctly, or some other reason. I'm sure someone can shine some light on the subject
Here is a function I wrote to get a specific item out of the Data Stack,
func getObjectById(id: NSManagedObjectID) -> Person?{
return context.objectWithID(id) as? Person
}
Here is how I am calling the function
func callFirstObject(){
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let personService = PersonService(context: context)
let firstPerson = personService.getObjectById(Person[0].objectID!)
}
and from there I am just calling callFirstObject() inside a button.
Edit: I have a function to call all of my objects
func getAllObjects() -> [Person]{
return getObject(withPredicate: NSPredicate(value: true))
}
and a function to call all of my objects with a predicate
func getObject(withPredicate queryPredicate: NSPredicate) -> [Person]{
let fetchRequest = NSFetchRequest(entityName: Person.entityName)
fetchRequest.predicate = queryPredicate
do {
let response = try context.executeFetchRequest(fetchRequest)
print("\(response)")
return response as! [Person]
} catch let error as NSError {
// In case of failure
print("There was an error - \(error)")
return [Person]()
}
}
I am just trying to call a specific name in the stack.
If more information is needed, I am glad to provide.
Person has no subscript members because Person is the class name. The subscript, [0], should be called on an array of Person objects.
That could look something like this:
func callFirstObject(){
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let personService = PersonService(context: context)
// Person Array
let persons: [Person] = [person1, person2, person3 ]
let firstPerson = personService.getObjectById(persons[0].objectID!)
}
Edit: I'm kind of confused the logic though. If you have the person already, you have to in order to access the objectID, then I don't see why you need to fetch it again. Can we see some more context around these two methods?
Answering your question from the comment below:
If you want to get all of the records for the Person model you can do as follows:
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Person")
do {
let results =
try managedContext.executeFetchRequest(fetchRequest)
people = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error)")
}
This will give you all of the Person objects stored in Core Data

Fatal error: unexpectedly found nil while unwrapping an Optional value inside an entity

I want an UICollectionViewCell to be deleted when the delete button is tapped:
#IBAction func deleteButtonClicked() {
error here: delegate?.deleteTrigger(clothes!)
}
clothes:
var clothes: Clothes? {
didSet {
updateUI()
}
}
func deleteTrigger:
func deleteTrigger(clothes: Clothes){
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!
let en = NSEntityDescription.entityForName("Category", inManagedObjectContext: context)
if let entity = NSEntityDescription.entityForName("Category", inManagedObjectContext: context) {
let indexPath = NSIndexPath()
//var cat : Category = clothing as! Category
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext: NSManagedObjectContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName: "Clothes")
let predicate = NSPredicate(format: "category == %#", self.selectedCategory!)
fetchRequest.predicate = predicate
var error: NSError? = nil
var clothesArray = managedContext.executeFetchRequest(fetchRequest, error: &error)!
managedContext.deleteObject(clothesArray[indexPath.row] as! NSManagedObject)
clothesArray.removeAtIndex(indexPath.row)
self.collectionView?.deleteItemsAtIndexPaths([indexPath])
if (!managedContext.save(&error)) {
abort()
}
}
Clothes is an entity in Core Data. Does anyone know why I am getting this error? I am trying to delete a collectionViewCell from core data with in a one-to-many relationship. Category is the parent entity and Clothes is the entity within the Category.
You have declared a property clothes:
var clothes: Clothes?
You never gave it any value, so it is nil. Thus when you force-unwrap it by saying clothes!, you crash.
As others said, your value is nil. You need to unwrap the variable before you can do anything with it. Try this:
#IBAction func deleteButtonClicked()
{
if var clothes = clothes
{
delegate?.deleteTrigger(clothes)
}
}

Updating CoreData adds a lot of nil values

I am trying to implement custom class to handle core data operations. It works great when creating new values. However when I want to update values I get nil entries in core data. Here is my code so far
/**
Update all records in given entity that matches input records
- parameters:
- entityName: name of entity to fetch
- updateBasedOnKey: name of key which will be used to identify entries that are going to be udpated
- values: NSMutableArray of all elements that are going to be updated
- important: if object with given updateBasedOnKey doesnt exist it will be created
- returns: nothing
*/
func updateRecord(entity: String, updateBasedOnKey: String, values: NSMutableArray){
let entityDescription = NSEntityDescription.entityForName(
entity, inManagedObjectContext: self.managedObjectContext)
let results = getRecords(entity)
for(elements) in values{
var newEntry = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: self.managedObjectContext)
//Determine whether to add new result or update existing
if(results.count > 0){
for result in results{
let entry = result as! NSManagedObject
if let keyValueToCompare = entry.valueForKey(updateBasedOnKey){
if (keyValueToCompare.isEqual(elements.valueForKey(updateBasedOnKey)) ){
//asign newEntry to result if found in entries
newEntry = entry
}
}
}
}
//update entry with new values
for(key, value) in elements as! NSMutableDictionary{
newEntry.setValue(value, forKey: key as! String)
}
//Try to save resulting entry
do {
try newEntry.managedObjectContext?.save()
} catch {
print(error)
}
}
}
/**
Fetch all records of given Entity in Core Data Model
- parameters:
- entityName: name of entity to fetch
- returns: NSArray of all records in given entity
*/
func getRecords(entity:String) -> NSArray{
let entityDescription = NSEntityDescription.entityForName(entity, inManagedObjectContext: self.managedObjectContext)
let fetchRequest = NSFetchRequest()
fetchRequest.entity = entityDescription
var result = NSArray()
do {
result = try self.managedObjectContext.executeFetchRequest(fetchRequest)
} catch {
let fetchError = error as NSError
print(fetchError)
}
return result
}
I think that problem is somewhere in asigning newEntry a NSManagedObject.
Any ideas how to fix this and get rid of nils?
Thanks in advance
EDIT:
this is actual working code created by implementing Wain suggestion
func updateRecord(entity: String, updateBasedOnKey: String, values: NSMutableArray){
let entityDescription = NSEntityDescription.entityForName(
entity, inManagedObjectContext: self.managedObjectContext)
let results = getRecords(entity)
for(elements) in values{
//set to true if value was already found and updated
var newEntry : NSManagedObject?
//Determine whether to add new result or update existing
if(results.count > 0){
for result in results{
let entry = result as! NSManagedObject
if let keyValueToCompare = entry.valueForKey(updateBasedOnKey){
if (keyValueToCompare.isEqual(elements.valueForKey(updateBasedOnKey)) ){
//asign newEntry to result if found in entries
newEntry = entry
}
}
}
}
if newEntry == nil {
newEntry = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: self.managedObjectContext)
}
for(key, value) in elements as! NSMutableDictionary{
newEntry!.setValue(value, forKey: key as! String)
}
}
}
You're right, the problem is that you're creating and inserting a new object each time. Instead you should be passing the object to update or running a fetch request to find it, then updating it.
It looks like your intention is to fetch, and the new entry should just be a reference, not initialised. So:
var newEntry : NSManagedObject?

I am trying to use Core Data and load a table view with its contents in order and in sections

I get it to load, I get the sections to load, but I cant get the cells in each section to load correctly. It restarts from the beginning in each section effectively duplicating each cell. My entity name is Customer and it holds the following attributes. firstName, lastName, phoneNumber,email, notes, and first. First is the first letter in the last name to use for the sections. I can get it to load, the headers load successfully but as soon as I start adding names with the same first letter in the last name, it starts messing up. Let me know if you need any more code.
override func tableView(tableView: UITableView,
cellForRowAtIndexPath
indexPath: NSIndexPath?) -> UITableViewCell
{
//getSections()
let cell =
tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath!)
let cust = customers[(indexPath?.section)!]
print(indexPath?.row)
let fName = cust.valueForKey("firstName") as? String
print(fName)
let lName = cust.valueForKey("lastName") as? String
print(lName)
cell.textLabel!.text = fName! + " " + lName!
print("Cell: \(cell.textLabel!.text)")
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
context = managedContext
//2
let fetchRequest = NSFetchRequest(entityName:"Customer")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "lastName", ascending: true)]
//3
//var error: NSError?
let fetchedResults = try!
managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if var results = fetchedResults
{
customers = results
}
else
{
print("Could not fetch")
}
tableView.reloadData()
getSections()
}
func getSections()
{
var sections:[String] = []
for(var i=0;i<customers.count;i++)
{
let cust = customers[i]
sections.append(cust.valueForKey("first") as! String)
}
sectionHeadersTotal = sections
let unique = Array(Set(sections))
sectionHeaders = unique.sort()
print(sectionHeaders.count)
//sectionHeaders = unique
//return unique.count
}
Take a look here for the approach to read section-wise data -
http://www.andrewcbancroft.com/2015/03/05/displaying-data-with-nsfetchedresultscontroller-and-swift/
and here
iOS UITableView sections with fetchedResultsController confusion

Swift - Sorting an array retrieved from Core Data [duplicate]

This question already has an answer here:
Sorting Array received from Core Data in Swift
(1 answer)
Closed 8 years ago.
I want to sort the array with usernames that I retrieve from my core data. I retrieve it like this:
override func viewDidAppear(animated: Bool) {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!
let freq = NSFetchRequest(entityName: "User")
let en = NSEntityDescription.entityForName("User", inManagedObjectContext: context)
myList = context.executeFetchRequest(freq, error: nil)!
tv.reloadData()
}
And later I set the cells in my tableview like this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellID:NSString = "cell"
var cell: UITableViewCell = self.tv.dequeueReusableCellWithIdentifier(CellID) as UITableViewCell
if let ip = indexPath as Optional {
var data:NSManagedObject = myList[ip.row] as NSManagedObject
cell.textLabel!.text = data.valueForKeyPath("username") as String!
}
I tried to use the sorting function for in the viewDidAppear function like this:
var sortedList = myList.sorted { $0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
But this gives me an error saying: "Could not find member 'localizedCaseInsensitiveCompare'"
Any suggestions on how to proceed would be appreciated.
executeFetchRequest returns [AnyObject]? and you need to convert this as [NSManagedObjet] array and sort on user key
if let myList = myList as? [NSManagedObject] {
var sortedList = myList.sorted { ($0.valueForKeyPath("user") as String).localizedCaseInsensitiveCompare(($1.valueForKeyPath("user") as String)) == NSComparisonResult.OrderedAscending }
}
else {
println("My List not contains the NSManagedObject array")
}
I like to use a separate function to fetch and sort the array. You sort the array by using an NSSortDescriptor. You can add the capitalization check as a selector for the sort descriptor. I got the separate function idea from this tutorial. I used it and it works for me. I got the idea for the selector idea by referencing this Objective-C code.
var objectives: [NSManagedObject]!
func fetchUsernames() {
static let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!
let freq = NSFetchRequest(entityName: "User")
let sortDescriptor = NSSortDescriptor(key: "username", ascending: true, selector: "localizedCaseInsensitiveCompare:")
freq.sortDescriptors = [sortDescriptor]
do {
let fetchedResults: [NSManagedObject] = try managedContext.executeFetchRequest(freq) as! [NSManagedObject]
if let results: [NSManagedObject] = fetchedResults {
objectives = results
}
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
tableView.reloadData()
}

Resources