I am trying to reinitialize all the data associated with a given cell when this one is deleted.
This is my function deleting the cell (cryptosArray is the array creating the cells):
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
cryptosArray.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
let userDefaults = UserDefaults.standard
let encodedData : Data = NSKeyedArchiver.archivedData(withRootObject: cryptosArray)
userDefaults.set(encodedData, forKey: "cryptosArray")
userDefaults.synchronize()
}
}
And this is a function that saves the amount entered in the textfield present in the cell:
func cellAmountEntered(_ walletTableViewCell: WalletTableViewCell, cryptoPrice: String) {
if walletTableViewCell.amountTextField.text == "" {
return
}
let str = walletTableViewCell.amountTextField.text
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "en_US")
let dNumber = formatter.number(from: str!)
let nDouble = dNumber!
let eNumber = Double(truncating: nDouble)
walletTableViewCell.amountLabel.text = String(format:"%.8f", eNumber)
UserDefaults.standard.set(walletTableViewCell.amountLabel.text, forKey: "\(cryptoPrice)Amount")
walletTableViewCell.amountTextField.text = ""
}
As you can see I am saving walletTableViewCell.amountLabel.text with "\(cryptoPrice)Amount" inside the UserDefaults file where \(cryptoPrice) is a parameter corresponding to the cell.
How can I delete the corresponding "\(cryptoPrice)Amount" key when I delete the cell? I can't figure out how to pass the parameter to commit editingStyle: ..
The basic solution is: create an array of cryptoPrices. Each cell should have a value in there. For example, you can initialize an array with all zeros (if it works for you I don't know). Then if user enters the amount, you delete the zero from the array and .insert a new value there. So then when you do deletion you may easily find your key by indexPath.row.
Hope it helps! But note: this is a very straightforward solution.
I would do this: don't use UserDefaults for such things. It's not a DataBase. Better to use Realm or CoreData. I understand that it's harder but there are many tutorials that may help you with that. Good luck! Hope it helps.
Related
I have a tableView which I created with firebase data.
Here is my load func :
var reservationList = [UserModal]()
private func loadposts() {
reservationList = []
activityIndicator.startAnimating()
Database.database().reference().child("BookReservations").observe(.value) { snapshot in
for case let child as DataSnapshot in snapshot.children {
guard let dict = child.value as? [String:Any] else {
print("Error")
self.activityIndicator.stopAnimating()
return
}
let name = dict["name"] as! String
let date = dict["date"] as? String ?? "nil"
let book = dict["bookName"] as? String ?? "nil"
let FullDate = dict["fullDate"] as? String ?? "nil"
let phoneNumber = dict["phoneNumber"] as? String ?? "nil"
let reservations = UserModal(name: name, dateAndTime: date, choosenBook: bookName , phoneNumber: phoneNumber, tamRandevu: fullDate)
self.reservationList.append(reservations)
print(self.reservationList)
self.activityIndicator.stopAnimating()
self.tableView.reloadData()
}
}
}
extension ReservationListViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return reservationList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reservationCell", for: indexPath) as! reservationCell
cell.bookNameLabel.text = reservationList[indexPath.row].choosenBook
cell.nameLabel.text = reservationList[indexPath.row].userName
cell.dateAndTimeLabel.text = reservationList[indexPath.row].fullDate
cell.phoneButton.setTitle(reservationList[indexPath.row].phoneNumber, for: .normal)
return cell
}
I can get value and show on tableView. But when I try to delete cell :
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
print("Deleted")
activityIndicator.startAnimating()
let reservationTime = reservationList[indexPath.row].fullDate ?? "nil"
print(reservationTime)
if reservationTime != "nil" {
let stationsRef = database.child("BookReservations")
print(reservationList.count)
print("Before deleting Count of reservation list : \(reservationList.count)")
reservationList.forEach { (UserModal) in
print(UserModal.name)
}
stationsRef.child(reservationTime).setValue(nil)
reservationList.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
print("After deleting Count of reservation list : \(reservationList.count)")
reservationList.forEach { (UserModal) in
print(UserModal.name)
}
activityIndicator.stopAnimating()
}else{
activityIndicator.stopAnimating()
print("no reservation found.")
}
}
}
I can succesfully delete data from firebase real time database. But when I delete data , If there is more than one data in the tableView, the non-deleted data will repeat itself.
I try to check my array count it seems good when delete count is decreasing.. I try tableview reload, begin updates and and updates but nothing works. Only works when I refresh tableview with refresh method. This is my print outputs :
Before deleting Count of reservation list : 2
Optional("yakalaaa")
Optional("xyzzz")
After deleting Count of reservation list : 1
Optional("yakalaaa")
this is my tableView which I load with firebase data :
enter image description here
I delete second cell :
enter image description here
and this is the problem :
enter image description here
any suggestions ?
the problem is with these lines:
tableView.beginUpdates()
reservationList.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .bottom)
tableView.endUpdates()
just use instead
reservationList.remove(at: indexPath.row)
tableView.reloadData()
The following code is deleting the wrong entry. I have an array that shows a list of events. in the debugger indexPath.row shows 1 (which is the entry I selected to delete. However when the view refreshes it has deleted the entry 4.
func tableView(_ tableView: UITableView,
commit editingStyle: UITableViewCellEditingStyle,
forRowAt indexPath: IndexPath) {
print(type(of: selectedRecipient))
var eventsOnArray = selectedRecipient?.events?.allObjects
guard let event = eventsOnArray?[indexPath.row] as? Event, editingStyle == .delete else {
return
}
managedContext.delete(event)
do {
try managedContext.save()
print("Deleted")
eventsOnArray?.remove(at: indexPath.row)
getEvents()
self.eventList.reloadData()
} catch let error as NSError {
print("Saving error: \(error), description: \(error.userInfo)")
}
}
My guess here is that the order of your data source is not the same as the array you use in the code we see.
I base this on the fact that you call events?.allObjects which suggests that events is a NSSet which is unordered and calling allObjects on it gives you an array with an undefined order. You need to have an array instead of a set so you can guarantee the same sort order of your objects through different parts of your code.
Adding to #Joakim Danielson’s answer, you would add an extension for your entity to provide for an array instead of a set, like this:
extension Recipient {
var eventsArray: [Event] {
get {
if let eventSet = events as? Set<Event> {
return eventSet.sorted { (item0, item1) -> Bool in
return item0.title < item1.title
}
}
return []
}
}
}
This assumes there is a title attribute in your Event entity so the array returns all events sorted by title. Modify the sorted closure according to your needs.
I'm just starting out and I've used UserDefaults once or twice, never used CoreData, so I'm not sure which to use in this situation. I have a table view, which has cells populated from an empty array, which is is populated by an array of strings, depending on the page title.
In other words, I have a function in viewDidLoad which checks the navigationItem.title, and depending on what it is, populates the empty array with the correct strings. Everything loads well but I need deleted items to remain deleted. Here is my code:
Initializations:
static var places = [String]()
var nycPlaces = ["Central Park", "Rockefeller Center", "Empire State Building"]
var londonPlaces = ["London Eye", "Windsor Castle", "Tower of London"]
var parisPlaces = ["Champs Elysee", "Eiffel Tower", "Catacombs"]
This is the function that populates the table view with the correct array - this is called in viewDidLoad:
func populateCells() {
switch navigationItem.title {
case "New York":
ListController.places = nycPlaces
case "London":
ListController.places = londonPlaces
default:
ListController.places = parisPlaces
}
tableView.reloadData()
}
And to delete, I have:
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == .delete
{
tableView.beginUpdates()
ListController.places.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
}
}
The problem is that when I navigate away from the page then re-load it, the empty array is re-popoulated with the 3 strings, regardless of which ones had been deleted before. How do I ensure that the deleted cell doesn't come back?
Thanks for any help!
You should use UserDefaults if your app is a simple as the code you posted (i.e. you're not fetching the data from some remote server and it really is just a few hard-coded values).
CoreData has a steep learning curve, it is difficult to use, and difficult to use correctly. And it is overkill for the scenario you have presented.
Also note: if you don't need this to persist across your process exiting and restarting you can just create mutable arrays that live somewhere accessible by all your classes (e.g. your AppDelegate, a global singleton class, whatever) and just modify them in place.
Since this is mostly static data, I would advise against having the overhead of CoreData. Instead I would use this approach:
var nycPlaces: [String] {
get {
guard let retval = UserDefaults.standard.object(forKey: "nyc") as? [String] else {
return ["Central Park", "Rockefeller Center", "Empire State Building"]
}
return retval
} set {
UserDefaults.standard.set(newValue, forKey: "nyc")
UserDefaults.standard.synchronize()
}
}
var londonPlaces: [String] {
get {
guard let retval = UserDefaults.standard.object(forKey: "london") as? [String] else {
return ["London Eye", "Windsor Castle", "Tower of London"]
}
return retval
} set {
UserDefaults.standard.set(newValue, forKey: "london")
UserDefaults.standard.synchronize()
}
}
var parisPlaces: [String] {
get {
guard let retval = UserDefaults.standard.object(forKey: "paris") as? [String] else {
return ["Champs Elysee", "Eiffel Tower", "Catacombs"]
}
return retval
} set {
UserDefaults.standard.set(newValue, forKey: "paris")
UserDefaults.standard.synchronize()
}
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == .delete
{
tableView.beginUpdates()
ListController.places.remove(at: indexPath.row)
switch navigationItem.title {
case "New York":
nycPlaces = ListController.places
case "London":
londonPlaces = ListController.places
default:
parisPlaces = ListController.places
}
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
}
}
P.S. - I'd also advise to have this data at a more global scope and to use an enum instead of doing string comparison against your navigation item's title :)
I have a tableview showing data from a Firebase console (JASON structure) like this:
chklst-xxx
- avaliation
-students
-KHZAjBi44eZ8JsaswkKI
-teacher: Paul
-datTime: 09.12.2016 12:25pm
-name: Mary Anne
-mark: 7.5
-preceptor: John
-KHWZlh7aasy78ahsiuKIi0
-teacher: Paul
-datTime: 09.12.2016 12:48pm
-name: Anne Caroline
-mark: 9.4
-preceptor: Peter
These data are shown at a tableview, each one, after node students, except the key. I have one function to delete an especific row, as i do with other apps in CoreData (tableview editingStyle):
// FUNC DELETAR
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
teste.remove(at: indexPath.row)
ref.removeValue() // HERE IS MY DOUBT
tableView.reloadData()
}
}
I can erase the item from the array and from the table, but i just don't know how to do it from Firebase. I have tried other ways with removeValue, but it just didn't work, except an way to erase the whole data, not specifically that row selected.
let ref = FIRDatabase.database().reference().child("avaliation").child("students")
I need some teaching to make this works. Thank you.
Here is my method for Saving this data: (Added 09.12 19:30pm)
func Save(){
let ref = FIRDatabase.database().reference().child("avaliation").child("students").childByAutoId()
referencia.child(“name”).setValue(Name)
referencia.child(“teacher”).setValue(Teacher)
referencia.child("preceptor").setValue(Preceptor)
referencia.child("datTime”).setValue(DateTime)
referencia.child(“mark”).setValue(ResultComFReFC)
}
And here is my Class where I work with the main Array:
import Foundation
import Firebase
struct ChkList {
var name: String?
var teacher: String?
var preceptor: String?
var DateToday: String?
var mark: String?
var key: String!
init(name: String, teacher: String, preceptor: String, mark: String, DateToday: String, key: String = "") {
self.name = name
self.teacher = teacher
self.preceptor = preceptor
self.mark = mark
self.DateToday = DateToday
self.key = key
}
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
let snapshotValue = snapshot.value as! [String: AnyObject]
name = snapshotValue["name"] as? String
teacher = snapshotValue["teacher"] as? String
preceptor = snapshotValue["preceptor"] as? String
mark = snapshotValue["mark"] as? String
DateToday = snapshotValue["DateToday"] as? String
}
func toAnyObject() -> Any {
return [
"name": name,
"teacher": teacher,
"preceptor": preceptor,
"mark": mark,
"DateToday": DateToday ]}}
UPDATE: Tableview EdityingStyle
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let ref = FIRDatabase.database().reference().child("avaliation").child("students").childByAutoId()
teste.remove(at: indexPath.row) //Array delet item
let userKeyA = ref.key
ref.child(userKeyA).removeValue()
tableView.reloadData()
}
}
My suggestion, store the pushKey of the user by using 'getKey' method when you push the user details.
chklst-xxx
- avaliation
-students
-KHZAjBi44eZ8JsaswkKI
-teacher: Paul
-userKey:KHZAjBi44eZ8JsaswkKI <--------------
-datTime: 09.12.2016 12:25pm
-name: Mary Anne
-mark: 7.5
-preceptor: John
Now you can retrieve the 'key` of the user. Suppose you are listing, users in the list view and would want to delete a row.
let ref = FIRDatabase.database().reference().child("avaliation").child("students").child(students.getUserKey()).removeValue();
Let me know, if it works.
Update: I don't know swift. But i'll give u an abstract idea.
When you create a new student, you refer student node and push() right. Like below,
DatabaseReference studentsRef = FIRDatabase.database().reference().child("avaliation").child("students").push();
And then you put your data like,
studentRef.child("teacher").setValue("Paul");
studentRef.child("userKey").setValue(studentRef.getKey()); <------------
I did it, using suggests above and studying and trying a lot.
The way to the method works is really capture the userkey.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let user: ChkList = teste[indexPath.row]
let uk = user.key
print("Userkey: \(uk)")
ref.child(uk!).removeValue()
}
}
This way you will have removed the data from Firebase and the row form tableview.
This answer moves the Firebase delete behavior behind the scenes so as
to not interfere with the normal tableview loading mechanisms. One
benefit is reducing the number of calls to Firebase and removing
timing issues from the internal tableview animation process.
What worked for me is getting the Firebase stuff out of the way. In my case, I collect all the keys that I want. During the delete, I remove the values from my local object, and when leaving the scene I call my cleanup method from viewDidDisappear() to sync the updated data back to Firebase:
func syncFavorites() {
//ref is the leaf for the favorites
//clear existing
ref?.removeValue()
//temp collection
var faves = [String:Any]()
//dataArrayKeys is my collection of keys on self
dataArrayKeys.keys.forEach { (key) in
faves.updateValue("true", forKey: key)
}
ref?.updateChildValues(faves)
}
I handle my tableview's delete action using the normal method.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
if let key = dataArr[indexPath.row]["key"] as? String {
dataArrayKeys.removeValue(forKey: keyToDelete)
dataArr.remove(at: indexPath.row)
}
tableView.deleteRows(at: [indexPath], with: .fade)
self.tableView.reloadData()
}
}
I have no doubt there is probably a better way. This was the simplest possible thing that worked for me.
I have a UITableView which looks like this image
.
When I swipe to delete the record, I can remove it perfectly okay from the array in which it is stored, but I am having difficulties in accessing it in Firebase to delete it there.
My Firebase database structure is as follows for the above screenshot:
-KWc7RTuOe5PefiMM2tL
bodyPart: "Arms"
exerciseName: "Test 1 "
userId: "8rHmyTxdocTEvk1ERiiavjMUYyD3"
-KWcEbpw_f6kxcePY5cO
bodyPart: "Chest"
exerciseName: "Test 2 "
userId: "8rHmyTxdocTEvk1ERiiavjMUYyD3"
-KWcEdUN49QaJIVf0kwO
bodyPart: "Legs"
exerciseName: "Test 3 "
userId: "8rHmyTxdocTEvk1ERiiavjMUYyD3"
-KWcFrMSaLKQRxghGHyT
bodyPart: "Arms"
exerciseName: "Test 4"
userId: "8rHmyTxdocTEvk1ERiiavjMUYyD3"
How can I access the autoId value which is set when it is created e.g "-KWc7RTuOe5PefiMM2tL" so I can remove that child node?
Or alternatively could I access the exerciseName value depending on the UserId that is logged in?
To solve this issue I tried a number of different methods before finally reaching my intended result.
To delete the value, I created a reference to the child node 'userExercises', then ordered it by 'exerciseName' and then .queryEqual(toValue:) the exercise name value which I extracted form the UITableViewCell.
I then removed the snapshot value of this and the example code is below:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
if let exerciseName = exercises[indexPath.row].exerciseName {
let ref = FIRDatabase.database().reference().child("userExercises")
ref.queryOrdered(byChild: "exerciseName").queryEqual(toValue: exerciseName).observe(.childAdded, with: { (snapshot) in
snapshot.ref.removeValue(completionBlock: { (error, reference) in
if error != nil {
print("There has been an error:\(error)")
}
})
})
}
exercises.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .left)
}
}
Following on from what MHDev has already answered:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
if let exerciseName = exercises[indexPath.row].exerciseName {
let ref = FIRDatabase.database().reference().child("userExercises")
ref.queryOrdered(byChild: "exerciseName").queryEqual(toValue: exerciseName).observe(.childAdded, with: { (snapshot) in
snapshot.ref.removeValue(completionBlock: { (error, reference) in
if error != nil {
print("There has been an error:\(error)")
}
})
})
}
exercises.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .left)
}
}
It's a fairly straightforward process:
In general, a datasource for tableViews is an array. That array is built from dictionaries read from Firebase snapshots - or an array of objects built from the snapshots (recommended).
So here's an example that matches your Firebase structure (this was populated from a single node from a snapshot)
class Exercise {
key: "KWc7RTuOe5PefiMM2tL"
bodyPart: "Legs"
exerciseName: "Test 3 "
userId: "8rHmyTxdocTEvk1ERiiavjMUYyD3"
}
Then, when the user swipes row 3 for example, retrieve the Exercise object from the array, row3.
let theObject = ExerciseArray[3]
let parentNode = theObject.key
let ref = rootNode.child(parentNode)
ref.setValue(nil)
and you're done.