Firebase queryEqual doesn't work - ios

I have the following hierarchy in my Firebase:
func invitedEvents() {
DataService.ds.REF_EVENTS.queryOrdered(byChild: "eventParticipant").observe(.childAdded, with: { (snap) in
print("KEEEY:\(snap.key)")
let value = snap.value as? NSDictionary
if snap.exists() , value?["eventParticipant"] != nil{
print("eventParticipant:\(value?["eventParticipant"])")
var valueArray = value?["eventParticipant"] as! [[String:AnyObject]]
for (index, element) in valueArray.enumerated() {
print("valueArray\(index): \(element)")
//self.selectedContacts.append(element as! Participant)
if element["partPhone"] as! String == "00 11 111 1111" {
print("KEY:\(snap.key) PHONE:\(element["partPhone"])")
self.eventKey = snap.key
print("snap.key:\(snap.key)")
//child("eventItem").child("eventOrganized").queryOrdered(byChild: snap.key).queryEqual(toValue: "1")
DataService.ds.REF_USERS.child("eventItem").child("eventOrganized").child(snap.key).observe(.value, with: { (snapshot) in
print("With Snapshot Key: \(snapshot)")
})
//DataService.ds.REF_USERS.queryOrdered(byChild: "provider").queryEqual(toValue: "email").observe(.value, with: { (snapshot) in
// print("With Provider:\(snapshot.value)")
//})
DataService.ds.REF_EVENTS.child(self.eventKey).observe(.value, with: { (snap) in
print(snap)
if snap.value is NSNull {
print("Event is not found!")
}
else {
let value = snap.value as? NSDictionary
print(value?["eventLocation"] ,
value?["eventCurrency"],
value?["eventDesc"],
value?["eventBeginDate"],
value?["eventEndDate"],
value?["eventCurrency"],
value?["eventLocation"],
value?["eventName"],
value?["eventDesc"],
value?["eventPrice"])
if
let eventAddress = value?["eventLocation"] ,
let eventCurrency = value?["eventCurrency"],
let eventDesc = value?["eventDesc"],
let eventBeginDate = value?["eventBeginDate"],
let eventEndDate = value?["eventEndDate"],
let eventImg = value?["eventCurrency"],
let eventLocation = value?["eventLocation"],
let eventName2 = value?["eventName"],
let eventNote = value?["eventDesc"],
let eventPrice = value?["eventPrice"]
{
let eventDetails = Event(
eventAddress: value?["eventLocation"] as! String,
eventCurrency: value?["eventCurrency"] as! String,
eventDesc: value?["eventDesc"] as! String,
eventBeginDate: value?["eventBeginDate"] as! String,
eventEndDate: value?["eventEndDate"] as! String,
eventImg: value?["eventCurrency"] as! String,
eventLikes: 1,
eventLocation: value?["eventLocation"] as! String,
eventName: value?["eventName"] as! String,
eventNote: value?["eventDesc"] as! String,
eventPrice: value?["eventPrice"] as! String,
eventCreated: value?["eventCreated"] as! String,
eventStatus: 0 as! Int?
)
//print(eventDetails)
let eventName = value?["eventName"] as! String
self.userPastEventNameList.append(eventName)
self.events.append(eventDetails)
print(eventAddress,eventCurrency,eventDesc, eventBeginDate, eventEndDate, eventImg, eventLocation, eventName2, eventNote, eventPrice)
}
else {
print("NULL FOUND")
}
}
DispatchQueue.main.async{
self.tableView.reloadData()
}
self.removeLoadingScreen()
})
//})
}
}
}
})
}
and snap.keys are in print("snap.key:(snap.key)"):
snap.key:-KePXAuTADuTLTsZ7qbe
snap.key:-KePjqMKrARQmp-gTss-
snap.key:-KePsCS5rB4lTwy6GWea
snap.key:-KePwdRQPFFX30_GDAOK
snap.key:-KePyNVghX4MkSxI_1fx
snap.key:-KePz200QsJp6CSb3bVN
If I'm using this, then I get a snapshot:
print("snap.key:\(snap.key)")
DataService.ds.REF_USERS.queryOrdered(byChild: "provider").queryEqual(toValue: "email").observe(.value, with: { (snapshot) in
print("With Provider:\(snapshot.value)")})
Result:
With Provider:Optional({
JIbrGLdfCDUpMUOHEu7KzRye8ZK2 = {
provider = email;
};
})
If I'm using this, then I don't get any result:
print("snap.key:\(snap.key)")
DataService.ds.REF_USERS.child("eventItem").child("eventOrganized").queryOrdered(byChild: snap.key).queryEqual(toValue: 1).observe(.value, with: { (snapshot) in ("With Snapshot Key:\(snapshot.value)")})
Result:
With Snapshot Key:Optional(<null>)
Somebody could help me? What am I doing wrong?

