ios - Setting value in to FIRDataSnapshot - ios

I'm new to swift and having trouble with setting value to FIRDataSnapshot. I simply created a calculator. I took MealCaloryArray in didload method and display it tableviewcell (with the help of FIRDataSnapshot list) and the calory is changed in the plus function and I have to send the new value to tableviewcell again. However, I couldnt set the new value in FIRDataSnapshot array list. I tried to useself.calory[buttonRow].setValue(<value: n, forUndefinedKey:"")but I dont have proper "forUndefinedKey" value. Do you have any suggestions?
My nested Firebase DB Structure and code blocks is attached.
Firebase Child Structure:
Database Screenshot
var calory: [FIRDataSnapshot]! = []
override func viewDidLoad() {
ref = FIRDatabase.database().reference()
let CoursesRef = ref.child("CompanyMeals")
CoursesRef.observe(.childAdded, with: { snapshot in
self.calory = snapshot.childSnapshot(forPath: "MealCaloryArray").children.allObjects as! [FIRDataSnapshot]
self.calory.append(snapshot)
self.ingredientTableView.reloadData()
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ingredientTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MealCalculatorListCell
let cellcalory = self.calory[indexPath.row].value as? Double
if (cellcalory != nil) {
cell.itemTotalCalory.text = ("\(cellcalory!)")//String (describing: cellcalory)
let cellcalory1 = Int(cellcalory!)
firstCaloriesArray.append((cellcalory1 as AnyObject))
}
}
#IBAction func plusAction(sender: UIButton) {
cell.itemTotalCalory.text = String ((Int(oldcalory!) + fcCalory))
let newcalory = cell.itemTotalCalory.text
let n = String(newcalory!)
self.calory[buttonRow].setValue(value: n, forUndefinedKey: "")
}

Related

Retrieve photos from firebase storage SWIFT

I have a problem since yesterday morning but I can't figure it out how can I resolve this issue.
I'm having a table view which is using prototype cells, 2 labels and 1 photo. For the labels I used Firestore and for the picture firebase storage.
The problems is that the only way I know how to retrieve photos from my firebase storage is this code
let storage = Storage.storage()
let storageRef = storage.reference()
let ref = storageRef.child("Mancare/Mancare3.jpg")
testImage.sd_setImage(with: ref)
I want to retrieve the photos into my table view, but I do not know how can I can accomplish that.
This is what im using for retrieving the labels with Firestore . I'll paste only the necessary parts of the code :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return labels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewTest.dequeueReusableCell(withIdentifier: "CountryTableViewCell", for: indexPath) as! CountryTableViewCell
let storage = Storage.storage()
let storageRef = storage.reference()
let ref = storageRef.child("Mancare/Mancare3.jpg")
let label = labels[indexPath.row]
cell.labelTest.text = label.firstLabel
cell.labelLaba.text = label.secondLabel
return cell
}
func getDatabaseRecords() {
let db = Firestore.firestore()
labels = [] // Empty the array
db.collection("labels").getDocuments { (snapshot, error) in
if let error = error {
print(error)
return
} else {
for document in snapshot!.documents {
let data = document.data()
let newEntry = Labels(
firstLabel: data["firstLabel"] as! String,
secondLabel: data["secondLabel"] as! String)
self.labels
.append(newEntry)
}
}
DispatchQueue.main.async {
self.tableViewTest.reloadData()
}
}
}
This is how I declared the labels:
struct Labels {
let firstLabel: String
let secondLabel: String
}
var labels: [Labels] = []
If someone can help me , ill be forever grateful . Thanks
First, you need to fix your model so it can help you. Add the bucket name to the model like this:
Struct Labels {
let firstLabel: String
let secondLabel: String
let photoKey: String // This will store the bucket name for this `Labels`
}
Now in your getDatabaseRecords change:
let newEntry = Labels(firstLabel: data["firstLabel"] as! String,
secondLabel: data["secondLabel"] as! String),
photoKey: data["photoKey"] as! String) // Added Line
Then in cellForRow:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewTest.dequeueReusableCell(withIdentifier: "CountryTableViewCell", for: indexPath) as! CountryTableViewCell
let label = labels[indexPath.row]
let storageRef = Storage.storage().reference()
let photoRef = storageRef.child(label.photoKey)
cell.labelTest.text = label.firstLabel
cell.labelLaba.text = label.secondLabel
cell.imageView.sd_setImage(with: photoRef) // Assuming the image view in your cell is named this
return cell
}
Last, make sure your document structure matches the new Labels Model in the firebase console, and you have images as well in the root of your storage that match with all the photoKeys. Btw, Labels is not a very good model name, I just went with it for consistency

