So I'm trying to delete data from a tableView, which It will delete the cell at the row, but it won't delete the information from coreData, causing it to load again when I call a .reloadData(). I'm really new to coredata and I don't know how to select a specific Recipe item that I make.
Here's where I handle the delete:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
// handle delete (by removing the data from your array and updating the tableview)
recipes.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
}
}
Here's how I am creating the items in coreData, if this helps. Also, I have a Git repository here if anyone's willing to look that deep
#IBAction func createNewRecipeButton(_ sender: Any) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let newRecipe = Recipe(context: context)
newRecipe.title = recipeTitleTextBox.text
newRecipe.instructions = recipeInstructionsTextBox.text
newRecipe.time = recipeTimeTextBox.text
(UIApplication.shared.delegate as! AppDelegate).saveContext()
navigationController!.popToRootViewController(animated: true)
}
Your current removal method merely deletes the recipe from the storage array. You need to tell the context to get rid of it as well…
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
let recipe = recipes.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
guard let moc = recipe.managedObjectContext else { return }
moc.delete(recipe)
moc.processPendingChanges()
}
}
You might also want to look into using a NSFetchedResultsController. There are several tutorials around.
Related
Try to delete tableViewCell in swift XIB. but there is error cannot remove models
viewModel :
private var combinedItems: [combinedInboxModel] = []
func get(){} // Add combinedItems data
View Controller
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
viewModel.combinedItems.remove(at: indexPath.row) // No exact matches in call to instance method 'remove' in swift
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
Why there is error remove here?
I am trying to delete data in my Firebase database and then listen for .childRemoved so I can update the TableView. I originally tried to remove the object from the array using the code below but got an index out of range error in cellForRowAt in indexPath.row.
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let post = posts[indexPath.row]
posts.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
DataService.ds.REF_POSTS.child(post.postKey).removeValue()
DataService.ds.REF_USERS.child("\(uid!)").child("posts").child("\(post.postKey)").removeValue()
//print("deleted post \(deletedPost)")
} else if editingStyle == .insert {
}
}
So, I decided to try to delete the Firebase data first and then listen at .childRemoved with the following combination of code (I commented out the removal of array objects in the editingStyle code).
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let post = posts[indexPath.row]
//posts.remove(at: indexPath.row)
//tableView.deleteRows(at: [indexPath], with: .fade)
DataService.ds.REF_POSTS.child(post.postKey).removeValue()
DataService.ds.REF_USERS.child("\(uid!)").child("posts").child("\(post.postKey)").removeValue()
//print("deleted post \(deletedPost)")
} 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.
}
}
Then, I created a listener at .childRemoved in ViewDidLoad with the following.
newPost.observe(.childRemoved, with: { (snapshot) in
//print("CHILD REMOVED")
//print("\(self.posts)")
//print("\(snapshot.postKey)")
let postId = snapshot.key
if let indexNew = self.posts.firstIndex(where: {$0.postKey == postId}){
self.posts.remove(at: indexNew)
for n in 0...self.posts.count - 1 {
print("POST \(n) - \(self.posts[n])")
}
print("REMOVE INDEX - \(indexNew)")
print("POSTS AFTER REMOVAL - \(self.posts)")
self.feedTableView.reloadData()
}
}, withCancel: nil)
I am still receiving the index out of range error at cellForRowAt on indexPath.row. Am I on the right track with the second method? If so, what am I doing wrong to cause the out of range error?
EDIT: I am adding my cellForRowAt where I am getting the index out of range error and most updated commit editingStyle. I'm not sure how I am running over the array since the debug statements print
INDEX OF POST ARRAY - 2
POSTS: SocialSpirit.Post
POSTS AFTER DELETE [SocialSpirit.Post, SocialSpirit.Post, SocialSpirit.Post]
INDEX OF POST ARRAY - 0
Fatal error: Index out of range
And here is the code:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let post = posts[indexPath.row]
DataService.ds.REF_POSTS.child(post.postKey).removeValue()
DataService.ds.REF_USERS.child("\(uid!)").child("posts").child("\(post.postKey)").removeValue()
posts.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
print("POSTS AFTER DELETE \(self.posts)")
//print("deleted post \(deletedPost)")
} 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.
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("INDEX OF POST ARRAY - \(indexPath.row)")
print("POSTS: \(posts[indexPath.row])") //Index out of range error here
let post = posts[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as? PostCell{
if let img = FeedViewController.imageCache.object(forKey: post.imageUrl as NSString) {
cell.configureCell(post: post, img: img)
} else {
cell.configureCell(post: post)
}
return cell
} else {
return PostCell()
}
}
The function this way removes the index error and correctly updates the tableView
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
let post = posts[indexPath.row]
DataService.ds.REF_USERS.child("\(uid!)").child("posts").child("\(post.postKey)").removeValue()
}
}
I want to refresh my conversations (table view cells) after deleting a conversation through a swipe action sheet.
I tried to reload the table view after deleting the data but it doesn't work. Also with a async.
// Swipe Action Sheet solution
func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
{
// Write action code for the Flag
let deleteAction = UIContextualAction(style: .normal, title: "Löschen", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
let ref = Database.database().reference()
ref.child("users").child((self.currentUser?.uid)!).child("conversations").child((self.items[indexPath.row].user.uid)!).removeValue()
DispatchQueue.main.async {
self.tableView.reloadData()
}
success(true)
})
deleteAction.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [deleteAction])
}
Thanks in advance for your help!
You have to reload data in completion block of removeValue method if there isn't any error. Also before you reload data you have to remove item from items array
.removeValue { error,_ in
if error == nil {
self.items.remove(at: indexPath.row)
self.tableView.reloadData() // self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
success(error == nil)
}
To delete row using swipe, you can use
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
And because you not reload table view, that will look much better
How can I get the marked key then remove it from UITableView swift 3 ?
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
self.grocery.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
To remove you can simply set the value as nil to remove it from firebase. After you've called the remove function then make sure to remove the row from the tableView.
func removeObjectFromFireBase(userKey: String, removeThisGrocery: String) {
let ref = Database.database().reference()
let groceryRef = ref.child("Users").child(userKey).child("Grocery")
groceryRef.child(removeThisObject).setValue(nil)
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: .destructive, title: "DELETE") { (rowAction, indexPath) in
//Call the removeFromFirebase function with the required parameters
removeObjectFromFirebase(userKey: userKey, removeThisGrocery: groceryKey)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
deleteAction.backgroundColor = UIColor.red
return [deleteAction, addAction]
}
You can remove any data from Firebase as following:
let ref = Database.database().reference()
let groceryRef = ref.child("Users").child(userKey).child("Grocery")
groceryRef.removeValue()
Bit of a beginner here so probably shouldn't be trying core data stuff but anyway, I would like to be able to delete a row by swiping. I have done this but it doesn't save the deleted cells and they come back again. I am using a xcdatamodeld file. If anyone can tell me how to save the deleted files to core data that would be great!
Here is my saving data code:
inputAlert.addAction(UIAlertAction(title: "Save", style: .default, handler: { (action:UIAlertAction) in
let taskTextField = inputAlert.textFields?.first
let descTextField = inputAlert.textFields?.last
if taskTextField?.text != "" && descTextField?.text != "" {
taskItem.task = taskTextField?.text
taskItem.desc = descTextField?.text
do {
try self.managedObjectContext.save()
self.loadData()
}catch {
print("Could not save data \(error.localizedDescription)")
}
}
Here is the code I have so far for the deleting:
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
tasks.remove(at: indexPath.row)
tableView.reloadData()
}
}
Don't let the fact that you are a beginner keep you from using this powerful persistent store friend. CoreData is a big topic, books have been written on it, but it is something that works very well once you understand the core concepts of programming with it. It looks like you want to remove data that is populated in a UITableView and then save what you deleted into CoreData. Let's break down the steps and give you some examples to work with in your own project.
1) Remove data from your UITableView's datasource
2) Save NSManagedObject to CoreData
3) Delete row from UITableView
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
// 1)
let task = tasks.remove(at: indexPath.row)
// 2)
saveToCoreData(task: task)
// 3)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
}
}
// Assuming your task is of type "`Task`". You should put whatever data type your task object actually is.
func saveToCoreData(task: Task) {
// Insert Into CoreData (very important)
let managedObject = NSEntityDescription.insertNewObject(forEntityName: "RemovedTask", into: self.context)
// assign values
managedObject.value = task.value
// Save CoreData Context (also, very important)
do {
self.context.save()
}
catch {
print("Could not save CoreData!")
}
}