how to delete cell and data in firebase and swift 3 - swift

I'm trying to delete the data from firebase with no luck so far. This is the code I'm using, can anyone give me a hand with it please.
class TableViewController: UITableViewController {
var ref: FIRDatabaseReference?
var grocery = [Grocery]()
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData() {
let uid = FIRAuth.auth()?.currentUser?.uid
FIRDatabase.database().reference().child("Users").child(uid!).child("Grocery").observe(.childAdded) { (snspshot: FIRDataSnapshot) in
if let dict = snspshot.value as? [String: Any] {
let Items = dict["Item"] as! String
let Quintities = dict["Quintities"] as! String
let Done = dict["Done"] as! Bool
let themBe = Grocery(Items: Items, Quintitiess: Quintities, Dones: Done)
self.grocery.append(themBe)
print(themBe)
self.tableView.reloadData()
}
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return grocery.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TasksTableViewCell") as! TasksTableViewCell
cell.titleLabel?.text = grocery[indexPath.row].Item
cell.numLabel?.text = grocery[indexPath.row].Quintities
return cell
}
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)
}
}
----------
import Foundation
class Grocery {
var Item: String
var Quintities: String
var Done: Bool
init(Items: String, Quintitiess: String, Dones: Bool) {
Item = Items
Quintities = Quintitiess
Done = Dones
}
}

You are only deleting data for your UITableView. The logic that you need is to delete from your UITableView and Fireabase Database. As the firebase docs says you can either call removeValue, or setValue to nil or updateChildValues.
To make the deletion easier, I'd save the key of the object where the data is saved (snapshot.keys), so when you want to delete you can just get that key and perform actions.

Related

Is there a way that I can use my "data," "values," and "rowNumber" constants from my "func startObservingDB()" in my "tableView" functions?

I know the issue is regarding scope; I just dont know if theres an easy fix I can do without changing my code much. but open to anything
import UIKit
import Firebase
import FirebaseAuth
import FirebaseFirestore
class AdminViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet var custodianRunReportsTableView: UITableView!
var dbRef: DatabaseReference!
var data = [String]()
override func viewDidLoad() {
super.viewDidLoad()
startObservingDB()
custodianRunReportsTableView.delegate = self
custodianRunReportsTableView.dataSource = self
// Do any additional setup after loading the view.
}
// Gets users' names from Cloud Firestore Database
func startObservingDB() {
let db = Firestore.firestore()
let namesDocumentRef = db.collection("Users").document("Names")
namesDocumentRef.addSnapshotListener { DocumentSnapshot, error in
guard let document = DocumentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
let values = data.values
let rowNumber = data.count
print("Current data: \(data)")
print("Current data has the values: \(values)")
print("Current data totals \(data.count) items.")
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped me!")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rowNumber
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = values[indexPath.row]
print("Names in cell: \(values)")
print("\(data)")
return cell
}
}
UPDATED CODE:
This is updated after an answer to the original post. The code no longer has the unresolved identified error; however, the table view does not display any cell text and is empty.
class AdminViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var custodianRunReportsTableView: UITableView!
var valuesArray:[String] = []
var data:[String] = []
var namesDocumentRef:DocumentReference!
override func viewDidLoad() {
super.viewDidLoad()
startObservingDB()
custodianRunReportsTableView.delegate = self
custodianRunReportsTableView.dataSource = self
}
// Gets users' names from Cloud Firestore Database
func startObservingDB() {
var namesDocumentRef:DocumentReference!
let db = Firestore.firestore()
namesDocumentRef = db.collection("Users").document("Names")
namesDocumentRef.addSnapshotListener { DocumentSnapshot, error in
if error != nil{
return
}
else {
guard let snapshot = DocumentSnapshot, snapshot.exists else {return}
guard let data = snapshot.data() else { return }
self.valuesArray = Array(data.values) as! Array<String>
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped me!")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return valuesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = valuesArray[indexPath.row]
return cell
}
}
The variables data,values and rowCount are inside closure so you can't just write values[indexPath.row] because you can't return from inside closure. Usually completionHandlers are used for this purpose, but in this scenario, you should put the values inside an array and then use inside the tableview. Let me show you how to do.
At the start of your viewController, declare a string array.
var valuesArray:[String] = []
Then, inside modify your startObservingDB() function
func startObservingDB() {
var docRef:DocumentReference!
let db = Firestore.firestore()
docRef = db.collection("Users").document("Names")
docRef.addSnapshotListener { (docSnapshot, error) in
if error != nil {
return
}
else {
guard let snapshot = docSnapshot, snapshot.exists else {return}
guard let data = snapshot.data() else { return }
self.valuesArray = Array(data.values) as! Array<String>
self.tableView.reloadData()
}
}
}
In viewDidLoad call this function
startObservingDB()
Then in tableView methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ValuesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = valuesArray[indexPath.row]
return cell
}
In your class AdminViewController you can reference properties defined outside of methods with self (in your example you have dbRef that you can reference like self.dbRef in methods).
So I suggest you make properties for data, value and rowNumber and change them in startObservingDB method rather than declare them. This way you will be able to reference them in tableView methods.

