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)
}
}
Swift 4 and 5
you should add document id into model:
Constructor should be:
in loadData() method should be:
This way you needn't query for document, you alraedy have and delete it directly:
And one last question: why do you interpolate variables which are already String type to get String value?
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.