Retrieving Data from Firebase under ChildByAutoId and adding to array - Xcode - Swift - ios

Here is my Database from Firebase:
Im trying to get the data for all purchases and quantities under "Tgb9MyxTfdTRd9tQhInsjNRXoPL2" and add to an array.
Here is my code:
func fetchPurchase(withUID uid: String, completion: #escaping (Purchase) -> ()) {
Database.database().reference().child("purchases").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dict = snapshot.value as? [String: Any] else { return }
print("dict-->", dict)
let purchase = Purchase(uid: uid, dictionary: dict)
print("purchase-->", purchase)
completion(purchase)
}) { (err) in
print("Failed to fetch purchase from database:", err)
}
}
This is the print out for print("dict-->", dict):
dict--> ["-LzjaFBgD3ATl7e8uR2-": {
purchase = "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzk4OTUzMjEyODM=";
quantity = 1;
}, "-LzjaFBiAmrj4m3ZS8m4": {
purchase = "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzk4OTUzMDk2OTk=";
quantity = 2;
}]
This is the print out for print("purchase-->", purchase):
purchase--> Purchase(uid: "Tgb9MyxTfdTRd9tQhInsjNRXoPL2", purchases: "", quantities: "")
The value dict holds the data I need, but I cant get past that to put the data into an array to display it?
How can I get the purchase and quantity data into their own arrays?
Please help!

func fetchPurchase(withUID uid: String, completion: #escaping (Purchase) -> ()) {
Database.database().reference().child("purchases").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
self.YourArrayList.removeAll()
for dict in snapshot.children.allObjects as! [DataSnapshot]
let purchase = Purchase(uid: uid, dictionary: dict)
self.YourArrayList.append(purchase)
}
}
completion(purchase)
}) { (err) in
print("Failed to fetch purchase from database:", err)
}
}
}

Related

Completion handler needed for calling data from firebase?

Im trying to call data from firebase. The problem is, the data is deeply nested and I don't think I can change that.
So I'm attempting to call values from firebase, which I can then use to reference new values.
The problem arises when my for loop is not finished before the next stage is called, meaning my dictionary count for the next stage is 0, so my next function is not called?
Is there a way to do this sufficiently?
Please help?
Heres my code:
func fetchBuyer(search: String, user: String, completion: #escaping ([Post]) -> (), withCancel cancel: ((Error) -> ())?) {
let ref = Database.database().reference().child("posts").child(user).child(search).child("purchases")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionaries = snapshot.value as? [String: Any] else {
completion([])
return
}
let keys: [String] = dictionaries.map({ $0.key })
var newdictionaries = [String: String]()
for i in keys {
let newref = Database.database().reference().child("posts").child(user).child(search).child("purchases").child(i).child("purchaser")
newref.observeSingleEvent(of: .value, with: { (snapshot) in
newdictionaries[i] = snapshot.value as? String
print("THESE ARE MY PURCHASES ID-->", newdictionaries.values)///prints out ["-M0pTHtZXYUVQT7DCLj-", "-M0pU79uQCCnBunAEkJN"]
})
}
var buyerPosts = [Post]()
print("newdictionaries.count--->", newdictionaries.count)//this print is 0
newdictionaries.forEach({ (postId, value) in
Database.database().fetchPost(withUID: user, postId: postId, completion: { (post) in
buyerPosts.append(post)
if buyerPosts.count == newdictionaries.count{
completion(buyerPosts)
}
})
})
}) { (err) in
print("Failed to fetch posts for buyers:", err)
cancel?(err)
}
}
Attempted answer:
let g = DispatchGroup() //// 1
for i in keys{
g.enter() //// 2
let newref = Database.database().reference().child("posts").child(user).child(search).child("purchases").child(i).child("purchaser")
print("now")
newref.observeSingleEvent(of: .value, with: { (snapshot)
newdictionaries[i] = snapshot.value as? String
print("print new dictionaries-->", newdictionaries)
// complete here
Database.database().fetchPost(withUID: user, postId: newdictionaries[i]!, completion: { (post) in
buyerPosts.append(post)
g.leave() //////// 3
})
})
}
g.notify(queue: DispatchQueue.main) {
print("finished!!!")
completion(buyerPosts)
}
You need a dispatch group and nest the calls
let g = DispatchGroup() //// 1
for i in keys{
g.enter() //// 2
let newref = Database.database().reference().child("posts").child(user).child(search).child("purchases").child(i).child("purchaser")
newref.observeSingleEvent(of: .value, with: { (snapshot)
newdictionaries[i] = snapshot.value as? String
// complete here
Database.database().fetchPost(withUID: user, postId: postId, completion: { (post) in
buyerPosts.append(post)
g.leave() //////// 3
})
})
}
/// 4
g.notfiy(queue.main) {
completion(buyerPosts)
}

