Delete Object from CoreData (error) - ios

I'd like to delete an object from core data. I tried this code but it's not working. What's wrong? I'm working with Swift and Xcode 6.1.1.
Code:
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
var daten = [TaskModel]() //CoreData Entity
managedObjectContext!.deleteObject(daten[indexPath.row])
appDelegate.saveContext()
let fetchRequest = NSFetchRequest(entityName: "TaskModel")
daten = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as [TaskModel]
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Right)

daten is an empty array.
Therefore, daten[x] will crash.
In your case, x is indexPath.row, but it will crash with any value.
If you try to access an element of an empty array by index, you will get the above error.
To get an object from your fetched results controller and delete it,
let task = self.fetchedResultsController.objectAtIndexPath(indexPath) as TaskModel
self.fetchedResultsController.managedObjectContext.deleteObject(task)
self.fetchedResultsController.managedObjectContext.save(nil)
The standard implementation of the fetched results controller from the Xcode template will take care of updating your table view automatically, you even get animation for free.
If you are not using a NSFetchedResultsController, stop here and refactor.

Related

Exc_bad_access NSManagedObjectContext(_NSInternalAdditions) _disposeObjects

I developing swift application which included Core data and Alamofire. I'm struggling to solve this issue for almost a day.
Below is a screen shot after app is crashing and it happens randomly. I know this information might not enough, please mention if anyone need more details. At least guide me to track this issue.
EDIT:
Its mainly crashing after this method reload tableview function.
func reloadTable(){
print("reloadTable called")
DispatchQueue.main.async {
let appDel:AppDelegate = (UIApplication.shared.delegate as! AppDelegate)
let context:NSManagedObjectContext = appDel.managedObjectContext
let request = NSFetchRequest < NSFetchRequestResult>(entityName: "Jars")
request.returnsObjectsAsFaults = false;
let sortDescriptor = NSSortDescriptor(key: "order", ascending: true)
let sortDescriptors = [sortDescriptor]
request.sortDescriptors = sortDescriptors
self.navigationController!.view.backgroundColor = UIColor.white
self.jarlist = NSArray()
do{
self.jarlist = try context.fetch(request) as NSArray
print("Check 11")
}catch{
}
self.ArrayPopulator("")
print("Check 12")
self.JarsTable.reloadData()
print("Check 13")
}
}
So looking at your stack trace it looks like you are using your managed context on thread 2.
A NSManagedObjectContext cannot be used on multiple threads, you must have one for every thread (queue) that you want to use it on. It is only safe to pass object identifiers between threads.
Can you double check that the objects your are wanting to dispose of in your NSOperation were retrieved on the same operation queue that you are trying to remove them on?
You will want to pass only the ids to the operation and have it rehydrate them from it's own managed object context.
EDIT: Looking closer this appears to be an internal queue managed by CoreData, so possibly this is not your doing?

Issue with adding multiple objects to Core Data (Swift)

OverView
I continue to run into issues with adding multiple values to my Core Data entity. All i need to do is simply add 6 string-value items from a text field into Core Data. Specific examples/critique of my code would be very appreciated, as i am nearing mental break down with this issue.
The Issue
The first time i ran this, i tested it by saving only the first line (the product name) to core data and then printing it off. It worked perfect. After that, i tried the same method for all of them, and then tried printing. My program would set a breakpoint next to the "entity1.setValue(three, forKey: "serialNo")."
I also get a message in the debugger area that says (lldb).
If i try to step through the breakpoint, everything just prints out as 'nil'.
CODE
#IBAction func saveButton(sender: AnyObject) {
let appDel: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context:NSManagedObjectContext = appDel.managedObjectContext
let entity1 = NSEntityDescription.insertNewObjectForEntityForName("UsedInfo", inManagedObjectContext:context) as NSManagedObject
let one = pickerTextField.text
let two = modelName.text
let three = serialNo.text
let four = YOM.text
let five = engineHours.text
let six = locationOfMachine.text
entity1.setValue(one, forKey: "product")
entity1.setValue(two, forKey:"modelName")
entity1.setValue(three, forKey:"serialNo")
entity1.setValue(four, forKey:"yom")
entity1.setValue(five, forKey:"engineHours")
entity1.setValue(six, forKey:"location")
print(entity1.valueForKey("product"))
print(entity1.valueForKey("modelName"))
print(entity1.valueForKey("serialNo"))
print(entity1.valueForKey("yom"))
print(entity1.valueForKey("engineHours"))
do {
try context.save()
}
catch {
print("error")
}
}