What am I doing wrong while populating this UITableView in Swift?

I am trying to populate a UITableView using an array and I am unable to do so. Here is what I have so far. This code is for retrieving data and storing it in the array that I am using to populate the UITableView:
func prepareForRetrieval() {
Database.database().reference().child("UserCart").child(Auth.auth().currentUser!.uid).observe(.value, with: {
(snapshot) in
for snap in snapshot.children.allObjects {
let id = snap as! DataSnapshot
self.keyArray.append(id.key)
}
self.updateCart()
})
}
func updateCart() {
for key in keyArray {
Database.database().reference().child("UserCart").child(Auth.auth().currentUser!.uid).child(key).observeSingleEvent(of: .value, with: {
(snapshot) in
let value = snapshot.value as? NSDictionary
let itemName = value?["Item Name"] as! String
let itemPrice = value?["Item Price"] as! Float
let itemQuantity = value?["Item Quantity"] as! Int
self.cartArray.append(CartData(itemName: itemName, itemQuantity: itemQuantity, itemPriceNumber: itemPrice))
print(self.cartArray.count)
})
}
}
The data is properly appending into the array and when I print the count of the array, it prints the correct count. This means that the data is there. However, when I try to populate a UITableView, it doesn't detect any data. I have the following code to make sure that there is data in the array before trying to populate the UITableView:
override func viewDidLoad() {
super.viewDidLoad()
cartBrain.prepareForRetrieval()
if cartBrain.cartArray.isEmpty == false{
tableViewOutlet.dataSource = self
tableViewOutlet.reloadData()
}
else {
tableViewOutlet.isHidden = true
tableViewOutlet.isUserInteractionEnabled = false
purchaseButtonOutlet.isEnabled = false
cartEmptyLabel.text = "Your cart is empty. Please add items and check back later."
}
}
When I open the View Controller, the TableView is disabled because it doesn't detect any data. I have already set the data source to self and the thing is that when the count of the array is printed, it again prints the correct amount. I have already set the data source to self for the UITableView. Here is my code for the UITableView:
extension CartViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cartBrain.cartArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cartcustomcell", for: indexPath)
cell.textLabel?.text = cartBrain.cartArray[indexPath.row].itemName
cell.detailTextLabel?.text = String(cartBrain.cartArray[indexPath.row].itemQuantity)
return cell
}
}
I don't understand why the count of the array prints the correct amount meaning that there is data stored in it but when the View Controller is loaded, it detects that the array is empty. Thanks for the help and I'm sorry if the question is a bit unclear.
After appending data to cartArray in updateCart you should reloadData(), like this:
weak var tableViewOutlet: UITableView?
func updateCart() {
for key in keyArray {
Database.database().reference().child("UserCart").child(Auth.auth().currentUser!.uid).child(key).observeSingleEvent(of: .value, with: {
(snapshot) in
let value = snapshot.value as? NSDictionary
let itemName = value?["Item Name"] as! String
let itemPrice = value?["Item Price"] as! Float
let itemQuantity = value?["Item Quantity"] as! Int
self.cartArray.append(CartData(itemName: itemName, itemQuantity: itemQuantity, itemPriceNumber: itemPrice))
DispatchQueue.main.async {
self.tableViewOutlet.reloadData()
}
})
}
}
The updateCart doesn't seem to have any connection to the tableViewOutlet so you need to pass in a reference to it in your viewDidLoad like this:
override func viewDidLoad() {
super.viewDidLoad()
cartBrain.tableViewOutlet = tableViewOutlet
cartBrain.prepareForRetrieval()
Note: Since you're using a for loop to trigger the async call multiple times you can use the array count to check if all the items are appended to do the reload to avoid multiple reloads.

Multiple Table View with different arrays

I have a detail view that shows the details of an event, the people who participate and the people who asked to participate. I have created two arrays of different types but they have the same fields, only that a first structure represents the users with the 'status_confirm' field equal to 1 (therefore Accepted Users), while the other has as 'status_confirm' equal to 0 (Users awaiting acceptance). I declared two arrays, the first one: var arrayUserAccepted = [User_accepted] ().
The second one: var arrayUserWaiting = [User_waiting] (). Struct Image
Next step: I populate these structures via a php script
func getData(){
let url = URL(string: “MYURL”)
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
guard let data = data, error == nil else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String:AnyObject]
print("JSON: \n\(json)\n")
let waiting = json["waiting"] as! [AnyObject]
let accepted = json["accepted"] as! [AnyObject]
DispatchQueue.main.async {
for list_user_waiting in waiting {
let id_user_waiting = list_user_waiting["id_user”] as! String
let name_user_waiting = list_user_waiting[“name_user”] as! String
let email_user_waiting = list_user_waiting["email"] as! String
var photo_user_waiting = list_user_waiting[“photo”]
let status_user_waiting = list_user_waiting["status”] as! String
if photo_user_waiting is NSNull {
photo_user_waiting = ""
}
let listUserWaiting = User_waiting(id_user_waiting: id_user_waiting, name_user_waiting: name_user_waiting, email_user_waiting: email_utente_attesa, foto_waiting: photo_user_waiting as! String, status_waiting: status_user_waiting)
self.arrayUserWaiting.append(listUserWaiting)
self.tableViewListUserWaiting.reloadData()
}
for list_user_accepted in accepted {
let id_user_accepted = list_user_accepted["id_utente"] as! String
let name_user_accepted = list_user_accepted["name_utente"] as! String
let email_user_accepted = list_user_accepted["email"] as! String
var photo_user_accepted = list_user_accepted[“photo"]
let status_user_accepted = list_user_accepted["status”] as! String
if photo_user_accepted is NSNull {
photo_user_accepted = ""
}
let listUserAccepted = User_accepted(id_user: id_user_accepted, nome_utente: name_user_accepted, email: email_user_accepted, foto: photo_user_accepted as! String, stato: status_user_accepted)
self.arrayUserAccepted.append(listUserAccepted)
self.tableViewListUserAccepted.reloadData()
}
}
} catch let error as NSError {
print(error)
}
}).resume()}
This above is a function that I call in the viewDidLoad(). The next step would be to use the functions of the table view and it is here that I think there is the injunction
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int?
if tableView == self.tableViewListUserAccepted {
count = arrayUserAccepted.count
}
if tableView == self.tableViewListUserWaiting {
count = arrayUserWaiting.count
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
if tableView == self.tableViewListUserAccepted {
cell.imageProfileUserAccepted.image = UIImage(named: "imageDefault")
cell.valueSliderUserAccepted.value = Float(50) //JUST FOR POPULATE THE INTERFACE
cell.name_user_accepted.text = arrayUserAccepted[indexPath.row].name_user
}
if tableView == self.tableViewListUserWaiting {
cell.imageProfileUserWaiting.image = UIImage(named: "imageDefault")
cell.valueSliderUserWaiting.value = Float(23) //JUST FOR POPULATE THE INTERFACE
cell.name_user_waiting.text = arrayUserWaiting[indexPath.row].name_user_waiting
}
return cell
}
Once done all this round, I start the application but nothing. The tables are empty. In the console the script answers me correctly and so I can not figure out where the error could be. Needless to say, I have declared the .delegate and .dataSource of both tables, both in the Main.Storyboard and in the code.
Everything is fine just change the format of IF condition and it will work.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int?
if tableView == self.tableViewListUserAccepted {
count = arrayUserAccepted.count
} else {
count = arrayUserWaiting.count
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableViewListUserAccepted {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.imageProfileUserAccepted.image = UIImage(named: "imageDefault")
cell.valueSliderUserAccepted.value = Float(50) //JUST FOR POPULATE THE INTERFACE
cell.name_user_accepted.text = arrayUserAccepted[indexPath.row].name_user
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.imageProfileUserWaiting.image = UIImage(named: "imageDefault")
cell.valueSliderUserWaiting.value = Float(23) //JUST FOR POPULATE THE INTERFACE
cell.name_user_waiting.text = arrayUserWaiting[indexPath.row].name_user_waiting
return cell
}
}
Also check if the datasource and delegate of both of your tableView are set. Finally call the tableView.reloadTable() method on both of your tableviews after you populate your arrays in the viewDidLoad() method.

Issue with first Firebase query item out of order until refresh

I have a firebase database to pull 50 users with the highest integer value and to display them from highest to lowest. The issue will arise when I enter the leaderboard view for the first time. The order should show jlewallen18 at the top AND THEN appledev. But on first load appledev is at the top, until I back out and open the leaderboard again (code at the bottom).
Leaderboard code:
class LeaderboardViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var leaderboardTableView: UITableView!
var userModel : [User] = [User]()
let pipeline = ImagePipeline {
//config settings for image display removed for brevity
}
override func viewDidLoad() {
super.viewDidLoad()
leaderboardTableView.delegate = self
leaderboardTableView.dataSource = self
fetchUsers()
}
func fetchUsers() {
let queryRef = Database.database().reference().child("users").queryOrdered(byChild: "ranking").queryLimited(toLast: 50)
queryRef.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String : AnyObject]{
let user = User(dictionary: dictionary)
self.userModel.append(user)
}
DispatchQueue.main.async(execute: {
self.leaderboardTableView.reloadData()
})
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userModel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "leaderboardTableCell", for: indexPath) as! LeaderboardTableCell
if userModel.count > indexPath.row {
if let profileImageURL = userModel[indexPath.row].photoURL {
let url = URL(string: profileImageURL)!
var options = ImageLoadingOptions()
options.pipeline = pipeline
options.transition = .fadeIn(duration: 0.25)
Nuke.loadImage(
with: ImageRequest(url: url).processed(with: _ProgressiveBlurImageProcessor()),
options: options,
into: cell.userImage
)
}
}
cell.userName.text = userModel[indexPath.row].username
cell.watchTime.text = "\(String(describing: userModel[indexPath.row].watchTime!))"
cell.ranking.text = "\(indexPath.row + 1)"
cell.userImage.layer.cornerRadius = cell.userImage.frame.size.width/2
return cell
}
}
I thought it might be because I am using the same model name userModel in both my Profile page view and my Leaderboard view but when i changed the model name in my leaderboard view nothing changed. What else can I share to help? Thanks!
EDIT: here's my console output after printing out watchTime which is the integer I have rankings for:
HERES WHERE I OPEN LEADERBOARD PAGE FIRST:
Optional(28)
Optional(247)
Optional(0)
Optional(0)
Optional(0)
Optional(0)
AFTER I GO BACK AND CLICK TO VIEW LEADERBOARD AGAIN:
Optional(247)
Optional(28)
Optional(0)
Optional(0)
Optional(0)
Optional(0)
The issue here is related to this line of code...
let queryRef = Database.database().reference().child("users").queryOrdered(byChild: "ranking").queryLimited(toLast: 50)
Changing this limit to 10 makes the app work as expected, which is a temporary 'fix'.
If we figure out why that limit is causing issues I'll be sure to update this answer.

