Retrieving data from firebase depending on (City) - ios

I am working on iOS application using swift, and I have the following data in firebase dashboard of the application
Users =
{
"07a5fa11-2a09-455b-92bf-a86dcd9d3e3e" =
{
Name = "Hissah";
Category = "Art & Designe";
City = "Riyadh";
Email = "H#him.fm";
ShortDescription = "";
};
"08e5443c-cdde-4fda-8733-8c4fce75dd34" =
{
Name = "Sara";
Category = "Cheefs";
City = "Dubai";
Email = "Sara#gmail.com";
ShortDescription = "best cake ever . ";
};
How can I retrieve the (Name) of the users whose (City) is "Riyadh" to a table view?
Thanks in advance.

Tossing this in the ring as it's a straightforward answer and addresses a dataSource that can be used to populate a tableView
let ref = Firebase(url:"https://your-app.firebaseio.com/users")
ref.queryOrderedByChild("City").queryEqualToValue("Riyadh")
.observeEventType(.Value, withBlock: { snapshot in
//iterate over all the values read in and add each name
// to an array
for child in snapshot.children {
let name = child.value["Name"] as! NSString
self.tableViewDataSourceArray.append(name)
}
//the tableView uses the tableViewDataSourceArray
// as it's dataSource
self.tableView.reloadData()
})
Edit: A follow up comment asked how to add the text to a NSTextView
ref.queryOrderedByChild("City").queryEqualToValue("Riyadh")
.observeEventType(.Value, withBlock: { snapshot in
//iterate over all the values and add them to a string
var s = String()
for child in snapshot.children {
let name = child.value["Name"] as! NSString
s += name + "\n" // the \n puts each name on a line
}
//add the string we just build to a textView
let attrString = NSAttributedString(string: s)
self.myTextView.textStorage?.appendAttributedString(attrString)
})

With your current node "Users" you would have to download all users and individually check to see which ones have the city "Riyadh". This would be a waste because you would be reading a lot of data you might not need.
If searching for users by city is a main feature of your app I would make another node "Cities" which would contain a list of cities. Each city node would then contain a list of all the users in that city and you could query that node. Then if you need more information on those users you know which specific people to look up in your "Users" node. You could then use this information however you see fit in your table view.
Cities:
{
"Riyadh":
{
"07a5fa11-2a09-455b-92bf-a86dcd9d3e3e":true
},
"Dubai":
{
"08e5443c-cdde-4fda-8733-8c4fce75dd34":true
}
},
Users:
{
"07a5fa11-2a09-455b-92bf-a86dcd9d3e3e":
{
Name: "Hissah";
Category: "Art & Designe";
City: "Riyadh";
Email: "H#him.fm";
ShortDescription: "";
};
"08e5443c-cdde-4fda-8733-8c4fce75dd34":
{
Name: "Sara";
Category: "Cheefs";
City: "Dubai";
Email: "Sara#gmail.com";
ShortDescription: "best cake ever . ";
};
Further reading here, where it talks about denormalizing data:
https://www.firebase.com/docs/web/guide/structuring-data.html

Related

How to get a specific string value out of a json response from firebase

I have this data structure and I can't extract the right value:
users
private
userID
birthday: "birthdayValue"
username: "nathan"
firstName: "Nathan"
etc...
I'm making a search feature in my app to search for users via their username through the firebase realtime database:
let reference = Database.database().reference()
if(searchText != ""){
reference.child("users").child("private").queryOrdered(byChild: "username").queryStarting(atValue: searchText).queryEnding(atValue: searchText + "\u{f8ff}").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.value is NSNull{
//handles errors
return
}
else{
if let user = snapshot.value as? NSDictionary {
for child in user{
print(child.key)
print(child.value)
}
}
else{
//null
}
}
})
at the moment the two print statements are printing these two results in the console every time I search:
wnszfmHilqNl6PG9khWtWkKUPtF3
{
birthday = 100;
dateCreated = "1579543450313.94";
description = nil;
email = "email#email.com";
firstName = Nathan;
instagramLink = nil;
lastLogin = "1579543450313.988";
lastName = Ellis;
profilePicURL = "url";
twitchLink = nil;
username = nathan;
youtubeLink = nil;
}
Which is expected, it prints the usersID (the key) and the value of the snapshot as a NSDictonary. I'm only interested in getting the username, nothing else. How would I extract the username out of this firebase snapshot so I can add their username as a string to an array for my search controller?
Obviously it needs to be dynamic as the userID will always be different.
Would I need to change my data model?
Your child.value seems to be a dictionary as well, so you can access it by:
if let valueDict = child.value as? [String: AnyObject] {
if let username = valueDict["username"] as? String {
// append username to results
print(username)
}
}
To print just the username, the smallest possible change is:
print(resultsLocalArray["username"])
This will fine, but will still retrieve the entire user node to the client, which uses more bandwidth than strictly needed.
If you find yourself frequently needing just the username of a user, or maybe even a list of username values across users, you might want to consider storing a node with just user names. So something like:
users
userID: "nathan"
But in your current setup you only retrieve the node for a single user, so I doubt the bandwidth savings are worth the additional complexity.

Get individual array Items

I am reading data from a firebase DB and storing it in a message object, How can I then access each element in that array? i.e how can I use the City string as I wish to assign that to a label. Same with each other element in the array.
firebaseDB.collection("user").document(key).collection("address").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
}
else {
self.dataArr.removeAll()
for document in querySnapshot!.documents {
//print("\(document.documentID) => \(document.data())")
let msgdata = document.data() as! [String:Any]
var msgObj = Details()
if let city = msgdata["city"] as? String {
msgObj.city = city
}
if let country = msgdata["country"] as? String {
msgObj.country = country
}
if let county = msgdata["county"] as? String {
msgObj.county = county
}
if let lineOne = msgdata["lineOne"] as? String {
msgObj.lineOne = lineOne
}
if let lineTwo = msgdata["lineTwo"] as? String {
msgObj.lineTwo = lineTwo
}
if let postCode = msgdata["postCode"] as? String {
msgObj.postCode = postCode
}
self.dataArr.append(msgObj)
}
}
}
I will need to access each element as I have another function which will take each element and place it on a label in my ViewController
Something like this is what I wish to have
func DisplayAddress(){
city.text = city
postCode.text = postCode
}
I may be totally reading the question wrong but in trying to read into your question, I think the terminology may be where clarification is needed; Object vs Array
An object has properties - lets examine the Details() object
var msgObj = Details()
which contains address information for one user. So conceptually this is how it would be represented in FireStore
users
this_user
address
city: "some city"
country: "some country"
county: "some county"
line1: "line one"
the 'documents' are the items stored within the address collection
city: "some city"
country: "some country"
county: "some county"
line1: "line one"
and your Details() object has properties that correspond to those documents and stores them as properties within the object; city, county etc
msgObj.city = city
msgObj.country = country
On the other hand, an array contains a series of objects, not properties. e.g. an array would generally not contain city, country etc, but it would contain a series of Details() objects and each of those Detail() objects has it's properties of city, country etc. For example, suppose you want to work with addresses of several different users - you would create a Details() object for each user, which contains their address information and append each one to an array.
self.dataArry[0] = the Details() objects of one user
self.dataArry[1] = the Details() object of another user
self.dataArry[2] = the Details() object of a third user
You could then, for example, display the users within a certain radius of this user, or send them all an email etc.
To answer your question, if you are working with a single users address information there is no need for an array, you can simply store it as a single Details() object variable within the class.
class ViewController: UIViewController {
var myUserAddress = Details()
func to get this users address documents from FireStore {
if let city = msgdata["city"] as? String {
self.myUserAddress.city = city
}
if let country = msgdata["country"] as? String {
self.myUserAddress.country = country
}
//remember that the properties are only valid from here on
//as FireStore is asychronous
self.DisplayCity()
self.DisplayLocation()
}
//and then later on when you want to display those properties
func DisplayCity() {
let city = self.myUserAddress.city
print(city)
}
func DisplayLocation() {
let lon = self.myUserAddress.logitude
let lat = self.myUserAddress.latitude
//show the location on a map via lon & lat
}

How can I find a specific Product id by sending Product Name?

I use Firebase For My Store App. I want to find a Product's Details by taking a product name for the user. My JSON format looks like this:
{
product :
electronic =
a = {
pname = "iphone 5"
pprice = "20000"
pdescription = "Details....." }
b = {
pname = "iphone 6"
pprice = "30000"
pdescription = "Details....." }
}
cloths =
a = pname = "shirt"
pprice = "200"
pdescription = "Details....." }
b = {
pname = "pents"
pprice = "300"
pdescription = "Details....." }
}
Now, suppose I have the name iphone 5, then how can I find out the other details of the product?
Try this :-
FIRDatabase.database().reference().child("product/electronic").queryOrderedByChild("pname").queryEqualToValue("iphone 5").observeSingleEventOfType(.Value , withBlock : {(snap) in
if let snapDict = snap.value as? [String:AnyObject]{
for each in snapDict{
print(each.0) // product key
print(each.1) //product details
}
}
})
import Firebase
FIRApp.configure()
ref = FIRDatabase.database().reference()
let prod_query = "iphone 5"
ref.observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let product_enum = snapshot.children
while let product = product_enum.nextObject() as? FDataSnapshot {
product.queryEqualToValue(child:"\(prod_query)").observeSingleEventOfType(.Value, withBlock: { (snap) in
let pid = snap.key as! String
let pprice = snap.value!["pprice"] as! Int
let pdescription = snap.value!["pdescription"] as! String
})
}
})
This implies that you know what the product letter is so that you can pull the correct name, price, and description.
The while loop will iterate through the different types of products (electronics, cloths, etc) and perform a query searching for a product ID that contains the child with the pname you're looking for.
Firebase suggests that instead of using .Value, it's better to use .ChildAdded since it accomplishes the same goal while managing new objects added. But since it appears you are trying to view static data, .Value works just fine.
This should serve as an excellent example as to how you can retrieve data using Firebase. But I suggest checking out the documentation on your own just in case you have further questions.
While I really don't mind looking this information up... this site is used in order to gain a better understanding of code, rather than existing as a collection of personal assistants.
Showing research efforts within your question can go a long way.

