Swift Firestore - Delete Table Cell and Remove Doc

2020-06-23 09:30发布

I am developing a parenting app in Swift, I am using Firebase Cloud Firestore to save data for my Swift App. A part of the app is where Parents can add their children and display them in the app.

I have created a child model which I use when adding a new child to my tableview.

Child Model

protocol DocumentSerializable {
    init?(dictionary:[String:Any])
}

// Child Struct
struct Child {

    var name: String
    var age: Int
    var timestamp: Date
    var imageURL: String

    var dictionary:[String:Any] {
        return [
            "name":name,
            "age":age,
            "timestamp":timestamp,
            "imageURL":imageURL
        ]
    }

}

//Child Extension
extension Child : DocumentSerializable {
    init?(dictionary: [String : Any]) {
        guard let  name = dictionary["name"] as? String,
            let age = dictionary["age"] as? Int,
            let  imageURL = dictionary["imageURL"] as? String,
            let timestamp = dictionary["timestamp"] as? Date else {
                return nil
        }
        self.init(name: name, age: age, timestamp: timestamp, imageURL: imageURL)
    }
}

I add data to my tableview in my app by running a function in my view did load, loadData()

I set 2 variables above this first:

//firestore connection
var db:Firestore!

//child array
var childArray = [Child]()

viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()

    //Connect to database
    db = Firestore.firestore()

    // call load data function
    loadData()
    checkForUpdates()

}

The loadData() Function connects to the logged in users data, then grabs that users 'children' documents and adds the children to the childArray using the Child Object protocol.

func loadData() {
        // create ref to generate a document ID
        let user = Auth.auth().currentUser
        db.collection("users").document((user?.uid)!).collection("children").getDocuments() {
            QuerySnapshot, error in
            if let error = error {
                print("\(error.localizedDescription)")
            } else {
                // get all children into an array
                self.childArray = QuerySnapshot!.documents.flatMap({Child(dictionary: $0.data())})
                DispatchQueue.main.async {
                    self.childrenTableView.reloadData()
                }
            }
        }
    }

I get the numberOfRowsInSection by returning the childArray count like so:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return childArray.count
    }

I populate the cellForRow using a custom cell class and using the childArray contents like so:

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
       let cell = childrenTableView.dequeueReusableCell(withIdentifier: "Cell") as! ChildCellTableViewCell
        let child = childArray[indexPath.row]

        cell.childNameLabel.text = "\(child.name)"
        cell.childAgeLabel.text =  "Age: \(child.age)"

        let url = URL(string: "\(child.imageURL)")
        cell.childImage.kf.setImage(with: url)

        cell.childNameLabel.textColor = UIColor.white
        cell.childAgeLabel.textColor = UIColor.white
        cell.backgroundColor = UIColor.clear

        return cell
    }

I want to be able to swipe to delete each table row cell so I have implemented the following:

 func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

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

            childArray.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)

        }
    }

This successfully removes the row from the app. But my problem is I am unsure how to get the deleted row to delete the matching data from the Firestore Database.

And to complicate things even more, as each child has an image, which gets stored in Firebase Storage, I need to somehow also delete this image. The image URL is being stored in the childrens document under imageURL.

I would appreciate any guidance or pointing in the right direction for this, I cannot find much documentation with regards to Firestore and UITableViews so don't know what to even try next?


UPDATE In the 'canEditRow' Function I have managed to delete the childs image from Firebase storage from the table row I deleted, but I am struggling to delete the child document from Firestore. I can query the document I need to delete, but not sure how to run the delete() function from this query?

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

            // 1. First Delete the childs image from storage
            let storage = Storage.storage()
            let childsImageURL = childArray[indexPath.row].imageURL
            let storageRef = storage.reference(forURL: childsImageURL)

            storageRef.delete { error in
                if let error = error {
                    print(error.localizedDescription)
                } else {
                    print("File deleted successfully")
                }
            }


            // 2. Now Delete the Child from the database
            let name = childArray[indexPath.row].name

            let user = Auth.auth().currentUser
            let query = db.collection("users").document((user?.uid)!).collection("children").whereField("name", isEqualTo: name)

            print(query)


            childArray.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)

        }
    }

3条回答
我命由我不由天
2楼-- · 2020-06-23 10:03

Swift 4 and 5

import FirebaseFirestore

var database : Firestore!

override func viewDidLoad() {
    super.viewDidLoad()
    db = Firestore.firestore()
}
private func remove(_ id: String) -> Void {

    database.collection("User").whereField("id", isEqualTo: id).getDocuments { (querySnapshot, error) in
        if error != nil {
            print(error)
        } else {
            for document in querySnapshot!.documents {
                document.reference.delete()
            }

        }
    }
}
查看更多
▲ chillily
3楼-- · 2020-06-23 10:05

you should add document id into model:

struct Child {
    var id: String
    var name: String
    var age: Int
    var timestamp: Date
}

Constructor should be:

init?(dictionary: [String : Any], id: String) {
    guard let  name = dictionary["name"] as? String,
        let age = dictionary["age"] as? Int,
        let  imageURL = dictionary["imageURL"] as? String,
        let timestamp = dictionary["timestamp"] as? Date else {
            return nil
    }
    self.init(id: id, name: name, age: age, timestamp: timestamp, imageURL: imageURL)
}

in loadData() method should be:

self.childArray = QuerySnapshot!.documents.flatMap({Child(dictionary: $0.data(), id: $0.documendID)})

This way you needn't query for document, you alraedy have and delete it directly:

self.db.collection("users").document((user?.uid)!).collection("children").document(childArray[indexPath.row].id)).delete()

And one last question: why do you interpolate variables which are already String type to get String value?

查看更多
▲ chillily
4楼-- · 2020-06-23 10:07

I think I have managed to work this out, and it seems to be working. In the 'canEditRow' Function, number 2, I can now find the specific child (when swiping the table cell to delete) and it deletes the same one in Firebase Firestore Database.

I am not sure if this is the correct way of doing it, or if I am missing any error checking, but it all seems to be working.

If anyone can spot any errors here please let me know, I really want to make sure it is safe to use and all fallbacks are in place.

So here is what I have done to make the whole thing work.

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

            // 1. First Delete the childs image from storage
            let storage = Storage.storage()
            let childsImageURL = childArray[indexPath.row].imageURL
            let storageRef = storage.reference(forURL: childsImageURL)

            storageRef.delete { error in
                if let error = error {
                    print(error.localizedDescription)
                } else {
                    print("File deleted successfully")
                }
            }



            // 2. Now Delete the Child from the database
            let name = childArray[indexPath.row].name
            let user = Auth.auth().currentUser
            let collectionReference = db.collection("users").document((user?.uid)!).collection("children")
            let query : Query = collectionReference.whereField("name", isEqualTo: name)
            query.getDocuments(completion: { (snapshot, error) in
                if let error = error {
                    print(error.localizedDescription)
                } else {
                    for document in snapshot!.documents {
                        //print("\(document.documentID) => \(document.data())")
                        self.db.collection("users").document((user?.uid)!).collection("children").document("\(document.documentID)").delete()
                }
            }})


            // 3. Now remove from TableView
            childArray.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)

        }
    }
查看更多
登录 后发表回答