简体   繁体   中英

Deleting an object from a table view with CoreData in Swift 3

I'm starting to learn CoreData today and I followed a good tutorial on Ray Wenderlich ( https://www.raywenderlich.com/145809/getting-started-core-data-tutorial )

The finished project ends with a table view, populated with labels the user enters via an Add button in the nav bar.

I also went ahead and put in a delete function since that wasn't in the tutorial:

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{
    if editingStyle == .delete
    {

        guard let appDelegate =
            UIApplication.shared.delegate as? AppDelegate else {
                return
        }

        tableView.beginUpdates()
        companies.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .fade)
        tableView.endUpdates()

        appDelegate.saveContext()
    }
}

However when I navigate to a different view controller then return, the deleted object is there again. I have companies initialized as a managed object at the top of the file:

var companies = [NSManagedObject]()

And I thought

companies.remove(at: indexPath.row)

followed by

appDelegate.saveContext()

in the delete function would remove the object from the data source, but like I said it's still there.

How can I delete the object so when I navigate away from the page and come back, its still deleted, but will come back if I restart the app?

EDIT (code that worked for me)

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{

    let company = companies[indexPath.row]

    if editingStyle == .delete {
        managedContext.delete(company)

        do {
            try managedContext.save()
        } catch let error as NSError {
            print("Error While Deleting Note: \(error.userInfo)")
        }

    }

    // Fetch new data/reload table
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: companyEntity)

    do {
        companies = try managedContext.fetch(fetchRequest) as! [NSManagedObject]
    } catch let error as NSError {
        print("Error While Fetching Data From DB: \(error.userInfo)")
    }

    tableView.reloadData()
}

The above code works so that objects stay deleted if I navigate off the view then come back, however if I relaunch the app, they're still deleted - I'd like the deleted item to return if the app is relaunched.

seems to me you are not delete object from CoreData at all.

companies.remove(at: indexPath.row) - here you just remove object from the fetched data array

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == .delete {
    guard let appDelegate =
        UIApplication.shared.delegate as? AppDelegate else {
            return
    }

    //remove object from core data
    let context:NSManagedObjectContext = appDelegate.managedObjectContext!
    context.deleteObject(companies[indexPath.row] as NSManagedObject)
    context.save(nil)

    //update UI methods
    tableView.beginUpdates()
    companies.remove(at: indexPath.row)
    tableView.deleteRows(at: [indexPath], with: .fade)
    tableView.endUpdates()

    appDelegate.saveContext()
    }
}

However when I navigate to a different view controller then return, the deleted object is there again

Of course, because you did nothing to change the underlaying data model.

objects stay deleted if I navigate off the view then come back, however if I relaunch the app, they're still deleted - I'd like the deleted item to return if the app is relaunched

I'm sorry but why would you want that? Deleting the item means deleting it! That is, it means that it is deleted from the underlying data model, which is the Core Data database. Why would you bring the dead back to life?

It seems to me that your real problem here is that Core Data is a completely inappropriate underlying data model for what you are really trying to do (whatever it is). Core Data is not a beginner technology. I would suggest that you abandon use of it.

I think your first code was closer to what you want.

The most simple behaviour is to fetch only once , delete from Core Data and delete from the table view to update your UI. That's it, no refetch, no reload.

OR

Fetch, delete from Core Data, refetch and reload the table to see the changes in your UI. Pick one!

If you have an array as intermediate you should delete in it either of course.

Now, you don't need to have an array always, (research about fetched results contoller) you can work directly with Core Data, but luckily it's exactly what you need in this case.

My suggestion: fetch Core Data only once to get the initial data, put everything into the array, use the array to work with, make it the data source of your table and delete items in the array and the table only. So your Core Data won't have anything deleted it will just provide the initial values to your working array.

For the inserts, I suppose you want to keep them. Save them to the array, the table and also Core Data if you wish.

One last thing, you are guarding the AppDelegate in the first lines of code. If your App is running, you have an AppDelegate for sure, no way your app would run without it, it controls your apps life cycle. You may have problems getting a context tough (rare but possible). Guard the context instead :

guard let context = (UIApplication.shared.delegate as? AppDelegate)?.managedObjectContext else { 
     return
}

Hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM