I am creating a online ordering service for practice project. Two things I've read into recently is the editing tableview function. when I go to swipe to delete a item it won't take the right item out of the database. Also, if there is only one item in the array and I try to delete it it will make it crash. Anyone have any idea whats going on with this?
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
orderNumber.remove(at: indexPath.row)
itemsArray.remove(at: indexPath.row)
priceArray.remove(at: indexPath.row)
quantityArray.remove(at: indexPath.row)
databaseRef.child("Users/\((authRef.currentUser?.uid)!)/Order/\(orderNumber.remove(at: indexPath.row))").removeValue { (error, ref) in
if error != nil {
print(error?.localizedDescription as Any)
} else {
}
}
}
Looks like you just need to check the array count before deleting it:-
if orderNumber.count > indexPath.row {
orderNumber.remove(at: indexPath.row)
}
if itemsArray.count > indexPath.row {
itemsArray.remove(at: indexPath.row)
}
if priceArray.count > indexPath.row {
priceArray.remove(at: indexPath.row)
}
if quantityArray.count > indexPath.row {
quantityArray.remove(at: indexPath.row)
}
Apparently, you're removing from orderNumber twice. First you're removing here:
orderNumber.remove(at: indexPath.row)
Then you're removing here:
child("Users/\((authRef.currentUser?.uid)!)/Order/\(orderNumber.remove(at: indexPath.row))")
Any specific reason you're using multiple collections?
Related
I'm trying to implement the functionality to delete some rows from a table view and not others. In this case, everything in section 0 should not be deletable (so not swipe to delete either), but everything in section 1 should be able to. How can I implement this? Currently section 0 rows cannot delete, but when the user swipes, the delete action still appears.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (indexPath.section == 0){
// dont delete the rows
} else {
if (editingStyle == .delete){
let fetchRequest: NSFetchRequest<Conversions> = Conversions.fetchRequest()
do {
let result = try PersistenceService.context.fetch(fetchRequest)
// Delete from CoreData and remove from the array
if (result.contains(allConversions[indexPath.row])){
PersistenceService.context.delete(allConversions[indexPath.row])
allConversions = allConversions.filter { $0 != allConversions[indexPath.row] }
PersistenceService.saveContext()
self.tableView.reloadData()
}
} catch {
print(error.localizedDescription)
}
}
}
UITableView has a method exactly for this purpose called canEditRowAt. You just need to return false when indexPath.section == 0
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return indexPath.section != 0
}
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()
}
}
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.
Sorry for the bad title, and I feel really dumb for asking this question.
I'm deleting a cell from my table and the first block of code runs just perfect, but when shortening the line by creating a variable the code crashes. Why?
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
emojisByCategories[indexPath.section].remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
This small change will cause a "libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)" error
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
var emojis = emojisByCategories[indexPath.section]
emojis.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
Build on Xcode Version 9.0 (9A235)
You have an array of arrays. Array in Swift is a struct which is a value type. When you assign an array to another variable, you are actually creating a copy of the array.
When you do:
var emojis = emojisByCategories[indexPath.section]
emojis.remove(at: indexPath.row)
you are modifying the copy in emojis. Nothing in emojisByCategories is actually being changed as a result of this code.
So now you tell the table view that a row has been deleted but your data source hasn't actually changed at all so you get the crash telling you about an invalid number of rows in a section.
The line:
emojisByCategories[indexPath.section].remove(at: indexPath.row)
doesn't have the same problem because you are not making a copy of any array and the values in emojisByCategories are being updated as expected.
You can make your second set of code work by adding a third line:
var emojis = emojisByCategories[indexPath.section]
emojis.remove(at: indexPath.row)
emojisByCategories[indexPath.section] = emojis
That 3rd line updates emojisByCategories with the updated emojis array so now your code won't crash.
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!")
}
}