How do I delete a row in a UITableView where the var is an integer?

I'm creating an audio voice recorder and after each upload, the name appends an int to the recording (e.g. 1, 2, 3, etc.) in a table view (note: regular view controller with a UITableView vs. Table View Controller).
I'm having trouble deleting each row, and I'm not sure if it is because 'numberOfRecords.remove(at: indexPath.row)' only accepts strings.
I get the error: "Value of type 'Int' has no member 'remove.'"
class ViewController2: UIViewController, RecordButtonDelegate, AVAudioRecorderDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var myTableView: UITableView!
var numberOfRecords : Int = 0
// Setting up Table View
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfRecords
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = String(indexPath.row + 1)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let path = getDirectory().appendingPathComponent("\(indexPath.row + 1).m4a")
do {
audioPlayer = try AVAudioPlayer(contentsOf: path)
audioPlayer.play()
}
catch {
}
}
// Delete rows
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete{
numberOfRecords.remove(at: indexPath.row)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
}
}
// Audio Player
var audioPlayer : AVAudioPlayer!
var recordingSession : AVAudioSession!
var audioRecorder : AVAudioRecorder!
var recordButton: RecordButton?
#IBOutlet weak var buttonLabel2: RecordButton!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
buttonLabel2.delegate = self
}
func tapButton(isRecording: Bool) {
// Check if we have an active recorder
if audioRecorder == nil {
numberOfRecords += 1
let filename = getDirectory().appendingPathComponent("\(numberOfRecords).m4a")
let settings = [AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue]
// Start audio recording
do {
audioRecorder = try AVAudioRecorder(url: filename, settings: settings)
audioRecorder.delegate = self
audioRecorder.record()
}
catch {
displayAlert(title: "Oops!", message: "Recording failed")
}
// Play speaker instead of earpiece
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.overrideOutputAudioPort(AVAudioSessionPortOverride.speaker)
} catch let error as NSError {
print("Audio Session error: \(error.localizedDescription)")
}
}
else {
// Stop audio recording
audioRecorder.stop()
audioRecorder = nil
UserDefaults.standard.set(numberOfRecords, forKey: "myNumber")
myTableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Setting up Recording session
recordingSession = AVAudioSession.sharedInstance()
if let number : Int = UserDefaults.standard.object(forKey: "myNumber") as? Int {
numberOfRecords = number
}
AVAudioSession.sharedInstance().requestRecordPermission { (hasPermission) in
if hasPermission {
print ("Accepted")
}
}
The problem that you're experiencing is due you calling remove(at:) on an Int. An Int has no function called remove(at:).
You're declaring var numberOfRecords: Int to track your indices, then in
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
you're calling
// this is the line that's causing your problem
numberOfRecords.remove(at: indexPath.row)
If you want to continue using an int to track your cells you should subtract from numberOfRecords
numberOfRecords-=1
Or you could track your records using an array something like this:
// declare an array of Strings to hold your filenames
var records: [String] = []
then where you're saving your files add the new filename to your array for the tableview
// Stop audio recording
audioRecorder.stop()
audioRecorder = nil
// add your filename to your array of records for the tableview
records.append(filename)
// update your total number of records if desired
UserDefaults.standard.set(numberOfRecords, forKey: "myNumber")
myTableView.reloadData()
Then your delegate functions might look something like this
// Setting up Table View
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return the total count of filenames in your array
return records.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// set the filename in your text label
cell.textLabel?.text = records[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// get the filename for this row
let filename = records[indexPath.row]
// use your filename in your path
let path = getDirectory().appendingPathComponent("\(filename)")
do {
audioPlayer = try AVAudioPlayer(contentsOf: path)
audioPlayer.play()
}
catch {
}
}
and you could update your remove(at:) call to run on the records array instead
records.remove(at: indexPath.row)
EDIT:
When you add or remove filenames from your records array update user defaults with your updated records:
// save array to user defaults when you create a new record or when you delete a record
UserDefaults.standard.setValue(records, forKey: "storedRecords")
To retrieve your saved array of filenames pull them from user defaults and update your records array with the stored names.
Replace these lines in your viewDidLoad function:
if let number : Int = UserDefaults.standard.object(forKey: "myNumber") as? Int {
numberOfRecords = number
}
with this:
// load stored records from user defaults and verify it's what you expect to receive
if let stored = UserDefaults.standard.value(forKey: "storedRecords") as? [String] {
// update your records array with the stored values
records = stored
}

Display user info from Firebase

I want to read and display user info from Firebase.
Here is how far I've got:
class UserInfoViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
var ref: DatabaseReference!
private var gotName = [""]
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return gotName.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "UserInfoCell") as! UserInfoTableViewCell
cell.nameLabel.text = gotName[indexPath.row]
return cell
}
override func viewDidLoad() {
ref = Database.database().reference()
let user = Auth.auth().currentUser!.uid
ref?.child("users").child(user).observeSingleEvent(of: .value, with: { (snapshot) in
guard let userDict = snapshot.value as? [String: Any],
let name = userDict["Name"] as? String else {
return
}
//Declare variables for use
self.gotName = [name]
})
}
}
Firebase structure:
-users
-----5DzurQyzyIbXfFCbAxc4ynwizYJ2
---------John Doe
I want the current user to be displayed in the nameLabel
You have written the code correctly.
but you must reload your table view after downloading the data from firebase.
Just write following code after self.gotName = [name] in your firebase observer.
DispatchQueue.main.async {
self.tableView.reloadData()
}
Hope this helps

