im trying to delete a cell from a tableview, and from Firestore too.
This how I declared my cart :
struct Cart
{
var photoKeyCart: String
var foodCart: String
var priceCart: Int
}
var cart: [Cart] = [] // This is in the cart controller
This is my tableview where I have my cart items :
extension CartViewController: UITableViewDelegate, UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var sum = 0
for item in cart{
sum += item.priceCart
}
priceTotalLabel.text = "\(sum) lei"
return cart.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = cartTableView.dequeueReusableCell(withIdentifier: "CartTableViewCell", for: indexPath) as! CartTableViewCell
let carts = cart[indexPath.row]
let storageRef = Storage.storage().reference()
let photoRef = storageRef.child(carts.photoKeyCart)
cell.foodInCartPrice.text = " \(carts.priceCart) lei "
cell.foodInCartName.text = carts.foodCart
cell.foodInCartImage.sd_setImage(with: photoRef)
cell.foodInCartImage.layer.borderWidth = 1
cell.foodInCartImage.layer.masksToBounds = false
cell.foodInCartImage.layer.borderColor = UIColor.black.cgColor
cell.foodInCartImage.layer.cornerRadius = cell.foodInCartImage.frame.height/2
cell.foodInCartImage.clipsToBounds = true
return cell
}
This is how im getting the data from the Firestore into the cart. This is called in the view did load.
func getCartProducts() {
let db = Firestore.firestore()
let userID = (Auth.auth().currentUser?.uid)!
db.collection("CartDatabase").document(userID).collection("CartItems").getDocuments { (document, error) in
if let error = error {
print(error)
return
} else {
for document in document!.documents {
let data = document.data()
let newEntry = Cart(photoKeyCart: data["photoKeyCart"] as! String, foodCart: data["foodCart"] as! String , priceCart: data["priceCart"] as! Int
)
self.cart.append(newEntry)
}
}
DispatchQueue.main.async {
// self.datas = self.filteredData
self.cartTableView.reloadData()
}
}
}
And, this is how im trying to delete the cell from the tableview, and from the Firestore too.
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
let carts = cart[indexPath.row]
let storageRef = Storage.storage().reference()
let photoRef = storageRef.child(carts.photoKeyCart)
photoRef.delete { error in
if let error = error {
print(error.localizedDescription)
} else {
print("File deleted successfully")
}
}
let db = Firestore.firestore()
let userID = (Auth.auth().currentUser?.uid)!
db.collection("CartDatabase").document(userID).collection("CartItems").getDocuments { (document, error) in
if let error = error {
print(error.localizedDescription)
} else {
for document in document!.documents {
//print("\(document.documentID) => \(document.data())")
db.collection("CartDatabase").document(userID).collection("CartItems").document(document.documentID).delete()
//self.db.collection("users").document((user?.uid)!).collection("children").document("\(document.documentID)").delete()
}
}}
cart.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
I have the following problem. When im trying to delete the cell, it works, but when im closing the cart and coming back again to the cart, it deletes all the items into the cart, not just the one I tried to delete.
What I want to achieve : to delete just only the cell selected.
Help :D
UPDATE:
I have a tableview with which contains a food, each cell is aa different king of food. I have a plus button, when the plus button is tapped, ill send the datas of the food into the Firestore, and in the cart I retrieve the data.
This is how im sending the data to the cart :
func updateDocument(collection: String, newValueDict: [String : Any], completion:#escaping (Bool) -> Void = {_ in }) {
let db = Firestore.firestore()
let userID = (Auth.auth().currentUser?.uid)!
db.collection(collection).document(userID).collection("CartItems").document().setData(newValueDict, merge: true){ err in
if let err = err {
print("Error writing document: \(err)")
completion(false)
}else{
completion(true)
}
}
}
And when I tapped the cell :
cell.didTapButton = {
self.updateDocument(collection: "CartDatabase",newValueDict: ["foodCart" : mancare.foodName, "photoKeyCart": mancare.photoKeyRestaurant, "priceCart": mancare.priceFood])
}
Check the photos
Photo1
Photo2
Without seeing all of the code it's hard to provide a specific example but let me cover this at a high level.
Suppose we have a posts class object
class PostsClass {
var docId = ""
var post = ""
}
and an class array (aka a 'dataSource') to store them in
var postsArray = [PostsClass]()
The first step is to load all of the posts from Firebase, storing the docId and post text in each class and then store the class in the dataSource array.
myFirebase.getDocuments { doc...
for doc in documents { //iterate over the documents and populate the array
let post = PostClass(populate with data from firebase)
self.postsArray.add(post)
}
}
the dataSouce array will look like this
postsArray[0] = some post
postsArray[1] = another post
etc, and all of that is displayed in a tableView.
The user then decides to delete the post at row 1. So they swipe row one, which fires a tableView delegate event letting the app know the index of the swiped row.
Step 1: You then get that post from the array based on the swiped row index
let postToDelete = self.postsArray[swiped index]
let docIdToDelete = postsToDelete.docID
Step 2: then remove it from the array
self.postsArray.deleteRow(atIndex: swiped index)
Step 3: then delete it from Firebase.
self.my_firebase.collection("posts").document(docIdToDelete).delete {....
Note that the func tableView:tableView:commit editingStyle will present the editing style of .delete when the row is supposed to be deleted and also provide the index in indexPath.row
Related
How can one display the data from the Firestore Collections to tableView on Swift?
This code runs (see below) but I want to import directly from the Firestore database instead of hardtyping data:
var habits = [Habit(id: "1", author: "Maiuran", text: "heyhey")]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "habitCell", for: indexPath) as! HabitTableViewCell
cell.set(habit: habits[indexPath.row])
return cell
}
The code below works to print to the console but not sure how to display the
let name = Auth.auth().currentUser?.uid
Firestore.firestore().collection("habits").whereField("author", isEqualTo: name).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
}
This is the database: Firestore Database structure
Thank you so much for your time and advice!
You have to check user: if user == nil you must login user before.
override func viewDidLoad() {
super.viewDidLoad()
let user = Auth.auth().currentUser
if user == nil {
login()
} else {
// processing
}
}
Here is a partial solution (now it posts to a given UITextfield so just need to figure out how to post to tableView):
Firestore.firestore().collection("habits").getDocuments(completion: { (snapshot, error) in
snapshot!.documents.forEach({ (document) in
let nametyname = document.data()["author"]
let street = document.data()["friend"]
self.myTextiFieldi.text = nametyname as! String
print(nametyname)
print(street)
})
})
I have this struct:
struct Info {
var name: String = ""
var number = Int()
}
var infoProvided : [Info] = []
I display desired data in a tableView:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "InfoCell") as! InfoTableViewCell
let name = infoProvided[indexPath.row].name
let number = infoProvided[indexPath.row].number
cell.infoLabelLabel.text = "\(name) with \(number)"
return cell
}
I am trying to write data to firebase like this:
self.ref?.child(gotLokasjon).child(location).child(dateAndTime).updateChildValues(["Signed up:" : infoProvided])
This returns the error:
Cannot store object of type _SwiftValue at 0. Can only store objects of type NSNumber, NSString, NSDictionary, and NSArray.'
How can I write my Struct to Firebase?. I would like to write it equal to how its displayed in the tableView:
cell.infoLabelLabel.text = "\(name) with \(number)"
I haven't understood where you want the upload to happen(before or after they are displayed on tableView) so adjust it to suit your needs.
guard let name = infoProvided.name else {return}
guard let number = infoProvided.number else {return}
let data = [ "name": name, "number": number] as [String : Any]
self.ref?.child(gotLokasjon).child(location).child(dateAndTime).updateChildValues(data, withCompletionBlock: { (error, ref) in
if error != nil{
print(error!)
return
}
print(" Successfully uploaded")
})
After a bit of fiddling I did this:
let infoArray = infoProvided.map { [$0.number, $0.name] }
let items = NSArray(array: infoArray)
Then implemented that in the above solution. This seams to work.
I don't know if this is a good solution?
firestore to store about more than 500 information and I want to display it to table view. Basically, I have successfully display all the data in my cell, but the problem is, it takes more than 1 minute to load all data. While the data loaded, I cannot scroll the table view, unless all data finish load. How to enable scrolling while the data is still loading? If not possible, how to load first 20 data first, and will continue load if user is at the end of the cell? Here is some code that I have tried to
get data from firestore:
func getData () {
db.collection("fund").getDocuments()
{
(querySnapshot, err) in
if let err = err
{
print("Error getting documents: \(err)");
}
else
{
for document in querySnapshot!.documents {
let data = document.data()
let agencyPath = data["agensi"] as? String ?? ""
let title = data["title"] as? String ?? ""
let program = data["program"] as? String ?? ""
let perniagaan = data["perniagaan"] as? String ?? ""
let newMax = data["max"] as? Int
let agencyId = document.documentID
let query = Firestore.firestore().collection("Agensi")
let newQuery = query.whereField("name", isEqualTo: "\(agencyPath)")
newQuery.getDocuments()
{
(querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)");
} else
{
for document in querySnapshot!.documents {
let data = document.data()
let logo = data["logo"] as? String ?? ""
//store to Struct
let newModel = DisplayModel(agency: title, agencyId: agencyId, programTag: program, perniagaanTag: perniagaan, max: newMax, agencyPath: agencyPath, logoUrl: logo, agencyTitle: agencyPath)
self.agencyList.append(newModel)
}
self.tableView.reloadData()
self.dismiss(animated: false, completion: nil)
}
}
}
}
}
}
display data on cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellData: DisplayModel
if searchController.searchBar.text != "" {
cellData = filteredData[indexPath.row]
} else {
cellData = agencyList[indexPath.row]
}
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? HomeTableViewCell
cell?.agencyName.text = cellData.agency
cell?.agencyImage.sd_setImage(with: URL(string: "\(cellData.logoUrl ?? "")"), placeholderImage: UIImage(named: "no_pic_image"))
return cell!
}
Action on last row of cell:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if searchController.searchBar.text != "" {
let lastElement = filteredData.count - 1
if indexPath.row == lastElement {
//getData()
// handle your logic here to get more items, add it to dataSource and reload tableview
}
} else {
let lastElement = agencyList.count - 1
if indexPath.row == lastElement {
//getData()
// handle your logic here to get more items, add it to dataSource and reload tableview
}
}
}
I really have no idea what method I should do to load 20 data first and continue load at the end of cell row, if there is no solution, at least I could scroll the table view during the load session. Thank You, for your information, i just learn swift last month. Thank you for helping me.
You should definitly adopt the UITableViewDataSourcePrefetching protocol.
Check some blogs, like:
https://www.raywenderlich.com/187041/uitableview-infinite-scrolling-tutorial
and adopt it to pagination as described here:
https://firebase.google.com/docs/firestore/query-data/query-cursors
I have a tableview that populates an array. I am able to add to firebase and the tableview reloads to show the newly added object. If I have 5 items in firebase then I would have 5 on the tableview. I am able to remove items from firebase through code and reload the tableview and it works great.
My issue is when I am on the last item on firebase and tableview and I delete that last item, the firebase removes it just fine, but the tableview keeps the last item but grays it out.
The app doesn't crash it just stays there until I add something back in.
Obviously if I tap on that grayed out cell my app crashes because I am tapping an index out of range.
Is there some code that I need to add to prevent this grayed out cell and just have an empty tableview?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return serviceArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "serviceCell", for: indexPath as IndexPath) as! ServiceTableViewCell
let row = indexPath.row
// cell.serviceLogoImage.image = UIImage.init(named: serviceArray[row].serviceUrl!)
cell.serviceNameLabel.text = serviceArray[row].serviceName
if serviceArray[row].serviceStatus == true {
cell.serviceStatusView.backgroundColor = .green
} else {
cell.serviceStatusView.backgroundColor = .red
}
return cell
}
I must be missing a small conditional because other than this the tableview works perfectly with firebase...
Edit 1
I have added the code used to populate the tableview
var serviceArray: [ServiceClass] = []
func pullCardData() {
serviceArray.removeAll()
let cardRef = ref.child("cards")
cardRef.observeSingleEvent(of: .value, with: { snapshot in
for cards in snapshot.children {
let allCardIDs = (cards as AnyObject).key as String
if allCardIDs == self.cardID {
let thisCardLocation = cardRef.child(self.cardID)
thisCardLocation.observeSingleEvent(of: .value, with: { snapshot in
let thisCardDetails = snapshot as FIRDataSnapshot
let cardDict = thisCardDetails.value as! [String: AnyObject]
self.selectedCard.cardID = thisCardDetails.key
self.selectedCard.nickname = cardDict["nickname"] as! String
self.selectedCard.type = cardDict["type"] as! String
self.cardNickNameLabel.text = cardDict["nickname"] as? String ?? ""
let thisCardServices = self.ref.child("cards").child(self.cardID).child("services")
let serviceRefLoc = self.ref.child("services")
thisCardServices.observeSingleEvent(of: .value, with: {serviceSnap in
if serviceSnap.hasChildren() {
for serviceChild in serviceSnap.children {
let serviceID = (serviceChild as AnyObject).key as String
serviceRefLoc.observeSingleEvent(of: .value, with: {allServiceSnap in
if allServiceSnap.hasChildren() {
for all in allServiceSnap.children {
let allServs = (all as AnyObject).key as String
let thisServiceLocationInServiceNode = self.ref.child("services").child(serviceID)
if serviceID == allServs {
thisServiceLocationInServiceNode.observeSingleEvent(of: .value, with: {thisSnap in
let serv = thisSnap as FIRDataSnapshot
let serviceDict = serv.value as! [String: AnyObject]
let aService = ServiceClass()
self.serviceCurrent = serviceDict["serviceStatus"] as? Bool
self.serviceName = serviceDict["serviceName"] as? String
self.serviceURL = serviceDict["serviceURL"] as? String
self.serviceFixedBool = serviceDict["serviceFixed"] as? Bool
self.serviceFixedAmount = serviceDict["serviceAmount"] as? String
aService.serviceUrl = serviceDict["serviceURL"] as! String
aService.serviceName = serviceDict["serviceName"] as! String
aService.serviceStatus = serviceDict["serviceStatus"] as? Bool
aService.serviceID = serviceID
self.serviceArray.append(aService)
self.tableView.reloadData()
})
}
}
}
})
}
}
})
})
}
}
})
}
Edit 2
I had the idea to check if the firebase node even exits (it shouldn't since I just deleted it. SO
func checkIfDataExits() {
ref.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.hasChild("services") {
self.pullCardData()
} else {
self.tableView.endUpdates()
print("no childen")
}
})
}
As expected since I don't have the firebase node there anymore it prints "no children" but it still shows that last tableview cell....so
Well, my last edit was one line of code off from being what I needed.
Instead of
self.tableView.endUpdates()
I replaced it with
self.tableView.reloadData()
So (without retying that long method) I simply wrote another method
func checkIfDataExits() {
serviceArray.removeAll()
ref.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.hasChild("services") {
self.pullCardData()
} else {
self.tableView.reloadData()
}
})
}
And THIS method decides wether or not to even run that long one
resolve:
serviceArray.removeAll()
Code:
ref.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.hasChild("services") {
self.pullCardData()
} else {
//code here
serviceArray.removeAll()
self.tableView.reloadData()
}
})
I'm clueless as to what is wrong. My console doesn't give me any errors, my code seems fine but nothing is showing up. Could someone check my code, see why it doesn't want to work? My tableView is connected with its delegates and source. Not sure what is the problem.
Here is my code:
private let cellIdentifier = "cell"
private let apiURL = "api link"
class TableView: UITableViewController {
//TableView Outlet
#IBOutlet weak var LegTableView: UITableView!
//API Array
var legislatorArray = [congressClass]()
func getLegislators (fromSession session: NSURLSession) {
//Calling url
if let jsonData = NSURL(string: apiURL) {
// Requesting url
let task = session.dataTaskWithURL(jsonData) {(data, response, error) -> Void in
//Check for errors
if let error = error {print(error)
} else {
if let http = response as? NSHTTPURLResponse {
if http.statusCode == 200 {
//Getting data
if let data = data {
do {
let legislatorData = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
//Get API data
if let getData = legislatorData as? [NSObject:AnyObject],
findObject = getData["results"] as? [AnyObject]{
//Return data
for cellFound in findObject{
if let nextCell = cellFound["results"] as? [NSObject:AnyObject],
name = nextCell["first_name"] as? String,
lastName = nextCell["last_name"] as? String,
title = nextCell["title"] as? String,
partyRep = nextCell["party"] as? String,
position = nextCell ["position"] as? String,
id = nextCell ["bioguide_id"] as? String
{
//Add data to array
let addData = congressClass(name: name, lastName: lastName, title: title, party: partyRep, position: position, bioID: id)
self.legislatorArray.append(addData)
}
}//end cellFound
//Adding data to table
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.tableView.reloadData()
}
}
}
//end do
catch {print(error)}
}//end data
}//end statusCode
}//end http
}//else
}//end task
//Run code
task.resume()
}//end jsonData
}
override func viewDidLoad() {
super.viewDidLoad()
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let urlSession = NSURLSession(configuration: sessionConfig)
getLegislators(fromSession: urlSession)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
//TableView Rows
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return legislatorArray.count
//return 5
}
//Cell Configuration
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CellTableView
cell.lesName?.text = legislatorArray[indexPath.row].name + " " + legislatorArray[indexPath.row].lastName
cell.lesTitle?.text = legislatorArray[indexPath.row].title
cell.lesParty?.text = legislatorArray[indexPath.row].party
//These tests worked fine.. the tableView is working. But the data doesn't seem to pass.
//cell.lesName.text = "Name" + " " + "lastName"
//cell.lesTitle.text = "Title goes here"
//cell.lesParty.text = "D"
return cell
}
}
You're not reloading the tableView
The problem is in this piece of code
//-----------------------------
//New empty array for api data
var indexPath:[NSIndexPath] = []
//Adding data to new array
for i in 0..<self.legislatorArray.count{
let secondIndexPath = NSIndexPath(forRow: i, inSection: 0)
indexPath.append(secondIndexPath)
}
//Adding data to table
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.tableView.insertRowsAtIndexPaths(indexPath, withRowAnimation: .Left)
}
You don't need any of that. You can just reload the tableView as follows:
//Adding data to table
dispatch_async(dispatch_get_main_queue()) { () -> Void in
//You only need to reload it and that should do the trick
self.tableView.reloadData()
}
I know you said your tableView is connected to the delegate and dataSource but it's not showing in your code.
You conformed the ViewController to the correct protocols but you need something like this in your viewDidLoad.
self.tableView.deletage = self
self.tableView.dataSource = self
//I don't know if this was a typo but in your cellForRowAtIndexPath you are using CellTableView
let nibName = UINib(nibName: "CellTableView", bundle:nil)
self.tableView.registerNib(nibName, forCellReuseIdentifier: cellIdentifier)
I created an example of a better design for your implementation
This is for the WebService and your Custom Class
https://github.com/phantomon/Stackoverflow/blob/master/SO1/MyTableView/MyTableView/Models/WebServiceManager.swift
This is for the ViewController with your tableView
https://github.com/phantomon/Stackoverflow/blob/master/SO1/MyTableView/MyTableView/ViewController.swift
You just need to modify the UITableViewCell with your custom one.
And of course review your custom class data.