Synchronize database of Firebase with Swift 4 language

I am new at swift 4 programming. I try to show some data(actually they are child names) from database of firebase. When view did load, I can take them and show on tableView but it is not working real time. When I delete some data from firebase, I need to refresh(go back and load again) the viewController. If I refresh the tableView with timer, all data append from its end. If I, firstly, delete after append new data to tableView. It is jittering. Is there anyway to synchronize this tableView with firebase, without these problems?
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
ref.keepSynced(true)
let userRef = self.ref.child("Users").child(Username).child("Schedule")
userRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
self.myList.append(key)
self.CourseList.append(key)
self.LessonsTableView.reloadData()
}
// Lessons are taking and after, they are locating under phone's memory!
UserDefaults.standard.set(self.CourseList, forKey: "LessonsArray")
UserDefaults.standard.synchronize()
})
}
//Setting up our table
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = LessonsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! LessonsTableViewCell
cell.myLabell.text = myList[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let Dummy:String = myList[indexPath.row] as Any as! String
let Selected:String = (Dummy as NSString) as Any as! String
UserDefaults.standard.set(Selected, forKey: "Selected")
self.performSegue(withIdentifier: "NextPage", sender: self)
}
Firebase's task is a network task which normally runs on a background (lower priority) thread. But tableView.reloadData() is an UI task. So you probably need to execute UI related codes by following codes.
DispatchQueue.main.sync {
let snap = child as! DataSnapshot
let key = snap.key
self.myList.append(key)
self.CourseList.append(key)
self.LessonsTableView.reloadData()
}
it is giving an error like this,
let userRef = self.ref.child("Users").child(Username).child("Schedule")
userRef.observeSingleEvent(of: .value, with: { snapshot in
DispatchQueue.main.sync {
let snap = child as! DataSnapshot
let key = snap.key
self.myList.append(key)
self.CourseList.append(key)
self.LessonsTableView.reloadData()
}
UserDefaults.standard.set(self.CourseList, forKey: "LessonsArray")
UserDefaults.standard.synchronize()
})

Resources