How to retrieve data of current user from firebase

I'm, developing a simple app to store users images. User authorized via Facebook, and the interact with app.
I stuck with retrieving data from current user.
In my firebase for now I have such structure:
I decided to get user data by uid.
So I initialized variable
let userID = FIRAuth.auth()?.currentUser?.uid
Then I retrieve an array of objects from my database
URL_BASE.child("profile").observeEventType(.Value, withBlock: { (snapshot) in
print (snapshot.value as! [String:AnyObject]!)
})
}
In my output I have this:
"-KJSKE4a2y-okl71vDSx": {
Interest = men;
gender = female;
name = girlsname1;
photo = "http://i.imgur.com/VAWlQ0S.gif";
surname = girlsurname1;
}, "-KJSKE4b8TlvxV-urHQo": {
Interest = men;
gender = female;
name = girlsname6;
photo = "http://media.tumblr.com/tumblr_lnb9aozmM71qbxrlp.gif";
surname = girlsurname6;
And so on
It is ok, but my goal is to print data of currently authorized user. I have an idea of sorting all objects by uid, but I think it is irrational. Hope there is simpler solution.
Authorized users stores in users database. profile database consist of user parameters I'm getting from Facebook.
let userID : String = (FIRAuth.auth()?.currentUser?.uid)!
print("Current user ID is" + userID)
self.dbRef?.child("profile").child(userID).observeSingleEvent(of: .value, with: {(snapshot) in
print(snapshot.value)
let userEmail = (snapshot.value as! NSDictionary)["addedByUser"] as! String
print(userEmail)
})
In Firebase, all users have a uid as you know. The uid should be used as the key for each user node in /users.
uid_0
gender: male
name: Leroy
uid_1
gender: female?
name: pat
This is a common design pattern in Firebase.
With that, you can simply get any user by their uid
let thisUserRef = usersRef.childByAppendingPath("the uid")
thisUserRef.observeSingleEventOfType(.Value... {
let gender = snapshot.value["gender"]
}

Firebase in Swift nested query not working properly

I have a JSON structure like the following:
{
"groups" : {
"-KAv867tzVgIghmr15CM" : {
"author" : "ruben",
"name" : "Item A"
},
"-KAv87nqLEG1Jtc04Ebn" : {
"author" : "ruben",
"name" : "Item B"
},
"-KAv88yZe8KTfkjAE7In" : {
"author" : "ruben",
"name" : "Item C"
}
},
"users" : {
"rsenov : {
"avatar" : "guest",
"email" : "ruben#ruben.com",
"groups" : {
"-KAv867tzVgIghmr15CM" : "true",
"-KAv87nqLEG1Jtc04Ebn" : "true",
"-KAv88yZe8KTfkjAE7In" : "true"
}
}
}
}
Every user has the element "groups" with a childByAutoId() key. Then I have the list of all the groups that exists in the app.
Every time that I run the app, I get the current user logged url reference, and I get the list of the groups of that user (in this case, the logged in user is "rsenov" that has 3 groups).
For every group that this user belongs to, I iterate through the groups url reference, looking for getting the information of that 3 groups.
I do this like this:
func loadTable() {
self.groups = []
var counter = 0
self.meses = []
var tempItems = [String]()
DataService.dataService.CURRENT_USER_GROUPS_REF.observeEventType(.Value, withBlock: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FDataSnapshot] {
tempItems = []
for snap in snapshots {
DataService.dataService.GROUPS_REF.childByAppendingPath(snap.key).queryOrderedByChild("name").observeEventType(.Value, withBlock: { snapshot in
if let postDictionary = snapshot.value as? Dictionary<String, AnyObject> {
tempItems.append(snapshot.value.objectForKey("name") as! String)
let key = snapshot.key
let group = Group(key: key, dictionary: postDictionary)
self.groups.insert(group, atIndex: 0)
}
counter++
if (counter == snapshots.count) {
self.meses = tempItems
self.miTabla.reloadData()
}
})
}
}
})
}
I think this is not a good idea of iterating in that way. For example, if there is a change of some child in the GROUPS_REF url, the code only runs in that nested code, and since it doesn't have the "snap.key" value got from the for loop, it doesn't work.
Which is the best way to do a good query in this case?
Phew, that took some time to write. Mostly because I don't iOS/Swift a lot:
let ref = Firebase(url: "https://stackoverflow.firebaseio.com/35514497")
let CURRENT_USER_GROUPS_REF = ref.childByAppendingPath("users/rsenov/groups")
let GROUPS_REF = ref.childByAppendingPath("groups")
var counter: UInt = 0
var groupNames = [String]()
CURRENT_USER_GROUPS_REF.observeEventType(.Value, withBlock: { groupKeys in
for groupKey in groupKeys.children {
print("Loading group \(groupKey.key)")
GROUPS_REF.childByAppendingPath(groupKey.key).observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot.value)
if (snapshot.childSnapshotForPath("name").exists()) {
groupNames.append(snapshot.value.objectForKey("name") as! String)
}
counter++
if (counter == groupKeys.childrenCount) {
print(groupNames)
}
})
}
})
By the way, this is how you create a minimal, complete verifiable example. The code has no external dependencies (such as Group and DataService in your code) and only contains what's relevant to the answer.
The important bits:
I used observeSingleEventOfType to get each group, since I don't want to get more callbacks if a group changes
I use snapshot.childSnapshotForPath("name").exists() to check if your group has a name. You probably want to either ensure they all have names or add them to the list with some other property in the real app.
Frank's answer is on-point. I wanted to throw in an alternative that may or may not work for your situation as it requires a slight alteration to the database.
groups
gid_0
author: "ruben"
name: "Item A"
users
uid_0: true
gid_1
author: "ruben"
name: "Item B"
users
uid_1: true
gid_2
author: "ruben"
name: "Item C"
users
uid_0: true
And then some ObjC Code for a Deep Query
Firebase *ref = [self.myRootRef childByAppendingPath:#"groups"];
FQuery *query1 = [ref queryOrderedByChild:#"users/uid_0"];
FQuery *query2 = [query1 queryEqualToValue:#"true"];
[query2 observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
NSLog(#"key: %# value: %#", snapshot.key, snapshot.value);
}];
This code does a deep query on the /groups for all groups that have a /users/uid_0 = true. In this case it returns gid_0 and gid_2
It eliminates the need for iterations and multiple calls to the database.
Adding a /users/ node to each group with a list of the uid's may offer some additional flexibility.
Just a thought.

Resources