Right,
DataService.ds.REF_USERS.child("eventItem").child("eventOrganized").queryOrdered(byChild: snap.key).queryEqual(toValue: 1).observe(.value, with: { (snapshot) in ("With Snapshot Key:\(snapshot.value)")})
That doesn't work because you're missing a reference to the V9T3cEgEGPRmlkMQb32hxa5gG7L2 node.
If you want all the eventOrganized nodes with a value of 1 under that key then you need to use the following query.
DataService.ds.REF_USERS.child("V9T3cEgEGPRmIkMQb32hxa5gG7L2").child("eventItem").child("eventOrganized").queryOrderedByValue().queryEqual(toValue: 1).observe(.value, with: { (snapshot) in
print("Snapshot Value:\(snapshot.value)")
})
Obviously you don't want to reference V9T3cEgEGPRmlkMQb32hxa5gG7L2 directly, you should store this key in a variable for reusability's sake (maybe you have, I can't tell in your code). I think that's what you wanted.

Related

iterate over users in firebase database

I am trying to iterate over the users' information where and save it in an Object.
Here is my data
{
"users" : {
"ApC2wS444YbEEUt5BOpFjkn7YTD3" : {
"UserInfo" : {
"lat" : 123123,
"long" : 0,
"petAge" : 5,
"petNeme" : "zhshs"
}
},
"doRJjseSogNJrCQ55zMGlJwj6jh2" : {
"UserInfo" : {
"UID" : "doRJjseSogNJrCQ55zMGlJwj6jh2",
"lat" : 0,
"long" : 0,
"petAge" : 5,
"petName" : "BBB"
}
}
}
}
My code is like this
ref = Database.database().reference()
tableView.delegate = self
tableView.dataSource = self
let userID = Auth.auth().currentUser?.uid
ref = Database.database().reference()
self.ref.child("users").observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
for child in snapshot.children.allObjects as! [DataSnapshot] {
print(child.value)
let value = snapshot.value as! NSDictionary
let userInfo = value[0] as! NSDictionary
let UID = userInfo["UID"] as! String
let lat = userInfo["lat"] as! Double
let long = userInfo["long"] as! Double
let name = userInfo["petName"] as! String ?? ""
let age = userInfo["petAge"] as! Double
}
}) { (error) in
print(error.localizedDescription)
}
I don't really understand how I should handle snapshots and how to get the data properly
I was able to a fetch a single node but when I try to pass iterate, it brings me back the same node with every iteration
Any help?
In your example you use in a for loop this let userInfo = value[0] as! NSDictionary which later gives you only the first node: [0].
Also let me show you my example of retrieving data from a snapshot (in my case I have cards with autoId):
func loadDataFromDb(completion: #escaping ([Card])->()) {
var cards: [Card] = []
let userRef = getCurrentUserRef()
usersRef.child(userRef).child(Paths.cards).observeSingleEvent(of: .value) { (snapshot) in
for child in snapshot.children {
if let snapshot = child as? DataSnapshot,
let card = Card(snapshot: snapshot) {
cards.append(card)
}
}
completion(cards)
}
}
Also I have failable init in Card:
private enum Constants {
static let name = "name"
static let barcode = "barcode"
}
final class Card {
let name: String
let barcode: String
init(_ name: String, barcode: String) {
self.name = name
self.barcode = barcode
}
init?(snapshot: DataSnapshot) {
guard let value = snapshot.value as? [String : AnyObject],
let name = value[Constants.name] as? String,
let barcode = value[Constants.barcode] as? String else {
return nil
}
self.name = name
self.barcode = barcode
}
}