Adding and Retrieving multiple values with Firebase under "User"

Im uploading my data like this:
By using these function:
extension Database {
func createPurchase (purchaseID : String, quantity: String, completion: #escaping (Error?) -> ()){
guard let uid = Auth.auth().currentUser?.uid else { return }
let userPostRef = Database.database().reference().child("purchases").child(uid) //taken off auto ID
let values = ["purchase" : purchaseID, "quantity" : quantity ] as [String: Any]
userPostRef.updateChildValues(values) { (err, ref) in
if let err = err {
print("Failed to save purchase to database", err)
completion(err)
return
}
completion(nil)
}
}
}
.
In Viewcontroller 1:
func callID (){
let itemCount = CartController.shared.items.count
for i in 0...itemCount-1 {
let quantity = CartController.shared.items[i].quantity
let quantity1 = String(quantity)
let item = CartController.shared.items[i].product.id
Database.database().createPurchase(purchaseID: item, quantity: quantity1 ) { (err) in
if err != nil {
print("error uploading purchase to Firebase ")
return
}
}
}
}
callID uses createPurchase to take the sku number of each product and its quantity and uploads to firebase.
However, it overrides what was previously uploaded. So when I load more than 1 product. It only leaves the last products sku and quantity.
For this example, I started with 3 product, but only 1 (the last one) got uploaded.
How can I upload all the data. i.e stop this overide?
Here is how Im retrieving it:
extension Database {
func fetchPurchase(withUID uid: String, completion: #escaping (Purchase) -> ()) {
Database.database().reference().child("purchases").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let userDictionary = snapshot.value as? [String: Any] else { return }
let purchase = Purchase(uid: uid, dictionary: userDictionary)
completion(purchase)
}) { (err) in
print("Failed to fetch purchase from database:", err)
}
}
}
.
In viewcontroller 2:
struct Purchase {
let uid: String
let purchases: String
let quantities: String
init(uid: String, dictionary: [String: Any]) {
self.uid = uid
self.purchases = dictionary["purchase"] as? String ?? ""
self.quantities = dictionary["quantity"] as? String ?? ""
}
}
var myPurchases: [Purchase] = []
private func fetchPurchaseForCurrentUser() {
guard let currentLoggedInUserId = Auth.auth().currentUser?.uid else { return }
Database.database().fetchPurchase(withUID: currentLoggedInUserId, completion: { (purchases) in
self.myPurchases.append(purchases)
print("myPurchases", self.myPurchases)
})
}
This is the printout I get:
myPurchases [myShop.Purchase(uid: "Tgb9MyxTfdTRd9tQhInsjNRXoPL2", purchases: "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzk4OTUzMDk2OTk=", quantities: "2")]
This only shows the one product, however Im needing to have multiple products.
Please Help!
EDIT:
This is a screenshot of when I use ChildByAutoId().
I get all products (2 in this case).
But I am unable to retrieve the information from the snapshot and set to my text fields?
Prepare an array from multiple records, sample code:
var myPurchases: [Purchase] = []
for (key, value) in userDictionary {
guard let dic = value as? [String: Any] else {
continue
}
let purchaseModel = Purchase(uid: key, dictionary: dic)
myPurchases.append(purchaseModel)
}

Appending to an array in Firebase - Asynchronous and reloadData()

I am having trouble with the asychronous nature of Firebase, particulary appending to an array from within an observe function.
Any suggestions or help would be much appreciated :)
The comp that is appended to the users array in the selectUsersComp function disappears when the firebase observe function is exited, even though I reload the data in the collectionView.
I have tried using Dispatch.main.async but it has not helped. I have a Firebase observe function inside another Firebase observe function. Does this change things?
fileprivate func fetchStartedComps() {
let ref = Database.database().reference().child("startedComps")
ref.queryOrdered(byChild: "creationDate").observe(.value, with: {
(snapshot) in
guard let dictionaries = snapshot.value as? [String : Any] else { return }
dictionaries.forEach({ (key, value) in
guard let compDictionary = value as? [String: Any] else { return }
let comp = StartedComp(Id: key, dictionary: compDictionary)
self.selectUsersComp(comp: comp)
})
self.filteredStartedComps = self.startedComps
self.collectionView?.reloadData()
}) { (err) in
print("Failed to fetch comps for search", err)
}
}
func selectUsersComp(comp: StartedComp) {
guard let userId = Auth.auth().currentUser?.uid else { return }
let ref = Database.database().reference().child("startedComps").child(comp.title).child("invitedUsers")
ref.observe(.value, with: { (snapshot) in
guard let dictionaries = snapshot.value as? [String : Any] else { return }
dictionaries.forEach({ (key, value) in
if key == userId {
self.startedComps.append(comp)
}
})
self.collectionView?.reloadData()
}) { (err) in
print(err)
}
}