tableView doesn't show data in swift

I'm trying to create a tableView within a viewController. I know it is annoying, but the table looks much better that way. I am also trying to incorporate data from Firebase to put into the table. Unfortunately when I run the code, it only shows a blank table. The console was able to print the desired data, but it just won't show on the actual table. Please let me know what I'm doing wrong. Many thanks!
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var user = Auth.auth().currentUser
var users = [Users]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.reloadData()
fetchUser()
}
func fetchUser() {
Database.database().reference(fromURL: "https://yala-2018.firebaseio.com/").child("users").observe(.childAdded, with: { (DataSnapshot) in
if let dictionary = DataSnapshot.value as? [String: AnyObject] {
let user = Users()
// user.setValuesForKeys(dictionary)
user.name = dictionary["name"] as! String
user.age = dictionary["age"] as! String
user.sex = dictionary["sex"] as! String
self.users.append(user)
print(user.name, user.age)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
})
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "guide", for: indexPath)
let user = users[indexPath.row]
cell.textLabel?.text = user.name
cell.detailTextLabel?.text = "Age: \(user.age) Sex: \(user.sex)"
// Configure the cell...
return cell
}
func tableView(tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
Change this to 1 as by 0 you mean no sections which will display empty tableView even if there is a data , or remove it as by default it's 1
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

UITableView don't reload data

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()
}
}