How to know if a Firebase has fetched a value

I have three functions getNewOrder(),storeOrderDetails(_ details:[String:String]) and getUserInfo(_ userID:String).
Function getNewOrder() is called first. It fetches new orders ( .childAdded values) and sends the dictionary to storeOrderDetails(_ details:[String:String]).
storeOrderDetails(_ details:[String:String])then segregate all the values and callsgetUserInfo(_ userID:String)` by passing it userID which was present in its details.
getUserInfo(_ userID:String) then fetches users details and returns user's
information.
However, the problem is [ userInfo = getUserInfo(_ userID:String) in storeOrderDetails(_ details:[String:String]) ] userInfo is always empty. Apparently func getUserInfo(_ userID:String) goes into a completion block after it has returned empty value.
I want these three functions to execute in sequential way.
Any advice is highly appreciated.
Please follow the below Links to review my code.
https://imgur.com/hNjvyDk
https://imgur.com/J0LMXMg
func childAdded(){
let ref = Database.database().reference().child("Orders").child(todaysDate)
ref.observe(.childAdded) { (snapshot) in
var details = [String:String]()
if let orderID = snapshot.key as? String {
ref.child(orderID).observeSingleEvent(of: .value, with: { (snap) in
self.newOrderTextView.text = ""
self.customerNameLabel.text = ""
self.customerPhoneLabel.text = ""
self.orderNumberLabel.text = ""
let enumerator = snap.children
while let rest = enumerator.nextObject() as? DataSnapshot {
details[rest.key as? String ?? ""] = rest.value as? String ?? ""
}
self.storeUserDetails(details)
})
}
}
}
func storeUserDetails(_ details:[String:String]){
if details["CustomerID"] != nil {
userInfo = getUserDetails(details["CustomerID"]!)
print(userInfo)
}
if !userInfo.isEmpty{
let order = OrderDatabase()
order.customerEmail = userInfo["Email"]!
order.customerName = userInfo["Name"]!
order.orderAcceptStatus = details["OrderStatus"]!
order.customerOrderNumber = details["orderNumber"]!
order.orderID = details["orderID"]!
order.time = details["Time"]!
order.customerFirebaseID = details["CustomerID"]!
self.orderDatabase[details["orderNumber"]!] = order
self.orderTable.reloadData()
}
}
func getUserDetails(_ userID:String) -> [String:String]{
var details = [String:String]()
let userDetailsReference = Database.database().reference().child("Users")
userDetailsReference.child(userID).observeSingleEvent(of: DataEventType.value, with: { (snapshot) in
if let dictionary = snapshot.value as? NSDictionary {
self.customerNameLabel.text = dictionary.value(forKey: "Name") as? String
self.customerPhoneLabel.text = dictionary.value(forKey: "Email") as? String
details["Name"] = dictionary.value(forKey: "Name") as? String
details["Email"] = dictionary.value(forKey: "Email") as? String
}
})
return details
}
From what I can see here, I am betting that the issue you are facing has to do with the fact that the methods are asynchronous. So one thing is not completely finished and some other method gets fired too soon. There are a few ways to deal with this issue. One is completion handlers, and the other is adding observers. Below is an example of doing both for Firebase. Here I'm asking a getLocationPhotos method to get all the photos from Firebase. Notice the observers and completion handler
func getLocationPhotos(coordinate:CLLocationCoordinate2D){
dbHandler.getImageFileNames(coordinateIn: coordinate) { (filenames) in
if filenames.isEmpty {
log.debug(String.warningGet + "filenames is empty")
return
}//if filenames.isEmpty
self.imageFiles = filenames.filter { $0 != "none" }
if self.imageFiles.isEmpty {
log.error(String.errorGet + "imageFiles array is empty")
return
}//if imageFiles.isEmpty
for file in self.imageFiles {
let reference = self.storageHandler.imageReference.child(file)
let download = self.imageView.sd_setImage(with: reference)
if let i = self.imageView.image {
self.imageArray.append(i)
self.collectionView.reloadData()
}//let i
download?.observe(.progress, handler: { (snapshot) in
guard let p = snapshot.progress else {
return
}//let p
self.progressView.progress = Float(p.fractionCompleted)
if self.progressView.progress == Float(1) {
self.progressView.isHidden = true
}
})//progress
download?.observe(.success, handler: { (snapshot) in
self.progressView.progress = 1
self.progressView.isHidden = true
self.collectionView.setNeedsLayout()
})//success
download?.observe(.failure, handler: { (snapshot) in
log.error(String.errorGet + "Error occured getting data from snapshot")
})//failure
}//for file
}//dbHandler

Access childAutoID to update selected child value in Firebase

In order to populate my tableView, I append items (created from a struct) to a local array:
func loadList() {
var newAnnotations: [AnnotationListItem] = []
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").queryOrderedByKey().observeSingleEvent(of: .value, with: {snapshot in
for item in snapshot.children {
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
newAnnotations.append(annotationItem)
}
annotationList = newAnnotations
self.tableView.reloadSections([0], with: .fade)
})
}
}
When I click a specific row, I am taken to a DetailViewController where it is only a large UITextView (named notes). The UITextView.text displayed is based on the selected indexPath.row and the "notes" value is retrieved from the array. Now the user is able to type some text and when they are done, the textViewDidEndEditing function is called:
func textViewDidEndEditing(_ textView: UITextView) {
notes.resignFirstResponder()
navigationItem.rightBarButtonItem = nil
let newNotes = self.notes.text
print(newNotes!)
}
Now I'd like to updateChildValues to newNotes to the child node "notes" in my JSON:
"users" : {
"gI5dKGOX7NZ5UBqeTdtu30Ze9wG3" : {
"annotations" : {
"-KuWIRBARv7osWr3XDZz" : {
"annotationSubtitle" : "1 Cupertino CA",
"annotationTitle" : "Apple Infinite Loop",
"notes" : "Does it work?!",
}
How can I access the selected autoID so I can update the specific notes node. So far the best I have is:
guard let uid = Auth.auth().currentUser?.uid else { return }
uidRef.child(uid).child("annotations").(somehow access the specific childID).updateChildValues(["notes": newNotes])
Any help will be greatly appreciated. Thanks in advance
UPDATE
The annotationListItem struct is created:
struct AnnotationListItem {
let key: String?
var annotationTitle: String?
let annotationSubtitle: String?
let notes: String?
let ref: DatabaseReference?
init(key: String = "", annotationTitle: String, annotationSubtitle: String, notes: String) {
self.key = key
self.annotationTitle = annotationTitle
self.annotationSubtitle = annotationSubtitle
self.notes = notes
self.ref = nil
}
init(snapshot: DataSnapshot) {
key = snapshot.key
let snapshotValue = snapshot.value as! [String: AnyObject]
annotationTitle = snapshotValue["annotationTitle"] as? String
annotationSubtitle = snapshotValue["annotationSubtitle"] as? String
notes = snapshotValue["notes"] as? String
ref = snapshot.ref
}
init(Dictionary: [String: AnyObject]) {
self.key = Dictionary["key"] as? String
self.annotationTitle = Dictionary["annotationTitle"] as? String
self.annotationSubtitle = Dictionary["annotationSubtitle"] as? String
self.notes = Dictionary["notes"] as? String
self.ref = nil
}
func toAnyObject() -> Any {
return [
"annotationTitle": annotationTitle as Any,
"annotationSubtitle": annotationSubtitle as Any,
"notes": notes as Any
]
}
}
UPDATE
This is how the annotationListItem is created to be stored in Firebase:
// Using the current user’s data, create a new AnnotationListItem that is not completed by default
let uid = Auth.auth().currentUser?.uid
guard let email = Auth.auth().currentUser?.email else { return }
let title = placemark.name
let subtitle = annotation.subtitle
let notes = ""
// declare variables
let annotationListItem = AnnotationListItem(
annotationTitle: title!,
annotationSubtitle: subtitle!,
notes: notes)
// Add the annotation under their UID
let userAnnotationItemRef = uidRef.child(uid!).child("annotations").childByAutoId()
userAnnotationItemRef.setValue(annotationListItem.toAnyObject())
I think you only need to do this:(since you have declared the note as global)
guard let uid = Auth.auth().currentUser?.uid else { return }
uidRef.child(uid).child("annotations").(note.key).updateChildValues(["notes": newNotes])
inside the method where you change the notes
If I am not mistaken you are creating an array of a custom object?
var newAnnotations: [AnnotationListItem] = []
You could do something like: var newAnnotations: [(key: String, value: [String : Any])] = [] (Any only if you are going to have Strings, Integers, ect. If it'll only be String then specify it as a String.
Accessing the key would be: newAnnotations[indexPath.row].key in your cellForRowAtIndex of your tableView. Accessing values would be: newAnnotations[indexPath.row].value["NAME"].
You can have a separate array that holds the key and just append it at the same time as your population:
for item in snapshot.children {
guard let itemSnapshot = task as? FDataSnapshot else {
continue
}
let id = task.key //This is the ID
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
newAnnotations.append(annotationItem)
}
Another thing you could do is go up one more level in your firebase call:
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").observeSingleEvent(of: .value, with: {snapshot in
if snapshot is NSNull{
//Handles error
} else{
if let value = snapshot.value as? NSDictionary{ //(or [String: String]
//set localDictionary equal to value
}
}
self.tableView.reloadSections([0], with: .fade)
})
}
And then when you select a row: let selectedItem = localDictionary.allKeys[indexPath.row] as! String //This is the ID you pass to your viewController.

Issue with a query Firebase - Swift

I am currently trying to do a query to obtain the information for displaying a list of followers/following. I am currently getting an error as such: Could not cast value of type 'FIRDataSnapshot' (0x108967580) to 'NSArray' (0x10b3e3e28). I am new to firebase and still learning about queries, my query is as follows:
if isFollowers == true {
self.isFollowers = true
ref = ref.child("followers")
let query = ref.queryOrdered(byChild: "userFollow")
query.queryLimited(toLast: 5).observeSingleEvent(of: .value, with: { (snapshot : FIRDataSnapshot) in
if snapshot.childrenCount > 0 {
for s in snapshot.children.allObjects.first as! [FIRDataSnapshot] {
print("are we even in here?")
let item = s.value as! Dictionary<String,AnyObject?>
let user = FollowInfoForUser(dictionary: item as Dictionary<String,AnyObject>)
self.userFollowIndex.insert(user, at: 0)
print(self.userFollowIndex)
//print(self.userFollowIndex.count)
self.collectionView.reloadData()
}
} else {
print("sorry no followers for you to see")
}
})
}
The line of error is as such:
for s in snapshot.children.allObjects.first as! [FIRDataSnapshot] {
my tree is also as follows:
-Users
---UserUID
-----Followers
--------FollowAutoChild
----------------userFollow
----------------userFollowKey
I am trying to store the FollowAutoChild information
My whole query function code is as follows:
func setValues(isFollowers : Bool, isFollowing : Bool, isViewingSelf : Bool, isViewingOther : Bool, key : String) {
var ref = FIRDatabase.database().reference().child("users")
if isViewingSelf {
print("we are viewing us")
ref = ref.child(FIRAuth.auth()!.currentUser!.uid)
} else
if isViewingOther {
print("we are viewing them")
ref = ref.child(key)
}
if isFollowers == true {
self.isFollowers = true
ref = ref.child("followers")
let query = ref.queryOrdered(byChild: "userFollow")
query.queryLimited(toLast: 5).observeSingleEvent(of: .value, with: { (snapshot : FIRDataSnapshot) in
if snapshot.childrenCount > 0 {
for s in snapshot.children.allObjects.first as! [FIRDataSnapshot] {
print("are we even in here?")
let item = s.value as! Dictionary<String,AnyObject?>
let user = FollowInfoForUser(dictionary: item as Dictionary<String,AnyObject>)
self.userFollowIndex.insert(user, at: 0)
print(self.userFollowIndex)
//print(self.userFollowIndex.count)
self.collectionView.reloadData()
}
} else {
print("sorry no followers for you to see")
}
})
}
}
The error is appearing because you are casting the first element of snapshot.children, which is just one element(An FIRDataSnapshot in this case), as an array of FIRDataSnapshot's. Either cast all the objects as an array of FIRDatasnapShot's, or the first one as an FIRDataSnapshot but not both at the same time.
Your for_in loop should look like this
for s in snapshot.children.allObjects as! [FIRDataSnapshot] {
print("are we even in here?")
let item = s.value as! [String: AnyObjcet]
let user = FollowInfoForUser(dictionary: item)
self.userFollowIndex.insert(user, at: 0)
print(self.userFollowIndex)
//print(self.userFollowIndex.count)
self.collectionView.reloadData()
}
you are doing wrong casting

Populating tableView with data obtained from Firebase

Here is my scenario:
1) datasource to my tableView is an array called books - var books:[BookItem]
2) I populate books anytime some data in db has changed
3) I create BookItem by joining data from two tables, since BookItem consists of some user-specific data and some general data about the book itself.
I wrote a method called createDataSource that takes care of populating books array with BookItems
func createDataSource()
{
self.books.removeAll()
let userId:String = UserDefaults.standard.value(forKey: "UserId") as! String
let bookForUserRef = self.ref!.child("users").child(userId).child("userbooks")
bookForUserRef.observe(.value, with: { snapshot in
var i = 0
for element in snapshot.children
{
i = i + 1
let item:FIRDataSnapshot = element as! FIRDataSnapshot
let postDict = item.value as! [String : AnyObject]
let key = item.key
let booksRef = self.ref!.child("books").child(key)
booksRef.observe(.value, with: { snapshot in
let bookItem = (snapshot as! FIRDataSnapshot).value as! [String : AnyObject]
let id = booksRef.key
print(id)
let record = [bookItem["title"] as! String, bookItem["author"] as! String, "0", "\(bookItem["pagesCount"] as! Int)"]
let item = BookItem(title: bookItem["title"] as! String, author: bookItem["author"] as! String, pagesCount: bookItem["pagesCount"] as! Int, currentPage: postDict["currentPage"] as! Int, language: bookItem["language"] as! String, year: nil, ISBN: nil, id: id, added: postDict["added"] as? String ?? "21")
self.books.append(item)
self.tableView.reloadData()
})
}
})
}
Anytime I call this method, all existing elements gets duplicated in books array.
I thought it might have something to do with for loop, but after debugging it seems alright.
I suppose there is something specific with observers in firebase that duplicates the elements. I am rather new to firebase and I suppose my approach might be a bit messed and confusing.
What should be modified in the createDataSource() method, to get rid of elements duplication?
Also, is my approach to working with firebase and tableView correct?
Thanks to #ntoonio comment I got it solved
func createDataSource()
{
let userId:String = UserDefaults.standard.value(forKey: "UserId") as! String
let bookForUserRef = self.ref!.child("users").child(userId).child("userbooks")
bookForUserRef.observe(.value, with: { snapshot in
var i = 0
self.books.removeAll()
for element in snapshot.children
{
i = i + 1
let item:FIRDataSnapshot = element as! FIRDataSnapshot
let postDict = item.value as! [String : AnyObject]
let key = item.key
let booksRef = self.ref!.child("books").child(key)
booksRef.observe(.value, with: { snapshot in
let bookItem = (snapshot as! FIRDataSnapshot).value as! [String : AnyObject]
let id = booksRef.key
print(id)
let record = [bookItem["title"] as! String, bookItem["author"] as! String, "0", "\(bookItem["pagesCount"] as! Int)"]
let item = BookItem(title: bookItem["title"] as! String, author: bookItem["author"] as! String, pagesCount: bookItem["pagesCount"] as! Int, currentPage: postDict["currentPage"] as! Int, language: bookItem["language"] as! String, year: nil, ISBN: nil, id: id, added: postDict["added"] as? String ?? "21")
self.books.append(item)
self.tableView.reloadData()
})
}
})
}

Resources