how to get latest message from firebase database in ios

I have don't know what the problem please help me. When I get a particular message from firebase database then the value is getting but my app got a crash on one line.So please tell me what I do wrong in my code.below is my function.We also provide the screenshot of the error.
func getLatestMessageFromFirebase(token:String,completionmessage: #escaping (_ message:String) -> Swift.Void)
{
print("getModelFromFirebase")
var message:String=""
ref.child("chatmessage/devicetoken/").child(token).queryLimited(toLast: 1).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
if value?["message"] as? String != ""
{
DispatchQueue.main.async
{
message = (value?["message"] as? String)! //My app stop on this line
completionmessage(message)
}
}
})
{ (error) in
print(error.localizedDescription)
}
}
func callAPI()
{
let response = (jsonResult.object(forKey: "chatListArr") as? NSArray)!
if response.count > 0
{
for i in 0..<response.count
{
let dict = response[i] as! NSDictionary
let chatlist = ChatList(dict: dict)
self.arr_list.append(chatlist)
}
for i in 0..<self.arr_list.count
{
let chatlist = self.arr_list[i]
self.getLatestMessageFromFirebase(token: chatlist.token, completionmessage: { (message) in
self.arr_list[i].msg = message
})
}
self.table_view.reloadData()
}
}
Please help me.
Thanks in Advance.
First of all you should clean your code up a bit, you do a couple of things which would be considered anti patterns
func getLatestMessageFromFirebase(token:String,completionmessage: #escaping (_ message:String) -> Swift.Void)
{
ref.child("chatmessage/devicetoken/").child(token).queryLimited(toLast: 1).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
for snap in snapshot.children.allObjects as [DataSnapshot] {
let value = snap.value as? [String: Any] ?? [:] // A good way to unwrap optionals in a single line
if let message = value["message"] as? String {
DispatchQueue.main.async {
completionmessage(message)
}
}
}
})
{ (error) in
print(error.localizedDescription)
}
}
With the above code your app shouldnt crash. And if there is a message AND it is a string (which might have been your problem before) then your callback will fire.

How do I add a google maps marker when Pressing a button?

Is it possible to fetch all child nodes from parent in firebase db?
Trying to say this:
I want to get all posts at the same time.
It fetches videos but only the current users videos. I want to fetch ALL users videos at the same time.
Here's some code to get more understanding of what I'm doing:
fileprivate func fetchAllPost() {
fetchPosts()
fetchAllPostsFromUserIds()
}
fileprivate func fetchAllPostsFromUserIds() {
guard let uid = FIRAuth.auth()?.currentUser?.uid else { return }
FIRDatabase.database().reference().child("posts").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let userIdsDictionary = snapshot.value as? [String: Any] else { return }
userIdsDictionary.forEach({ (key, value) in
FIRDatabase.fetchUserWithUid(uid: key, completion: { (user) in
self.fetchPostsWithUser(user: user)
})
})
}) { (err) in
print("failed to fetch following users ids:", err)
}
}
var posts = [Post]()
fileprivate func fetchPosts() {
guard let currentUserID = FIRAuth.auth()?.currentUser?.uid else { return }
FIRDatabase.fetchUserWithUid(uid: currentUserID) { (user) in
self.fetchPostsWithUser(user: user)
}
}
fileprivate func fetchPostsWithUser(user: User) {
let ref = FIRDatabase.database().reference().child("posts/\(user.uid)/")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
self.collectionView?.refreshControl?.endRefreshing()
guard let dictionaries = snapshot.value as? [String: Any] else { return }
dictionaries.forEach({ (key,value) in
guard let dictionary = value as? [String: Any] else { return }
var post = Post(user: user, dictionary: dictionary)
post.id = key
guard let uid = FIRAuth.auth()?.currentUser?.uid else { return }
FIRDatabase.database().reference().child("likes").child(key).child(uid).observe(.value, with: { (snapshot) in
if let value = snapshot.value as? Int, value == 1 {
post.hasLiked = true
} else {
post.hasLiked = false
}
self.posts.append(post)
self.posts.sort(by: { (p1, p2) -> Bool in
return p1.creationDate.compare(p2.creationDate) == .orderedDescending
})
self.collectionView?.reloadData()
}, withCancel: { (err) in
print("Failed to fetch info for post")
})
print(self.posts)
})
}) { (error) in
print("Failed to fetch posts", error)
}
}
I don't know Swift, but you could fetch FIRDatabase.database().reference().child("posts") and then iterate over children.

Resources