CoreData - fill array with entity attribute in swift

I'm pretty new to Swift/Xcode and I'm faced with a problem of trying to place CoreData attribute values into an array.
The app is a simple shopping list with an entity with 5 attributes:
entity is "Item"
image Binary data
name String
note String
qty String
status Boolean
The user creates a new shopping item which is displayed in a UITableVIewController (so far I'm fine)
I have to data loading into cells correctly.
I also want to display the total quantity(entity "qty") of items in a label.
I can't seem to put each qty into an array so that I can add them together to display them. There seems to be plenty of resources for Objective C but not much out there for swift.
These are what I've been looking at:
Filling an array with core data attributes
CoreData to Array in Swift
Swift: Fetch CoreData as Array
coredata - fetch one attribute into an array
UITableViewController Code:
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var frc2 : NSFetchedResultsController = NSFetchedResultsController()
func fetchRequest2() -> NSFetchRequest {
let fetchRequest2 = NSFetchRequest(entityName: "Item")
let sortDescriptor2 = NSSortDescriptor(key: "name",ascending:true)
fetchRequest2.sortDescriptors = [sortDescriptor2]
return fetchRequest2
}
override func viewDidLoad() {
super.viewDidLoad()
//set frc
frc2 = getFRC()
frc2.delegate = self
do {
try frc2.performFetch()
} catch {
print("failed to perform initial fetch request")
return
}
self.tableView.reloadData()
}
Any help would be greatly appreciated.

Why is UICollectionView reloadData crashing my app?

I am using uicollectionview to display some photos and I have a button that allows the user to delete the selected photo.
It works perfectly unless I try to delete the last photo in the array of photos being used to populate the uicollectionview. Ie if there are 5 photos then there will be a problem if a user removes the 5th photo but not the 1st, 2nd, 3rd or 4th. When I try to delete the 5th photo it crashes on reloadData() with the following error
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete item 4 from section 0 which only contains 4 items before the update'
I don't understand why this would happen... The error even mentions "attempt to delete" but I never actually told it I was deleting anything. I just changed the source and then asked it to update. Also in the error message it says the section only contains 4 items before the update when their were actually 5 photos. Why?
A little bit more info about what I'm doing and how (Using Swift)...
I've got a ProgressPhotoSheetClass from which I have instantiated the object progressPhotoSheet
this progressPhotoSheet object has an array of photos and the photos can have priorities such as photo id, title etc
in my number of items in section I use
var numPics: Int = progressPhotoSheet.progressPhotos.count
return numPics
in my cellForItemAtIndexPath I use
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! PhotoHolder
cell.photoTitle.text = progressPhotoSheet.progressPhotos[indexPath.row].photoName
...etc..
When deleting the item I use
progressPhotoSheet.deletePhoto(progressPhotoSheet.progressPhotos[indexPath.row].photoId)
which deletes the photo from the device and from my core data and then reloads progressPhotoSheet.progressPhotos from using the modified core data so it is now exactly how it was before but without the deleted photo.
I then call
self.collectionView.reloadData()
Which should update the UICollectionView for the new data
I could understand if it felt there was a mismatch between what should be in the collection view and what is in the datasource if I were using
self.collectionView.deleteItemsAtIndexPaths(indexPaths)
because that would be saying ignored to get them to match we need to delete one item - here there is a possibility something could mismatch.. But surely using self.collectionView.reloadData() it doesn't matter what changes were made it should just look at what data is there now and update the UICollectionView accordingly....
So my question is... Why am I getting this error and what should I do to fix things so I don't get it?
Edit to include more info
Here is my telephoto Code
func deletePhoto(photoId: Int) {
// Set up Core Data Managed Object Context
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
// Fetch correct photo
let fetchRequest = NSFetchRequest(entityName: "CDProgressPhoto")
fetchRequest.predicate = NSPredicate(format: "photoId = %#", String(photoId))
// Save
if let fetchResults = managedContext.executeFetchRequest(fetchRequest, error: nil) as? [NSManagedObject] {
if fetchResults.count != 0{
// Will only be one photo with this photo id
var photo = fetchResults[0]
photo.setValue(true, forKey: "toDelete")
// Save the object
var error: NSError?
if !managedContext.save(&error) {
println("Could not save \(error), \(error?.userInfo)")
}
}
}
// Reload from core data
self.loadPhotoSheetFromCoreData()
}
self.loadPhotoSheetFromCoreData() then empties progressPhotoSheet.progressPhotos before getting the new data from core data... Code below...
private func loadPhotoSheetFromCoreData() {
if(self.hasPhotoSheet()) {
// Clear existing photos
self.progressPhotos = []
// Set up Core Data Managed Object Context
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let request = NSFetchRequest(entityName: "CDProgressPhoto")
let predicate1 = NSPredicate(format: "photoSheetId == %#", String(self.sheetId))
let predicate2 = NSPredicate(format: "toDelete == %#", false)
request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false) as NSSortDescriptor]
var predicatesArray: [NSPredicate] = [predicate1, predicate2]
//predicatesArray.append(predicate1)
request.predicate = NSCompoundPredicate.andPredicateWithSubpredicates(predicatesArray)
let existings = managedContext.executeFetchRequest(request, error: nil)
let existingPhotos: [CDProgressPhoto] = existings as! [CDProgressPhoto]
// for each photo make a ProgressPhoto object and add to progress photos array
for photo in existingPhotos {
var newPhoto: ProgressPhoto = ProgressPhoto()
newPhoto.photoSheetId = Int(photo.photoSheetId)
newPhoto.photoId = Int(photo.photoId)
newPhoto.photoName = photo.photoName
newPhoto.date = Int(photo.date)
newPhoto.url = photo.url
newPhoto.filename = photo.filename
newPhoto.height = Float(photo.height)
newPhoto.width = Float(photo.width)
newPhoto.selected = false
self.progressPhotos.append(newPhoto)
}
}
}
As you can see the photo isn't actually deleted at this point I just set a toDelete flag to true and then only re load items where toDelete is set to false. The photos are deleted later asynchronously depending on network connection etc because they are also stored on a server for use on the main website.
Have you tried calling invalidateLayout() on the collectionView? That might help incase your view is empty i.e. 0 elements are present.

Core Data array

I am using the code below to fetch the objects in an array. In my program, I have an array displayed in a tableview and when a cell is tapped, it leads to another array displayed in a tableview. The user can add cells in both of these tableviews. What is happening is that when I create new rows in my second tableview, go back, and tap the same cell that got me there, I notice that the objects I created are not there (they were reassigned to another cell). I believe that the problem lies in the line: routines = results. What I think is happening, is that when I tap back in my second view, the line routines = results is called again, and because results is by nature unordered, it messes up the order of my previously established routines array.
var routines = [NSManagedObject]()
override func viewWillAppear(animated: Bool) {
self.navigationItem.leftBarButtonItem = self.editButtonItem()
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName: "Routine")
var error: NSError?
let fetchedResults = managedContext.executeFetchRequest(fetchRequest, error: &error) as! [NSManagedObject]?
if let results = fetchedResults {
routines = results
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
I agree with the comment from Jonathan. You are just making work for yourself holding onto everything in arrays. Use the tools that are designed to work with both Core Data and UITableView controllers and you will have a much easier (and more maintainable) time.

Resources