So I'm trying to implement a swipe delete of my favorite items off the favorites tab. The swipe implementation works but when I go back to the Favorites Tab, the item deleted reappears. I'm using core data and I know it is not deleting it from core data but not sure how to fix it. This is my file for the favorites tab:
import UIKit
import CoreData
class FavAnimeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var favorites = [NSManagedObject]()
var numAnime = 0
var managedObjectContext: NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
// Do any additional setup after loading the view.
}
//loads every time view appears
override func viewDidAppear(_ animated: Bool) {
load()
}
func load() {
//1
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
//2
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Favorite")
//3
do {
favorites = try managedContext.fetch(fetchRequest)
tableView.reloadData()
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return favorites.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FavTableViewCell") as! FavTableViewCell
let favAnime = favorites[indexPath.row]
cell.favtitleLabel.text = favAnime.value(forKeyPath: "title") as? String
cell.favsumLabel.text = favAnime.value(forKeyPath: "synopsis") as? String
if let poster = favAnime.value(forKey: "poster") as? String{
let posterUrl = URL(string: poster)!
cell.favposterView.af.setImage(withURL: posterUrl)
}
return cell
}
//MARK: Delete favorite items
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
favorites.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
}
if any can help that be great
The changes (in this case Delete) wont be seen affected until you save the state of the NSManagedObjectContext, Helpfully, managed object context has a matching delete() method that will delete any object regardless of its type or location in the object graph. Once an object has been deleted from the context, we can then call saveContext() to write that change back to the persistent store so that the change is permanent.
do {
try managedObjectContext.save()
} catch (let error) {
print(error.localizedDescription)
}
Remember: you must call saveContext() whenever you want your changes to persist.
I Hope I Helped!
Core Data | nsmanagedobjectcontext
Delete from CoreData
func deleteParticularRecord(id:String){
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
// use predicate for delete particular record from coredata.
let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "EnitityName")
let predicate = NSPredicate(format: "id = '\(id)'")
fetchRequest.predicate = predicate
let objects = try! context.fetch(fetchRequest)
for obj in objects {
context.delete(obj as! NSManagedObject)
}
do {
try context.save() // <- remember to put this :)
} catch {
// Do something... fatalerror
}
}
// Call
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
favorites.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
// use id or object id
deleteParticularRecord(id: "_Id_")
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
// Hope its works :)
Related
I'm having problems deleting an item from my Core Data, and having looked at lots of other examples and questions - they all say about deleting an NSManagedObject whereas I'm trying to delete the item at the indexPath.row (which is a String).
var itemsArray = [String]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
What would I put in the following function?
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
}
}
Loading items in Core Data
func loadItems() {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Items")
request.returnsObjectsAsFaults = false
do {
let results = try context.fetch(request)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let product = result.value(forKey: "product") as? String {
self.itemsArray.append(product)
}
}
}
} catch {
print("Error")
}
}
To be able to delete the object you have to use the NSManagedObject as data source
var itemsArray = [Items]()
The loadItems can be reduced to
func loadItems() throws {
let request = NSFetchRequest<Items>(entityName: "Items")
request.returnsObjectsAsFaults = false
itemsArray = try context.fetch(request)
}
Put the do - catch block around the loadItems() call and print the error instance, not a meaningless literal string.
In cellForRow use
let item = itemArray[indexPath.row]
let product = item.product
To delete the item you have to remove it from the data source, then delete the item in the context and then save the context:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let objectToDelete = itemArray[indexPath.row]
itemArray.remove(at: indexPath.row)
context.delete(objectToDelete)
// here add code to save the context
self.tableView.deleteRows(at: [indexPath], with: .fade) // and you have to update the table view
}
}
I got an array that populates a tableview, it works fine when I run the app.
I created a popover with a PickerView to choose one option to sort the TableView data.
I get the user choise in the popover, pass it to the main ViewController, sorted the data and called tableview.reloadData() but nothing happens.
I printed the array after the sort and the array is sorted but I can't saw the changes.
But if I go to other ViewController and came back the data is changed.
Why the changes are not showing when I call the tableview.reloadData().
Here's the code:
var dataModel = DataModel()
var ordenacao = String()
override func viewWillAppear(_ animated: Bool) {
dataModel.loadData()
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
ordenadados(ordem: ordenacao)
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier")
cell.textLabel?.text = dataModel.notas[indexPath.row].titulo
cell.detailTextLabel?.text = dataModel.notas[indexPath.row].datafinal
print("Ordena Tableview")
for nota in dataModel.notas {
print (nota.titulo ?? "")
}
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataModel.notas.count
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
dataModel.notas.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath as IndexPath], with: UITableViewRowAnimation.fade)
dataModel.saveData()
}
EDIT:
func ordenadados(ordem: String){
dataModel.loadData()
if(ordenacao == "Titulo Asc."){
print("Titulo Asc")
dataModel.notas.sort { $0.titulo! < $1.titulo! }
}else if(ordenacao == "Titulo Desc."){
print("Titulo Desc.")
dataModel.notas.sort { $0.titulo! > $1.titulo! }
}
dataModel.saveData()
for nota in dataModel.notas {
print (nota.titulo ?? "")
}
dataModel.loadData()
tableView.reloadData()
}
In the output the array was sorted but in the TableView nothing changed.
Save and Load Data methods:
//save data
func saveData() {
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWith: data)
archiver.encode(notas, forKey: "teste")
archiver.finishEncoding()
data.write(toFile: dataFilePath(), atomically: true)
}
//read data
func loadData() {
let path = self.dataFilePath()
let defaultManager = FileManager()
if defaultManager.fileExists(atPath: path) {
let url = URL(fileURLWithPath: path)
let data = try! Data(contentsOf: url)
let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
notas = unarchiver.decodeObject(forKey: "teste") as! Array
unarchiver.finishDecoding()
}
}
I am doing a sample app: one add button to add the phonenumber from the contacts; when i swipe the row, it will appear delete and I could delete the data in the row and at the same time, the data in the coredata there is also gone.
I did the following code: adding function works well with tableview and core data. But for the delete one, i could delete the data in the tableview but it seems the data is not deleted from the core data. However, it didn't give me an error message. I just don't know what goes wrong and why I could not delete it in the core data.Please help. Thank you.
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
if (editingStyle == UITableViewCellEditingStyle.Delete){
let item = items[indexPath.row]
managedContext.deleteObject(item)
items.removeAtIndex(indexPath.row)
tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// fetching the contacts from the core data
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Contact")
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
items = results as! [NSManagedObject]
} catch {
print("there is an error")
}
}
You will have to save your managedContext for your changes (delete) to reflect.
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
if (editingStyle == UITableViewCellEditingStyle.Delete){
let item = items[indexPath.row]
managedContext.deleteObject(item)
do {
try managedContext.save()
} catch let error as NSError {
print("\(error)")
}
items.removeAtIndex(indexPath.row)
tableView.reloadData()
}
}
We successfully implemented saving and reading from Core Data but we have problems with removing data from Core Data when random cell is deleted.
How to remove data from Core Data when random cell is deleted from tableView??? We typed some code like removing last index from array but it does not work...
2cd Vc
import UIKit
import CoreData
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var item = [NSManagedObject] ()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
}
func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int
{
return item.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let person = item[indexPath.row]
cell.textLabel!.text = person.valueForKey("username") as? String
cell.detailTextLabel?.text = person.valueForKey("passwords") as? String
return cell
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
// WHAT TO TYPE HERE TO REMOVE DATA FROM CORE DATA WHEN RANDOM CELL IS DELETED
let context: NSManagedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
do {
try context.save()
} catch _ {
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Users")
do {
let results =
try managedContext.executeFetchRequest(fetchRequest)
item = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
I found it working like this
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
switch editingStyle {
case .Delete:
// remove the deleted item from the model
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext
context.deleteObject(item[indexPath.row] as NSManagedObject)
item.removeAtIndex(indexPath.row)
do{
try context.save()
}catch{
print("Error, data not saved!")
}
//tableView.reloadData()
// remove the deleted item from the `UITableView`
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
default:
return
}
I am a bit stuck here. Tried many posts and ideas here from Stack, but could not get it to delete content from the table view. Neither deleting a record from COREDATA or a simple testing array. Apart from that the code works fine, lists data and displays in the tableview.
This is the error I am getting:
2015-07-09 21:49:24.881 PlaygroundApp[36985:1319445] * Assertion
failure in -[UITableView _endCellAnimationsWithContext:],
/SourceCache/UIKit_Sim/UIKit-3347.44/UITableView.m:1623 2015-07-09
21:49:24.889 PlaygroundApp[36985:1319445] * Terminating app due to
uncaught exception 'NSInternalInconsistencyException', reason:
'Invalid update: invalid number of rows in section 0.
import Foundation
import UIKit
import CoreData
class MyTableViewController: UIViewController, UITableViewDelegate {
var cellCount = Int()
var selectedCell = Int()
var totalResults = Int()
// THIS VARIABLE IS INITIALISED BY COREDATA FETCHREQUEST THEN USED TO POPULATE CELLS
var recipients = [Float]()
//THIS IS JUST A TEST VAR TO TEST THE TABLE VIEW DELETE
var recipientsTeste = [1,2,3,4,5,6,7,8]
override func viewDidLoad() {
super.viewDidLoad()
// FETCH COREDATA
var appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var context: NSManagedObjectContext = appDel.managedObjectContext!
var request = NSFetchRequest(entityName: "ConcreteEntity")
request.returnsObjectsAsFaults = false
var fecthResults = context.executeFetchRequest(request, error: nil)!
//PRODUCE ARRAY FROM COREDATA
if fecthResults.count > 0 {
var saydir = fecthResults.count - 1
for (var i=0; i < fecthResults.count; i++) {
let match = fecthResults[i] as! NSManagedObject
var tela = match.valueForKey("costAt") as! Float
println(tela)
recipients.append(tela)
}
} else {
}
//DEFINE # OF RECORDS AT COREDATA
totalResults = fecthResults.count
}
// NUMBER OF CELLS BASED ON # OF RECORDS AT COREDATA
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return totalResults
}
// TRANSFER COREDATA RECORDS TO CELLS
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
// LABEL CELL
cell.textLabel?.text = "\(recipientsTeste[indexPath.row])"
return cell
}
// IDENTIFY CLICKED CELLS
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedCell = indexPath.row
println(selectedCell)
// DEFINE AN ACTION LATER TO USE THE SELECTED ROW AND CLOSE VIEW
if indexPath.row == 0 {
println("I clicked ZERO")
navigationController?.popViewControllerAnimated(true)
}
}
// ENABLE EDIT
func tableView(tableView: UITableView!, canEditRowAtIndexPath indexPath: NSIndexPath!) -> Bool {
return true
}
// EDIT CELL
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
//// I GET THIS INFO PRINTED CORRECTLY. IT CALLS THE METHOD FINE.
// println("delete indexPath \(indexPath.row)and \(recipients[indexPath.row])")
//****************** THIS IS THE BIT THAT DOESNT WORK **********************
switch editingStyle {
case .Delete:
var appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var context: NSManagedObjectContext = appDel.managedObjectContext!
var request = NSFetchRequest(entityName: "ConcreteEntity")
request.returnsObjectsAsFaults = false
var fecthResults = context.executeFetchRequest(request, error: nil)!
context.deleteObject(fecthResults[indexPath.row] as! NSManagedObject)
fecthResults.removeAtIndex(indexPath.row)
context.save(nil)
tableView.reloadData()
// remove the deleted item from the `UITableView`
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
default:
return
}
// *************************************************************************
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You are reloading the table, which will remove the rows you are trying to delete, then you try and delete the rows again. Remove the call to tableView.reloadData().
tableView.reloadData() // Delete this line
// remove the deleted item from the `UITableView`
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
Edit: You should look into NSFetchedResultsController its made for this kind of thing.