Retrieve firebase data where users root collection references a sibling collection - ios

Accessing all maps works perfectly in Firebase with this path, however it grabs all maps available in the "Maps" root collection.
let yourMapRef = Database.database().reference().child("Maps")
I am trying to only access the maps that the user is part of. I have therefore tried to follow stack questions and firebase tutorials but i cant grasp how to do this.
For example i would like Adam to only grab his moderated maps by
let yourMapRef = Database.database().reference().child("users/\(userProfile.uid)/Maps")
How should i think around this problem and how can this be solved?
Users (root collection structure)
{
"4g99cMTM4begwooORsO4EKNV456" : {
"username" : "Adam",
"Maps" : {
"-LpYo_pQ8zIOGHHlNU1Q" : true
}
},
"6g55cHXH4begwooHQvO4EKNV3xm1" : {
"username" : "Ellen",
"Maps" : {
"-LpY4XEER-b21hwMi9sp" : true
}
}
}
Maps (root collection structure)
{
"-LpY4XEER-b21hwMi9sp" : {
"mapmoderators" : {
"6g55cHXH4begwooHQvO4EKNV3xm1" : true
},
"mapphotoURL" : "https://firebasestorage.googleapis.com/v0/b/...",
"mapusername" : "Hello World"
},
"-LpYo_pQ8zIOGHHlNU1Q" : {
"mapmoderators" : {
"4g99cMTM4begwooORsO4EKNV456" : true
},
"mapphotoURL" : "https://firebasestorage.googleapis.com/v0/...",
"mapusername" : "Dream"
}
}

So what you want to do is get the users first, then use it against the maps collection to check wether they moderate a map or not:
func getUsers() {
let userRef = Database.database().reference().child("users").child(currentUserID)
userRef.observeSingleEvent(of: .value, with: { (snapshot) in
let root = snapshot.value as? Dictionary
if let mapsByUser = root["Maps"] as? [String: Bool] {
for (documentId, status) in mapsByUser {
if status {
// Document is true, check for the maps
self.getMaps(key: documentId, owner: currentUserID)
}
}
}
}) { (error) in
print(error.localizedDescription)
}
}
// Check for maps
func getMaps(key:String, owner:String) {
let userRef = Database.database().reference().child("maps").child(key)
userRef.observeSingleEvent(of: .value, with: { (snapshot) in
let user = snapshot.value as? Dictionary
if let mapsByUser = user["mapmoderators"] as? [String: Bool] {
for (userId, status) in mapsByUser {
if userId == owner && status == true {
print("Owner \(owner) manages this \(user)")
}
}
}
}) { (error) in
print(error.localizedDescription)
}
}
Call getUsers() on viewDidLoad to test this.

Related

How to grab Users personal list in Firebase to a tableview

I am trying to simply fetch a users favourite maps onto a tableview.
something that i thought would be very basic but turned out to be extremely difficult.
The code here is the best that i have managed so far, Attempting to somehow reference a (users id) with a (yourmaps id) to fetch specific information.
For example. Since the user has made 1 map his favourite(with id (-LpY4XEER-b21hwMi9sp)). I want to look through all maps within root["yourmap"] and only fetch his map onto a tableview.
Firebase
"users" {
"6g55cHXH4begwooHQvO4EKNV3xm1" : {
"photoURL" : "https://firebasestorage.googleap...",
"username" : "lbarri",
"yourmaps" : {
"-LpY4XEER-b21hwMi9sp" : true
}
}
}
"yourmaps": {
"-LpY4XEER-b21hwMi9sp" : {
"author" : {
"photoURL" : "https://firebasestorage.googleapis.com/v...",
"uid" : "6g55cHXH4begwooHQvO4EKNV3xm1",
"username" : "lbarri"
},
"mapmoderators" : {
"6g55cHXH4begwooHQvO4EKNV3xm1" : true
},
"mapphotoURL" : "https://firebasestorage.googleapis...",
"mapusername" : "Hello World"
},
"-LpYo_pQ8zIOGHHlNU1Q" : {
"author" : {
"photoURL" : "https://firebasestorage.googleapis.com/v...3",
"uid" : "RLFK9xnvhccTu2hbNHq0v05J2A13",
"username" : "lbarri"
},
"mapmoderators" : {
"RLFK9xnvhccTu2hbNHq0v05J2A13" : true
},
"mapphotoURL" : "https://firebasestorage.googleapis.com/...",
"mapusername" : "Dream"
}
}
Swift
func getCurrentUserMaps() {
guard let userProfile = UserService.currentUserProfile else { return }
let currentUserId = userProfile.uid
let userRef = Database.database().reference().child("users").child(currentUserId)
userRef.observeSingleEvent(of: .value, with: { (snapshot) in
let root = snapshot.value as? NSDictionary
if let mapsByUser = root!["yourmaps"] as? [String: Bool] {
for (documentId, status) in mapsByUser {
if status {
// Document is true, check for the maps
self.fetchyourmaps(key: documentId, owner: currentUserId)
}
}
}
}) { (error) in
print(error.localizedDescription)
}
}
func fetchyourmaps(key:String, owner:String) {
let yourMapRef = Database.database().reference().child("yourmaps")
yourMapRef.observeSingleEvent(of: .value, with: {snapshot in
let user = snapshot.value as? NSDictionary
if let mapsByUser = user!["mapmoderators"] as? [String: Bool] {
for (userId, status) in mapsByUser {
if userId == owner && status == true {
print("Owner \(owner) manages this \(user)")
var tempYourMap = [YourMapProfile]()
for key in (snapshot.value as? NSDictionary)! {
let childSnapshot = key as? DataSnapshot
let dict = childSnapshot!.value as? [String:AnyObject]
let author = dict!["author"] as? [String:AnyObject]
let uid = author!["uid"] as? String
let username = author!["username"] as? String
let photoURL = author!["photoURL"] as? String
let url = URL(string:photoURL!)
let mapusername = dict!["mapusername"] as? String
let mapphotoURL = dict!["mapphotoURL"] as? String
let mapurl = URL(string:mapphotoURL!)
let userProfile = UserProfile(uid: uid!, username: username!, photoURL: url!, mapPoints: mapPoints!)
let yourmapprofile = YourMapProfile(mapid: childSnapshot!.key as! String, mapauthor: userProfile, mapusername: mapusername!, mapphotoURL: mapurl!)
tempYourMap.append(yourmapprofile)
}
self.yourmaps = tempYourMap
self.tableView.reloadData()
}
}
}
})
}
print("Owner \(owner) manages this \(user)") does print the correct maps onto the console
After that line it is when i cant figure out how to package the information to my tableview.
I have searched everywhere for information on how to retrieve data from Firebase when referencing one root folder to another but i cant find anything helpful. So any link/guide/ tutorial etc would be appreciated and i'll gladly take it from there. Is this at least how you are supposed to do it?
There are a few ways to do this but here's two: Option 1 is to leverage a deep query to get the maps that are this users favorites. The second is to iterate over the users maps and pull each one at a time.
Option 1:
Start with a maps node like this
allMaps
map_0
favorite_of
uid_0: true
uid_3: true
map_user_name: "Larry"
map_1
favorite_of
uid_2: true
map_user_name: "Moe"
map_2
favorite_of
uid_0: true
map_user_name: "Curly"
Then, a deep query to get all the favorite maps of uid_0
func queryToGetMyFavoriteMaps() {
let uid = "uid_0"
let ref = self.ref.child("allMaps")
let path = "favorite_of/" + uid
let query = ref.queryOrdered(byChild: path).queryEqual(toValue: true)
query.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
print(child) //prints the map_0 & map_2 nodes since that's the favorite onces
}
})
}
Option 2
Change up the allMaps node since we won't be doing a query
allMaps
map_0
map_user_name: "Larry"
map_1
map_user_name: "Moe"
map_2
map_user_name: "Curly"
and then the users node will be something like this
users
uid_0
name: "Frank"
favorite_maps:
map_0: true
map_2: true
uid_1
name: "Leroy"
favorite_maps:
map_1: true
and then the code that reads uid_0's favorite_maps node, and gets the keys from that snapshot, and then iterates over them, reading the map nodes one at a time.
func iterateToGetFavoriteMaps() {
let uid = "uid_0"
let userRef = self.ref.child("users").child(uid)
userRef.observeSingleEvent(of: .value, with: { snapshot in
if let mapRefs = snapshot.childSnapshot(forPath: "favorite_maps").value as? [String: Any] {
let mapKeys = mapRefs.keys //note mapKeys is a Dict so we can directly access the keys
for key in mapKeys {
let mapRef = self.ref.child("allMaps").child(key)
mapRef.observeSingleEvent(of: .value, with: { mapSnapshot in
let name = mapSnapshot.childSnapshot(forPath: "mapUserName").value as? String ?? "No Name"
print(name)
})
}
}
})
}

Fetching from Firebase so slow

I try to fetch data from firebase but I think I have done something wrong in my code. I'll share my fetch method and firebase structure in the below if you could help me I'd be very happy about it
This is the first part of a fetching process
DispatchQueue.main.async {
Database.database().reference().child("Products/\(categoryUID)").observe(.value) { (snapshot) in
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let companyId = child.key
self.getCompanyName(compID: companyId)
print(result)
}
}
}
}
After I got company I'll use second fetch method for the populate tableView
Database.database().reference().child("company").child(compID).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String: Any] else {return}
self.company = Company(dictionary: dictionary, uid: compID)
self.data.append(self.company!)
print(self.data)
self.tableView.reloadData()
}) { (err) in
print("Failed to fetch user for posts:", err)
}
This process taking too long about 15-20 seconds. I couldn't find the reason of the problem
Here is the firebase structure
"Products" : {
"-LCJzPPR6knojTMm3sqd" : {
"-LCJz95HuFlcrpGeRMa2" : {
"-LCK3ysCZTUG7rBBZuRS" : {
"product_detail" : "2325423542342",
"product_image_url" : [ "https://firebasestorage.googleapis.com/v0/b/e-fiyat-69e44.appspot.com/o/p%2F1526140758172-1.jpg?alt=media&token=8e9c3feb-c722-427a-98e3-c02a27607874" ],
"product_name" : "DENEME12",
"product_price" : "234"
}
}
},
"category" : {
"-LCJzPPR6knojTMm3sqd" : {
"imageUrl" : "https://firebasestorage.googleapis.com/v0/b/e-fiyat-69e44.appspot.com/o/category%2F1526139301086-Sandalye%20ve%20Koltuklar.png?alt=media&token=401ce15e-d08d-4487-9d79-67ec54e3f2b4",
"name" : "Sandalye & Koltuklar"
},
"company" : {
"-LCJz95HuFlcrpGeRMa2" : {
"imageUrl" : "https://firebasestorage.googleapis.com/v0/b/e-fiyat-69e44.appspot.com/o/company%2F1526139234155-cad.png?alt=media&token=9371db0c-d191-4277-93f6-871c43e758eb",
"name" : "Cadı"
},
While I'm writing this function logic is like this,
First fetched categories
Second fetched Companies ( Delayin in this section)
Third fetched Products
We need to access each product for the Determine which company has category.
I hope I explained myself clearly
First I recommend this: https://firebase.google.com/docs/database/ios/structure-data
Then your products structure may be like this if I understood your need correctly. But you will get the main idea.
"Products" : {
"products-uid" : {
"product_detail" : "2325423542342",
"product_image_url" : [ "https://firebasestorage.googleapis.com/v0/b/e-fiyat-69e44.appspot.com/o/p%2F1526140758172-1.jpg?alt=media&token=8e9c3feb-c722-427a-98e3-c02a27607874" ],
"product_name" : "DENEME12",
"product_price" : "234",
"companies": {
// the value here doesn't matter, just that the key exists
"company-one-uid": true,
"company-two-uid": true,
...
}
"categories": {
// the value here doesn't matter, just that the key exists
"cat-one-uid": true,
"cat-two-uid": true,
...
}
}
"companies" : {
"company-one-uid" : {
"imageUrl" : "https://firebasestorage.googleapis.com/v0/b/e-fiyat-69e44.appspot.com/o/company%2F1526139234155-cad.png?alt=media&token=9371db0c-d191-4277-93f6-871c43e758eb",
"name" : "Cadı"
}
"company-two-uid" : {...
}
}
"categories" : {
"cat-one-uid" : {....
Then maybe you can filter products if they contains company or category with queryOrdered method.
1) It is very similar to the fact that the tableView reload does not occur in the main thread.
Try add DispatchQueue.main.async for self.tableView.reloadData():
func updateCompanies() {
Database.database().reference().child("company").child(compID).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String: Any] else { return }
self.company = Company(dictionary: dictionary, uid: compID)
self.data.append(self.company!)
print(self.data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}) { (err) in
print("Failed to fetch user for posts:", err)
}
}
2) Also, if you have a lot of items in the database, try not to get everything at once. Use queryLimitedToLast
Database.database().reference().child("Products/\(categoryUID)").queryLimited(toLast: 20).observe(.value) { ... }

Swift & Firebase: Accessing Information

I'm creating an app that allows notes to be shared. I'm having trouble with the Firebase database and feel that the amount of lines of code I am using to get information the user needs is more than I actually need and on top of that doesn’t work. Below is a screenshot of my database and a show of the code I’ve used. There are 3 different account types in my app. Parent, Child, and Other.
The Child can only see their own notes
The Parent can see everyone who’s written notes in for their code
The Other is supposed to see their notes and the parents notes per
code.
I am attempting to show the users whose notes can be seen in a collection view. So far it works for Parents and Children but not for the 'Other' which I am trying to fix.
This is my database. Right now, a Parent makes the account which generates a unique code. Then an Other or Child can join by typing the code in on a ViewController. For a child account it will automatically add the code to the account of the child in the user section and for the Other it is supposed to make a new section (I added a picture of what it looked like before I changed it). All users that have a certain code are added to the code section under that code.
{
"codes" : {
"ParVWb" : {
"totalUsers" : 2,
"users" : {
"VWbSR8qQyWd9deka6tgbxFC9ahs1" : {
"numberofNotes" : 0
},
"qm12qgkWfdbtSMMx58wIrxKuLKh1" : {
"numberofNotes" : 0
}
}
}
},
"users" : {
"JN47TBdI4EYX0NvoSHN21o1xFH92" : {
"email" : "other1#restart.mocs",
"fullName" : "Other 1 Name",
"type" : "Other",
"uid" : "JN47TBdI4EYX0NvoSHN21o1xFH92"
},
"VWbSR8qQyWd9deka6tgbxFC9ahs1" : {
"code" : "ParVWb",
"email" : "parentemail#restart.mocs",
"fullName" : "Parent Name",
"type" : "Parent",
"uid" : "VWbSR8qQyWd9deka6tgbxFC9ahs1"
},
"qm12qgkWfdbtSMMx58wIrxKuLKh1" : {
"code" : "ParVWb",
"email" : "childname#restart.mocs",
"fullName" : "Child Name",
"type" : "Child",
"uid" : "qm12qgkWfdbtSMMx58wIrxKuLKh1"
}
}
}
I used to have the use section look like this but now because of the large amount of reference blocks, it doesn't run the part that adds the "codes" section to the 'Other' user.
This is the function I have in my CollectionViewController that obtains all the needed users depending on the currentUser type.
func fetchConnections() {
ref.child("users").child(userID!).observe(.value, with: { (snapshot) in
let user = User(snapshot: snapshot)
if user.type == "Parent" {
let code = user.code as? String
let codeRef = databaseRef.child("codes").child(code!).child("users")
codeRef.observe(.value, with: { (codeSnapshot) in
if let users = codeSnapshot.value as? [String:AnyObject] {
DispatchQueue.main.async(execute: {
for user in users {
if let dict = user.value as? [String: Any] {
let userInformation = cellInfo(uid: user.0, numberofNotes: (dict["numberofNotes"] as? Int)!, code: self.code)
self.userCollection.append(userInformation)
self.collectionView.reloadData()
}
}
})
}
}) { (error) in
print(error.localizedDescription)
}
} else if user.type == "Child" {
let code = user.code
self.ref.child("codes").child(code!).child("users").child(self.userID!).observe(.value, with: { (snapshot) in
if let dict = snapshot.value as? [String: Any] {
print(user.key, (dict["numberofNotes"] as? Int)!)
let userInformation = cellInfo(uid: (user.key as? String)!, numberofNotes: (dict["numberofNotes"] as? Int)!, code: "code")
self.userCollection.append(userInformation)
self.collectionView.reloadData()
}
})
} else if user.type == "Other" {
self.ref.child("users").child(self.userID!).child("codes").observe(.value, with: { (snap) in
if let codes = snap.value as? [String:AnyObject] {
for code in codes {
if code.key == "numberofCodes" {
print("ignore")
} else { // If it's an actual code
self.ref.child("users").child(code.0).observeSingleEvent(of: .value, with: { (codeSnap) in
let user = User(snapshot: codeSnap)
let codetoFetch = user.code
self.ref.child("codes").child(codetoFetch!).child("users").child(self.userID!).observe(.value, with: {(codeSnapshot) in
if let users = codeSnapshot.value as? [String:AnyObject] {
DispatchQueue.main.async(execute: {
for user in users {
if let dict = user.value as? [String: Any] {
let userInformation = cellInfo(uid: code.0, numberofNotes: (dict["numberofNotes"] as? Int)!, code: self.code)
//self.userCollection.append(userInformation)
//self.collectionView.reloadData()
}
}
})
}
})
})
}
}
}
})
}
})
}
How can I obtain all of the codes that the 'Other' is a part of and all of the UID's & NumberofNotes for the 'Parents' of that code so that I can put them into the collectionView? (This code works for Parent and Child but if the way I'm doing it is long and ineffective please let me know.)

Show posts from the users you are following - swift

I have made a following feature where users can follow each other. The problem is that it is not being used the way it should. At the moment when a user is writing a post it will be saved in my Firebase database under this reference:
FIRDatabase.database().reference().child("feed-items").childByAutoId()
The feed-items is where all posts are. I am changing that however now, so when a user is posting something it will be saved here:
FIRDatabase.database().reference().child("Users").child(UserID).child("Posts").childByAutoId()
I do that because it somehow tells me that would be easier to only show the posts of the people you follow in my apps feed.
At the moment I am getting all the posts for my feed (from feed-items) like this:
func startObersvingDB() {
FIRDatabase.database().reference().child("feed-items").observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
var newUpdates = [Sweet]()
for update in snapshot.children {
let updateObject = Sweet(snapshot: update as! FIRDataSnapshot)
newUpdates.append(updateObject)
}
self.updates = newUpdates.reverse()
self.tableView.reloadData()
}) { (error: NSError) in
print(error.description)
}
}
And then I call startObservingDB() in the viewDidLoad.
If you want to see my Sweet struct here it is:
import Foundation
import FirebaseDatabase
import FirebaseAuth
import UIKit
struct Sweet {
let key: String!
let content: String!
let addedByUser: String!
let profilePhoto: String!
let itemRef: FIRDatabaseReference?
init (content: String, addedByUser: String, profilePhoto: String!, key: String = "") {
self.key = key
self.content = content
self.addedByUser = addedByUser
self.profilePhoto = profilePhoto
self.itemRef = nil
}
init (snapshot: FIRDataSnapshot) {
key = snapshot.key
itemRef = snapshot.ref
path = key
if let theFeedContent = snapshot.value!["content"] as? String {
content = theFeedContent
} else {
content = ""
}
if let feedUser = snapshot.value!["addedByUser"] as? String {
addedByUser = feedUser
} else {
addedByUser = ""
}
if let feedPhoto = snapshot.value!["profilePhoto"] as? String! {
profilePhoto = feedPhoto
} else {
profilePhoto = ""
}
}
func toAnyObject() -> AnyObject {
return ["content":content, "addedByUser":addedByUser, "profilePhoto":profilePhoto!]
}
}
And in my TableViewController I am using this to display name etc. in the custom cell:
var update = updates[indexPath.row]
cell.nameLabel.text = update.addedByUser
etc. etc.
My question is:
How do I change that to only show posts from the people I am following?
Sorry for the long post
Assuming that you are saving your followers list as an dictionary in other parent node like this :-
user_followed_by :{
userID2 : {
userID1 : true,
userID5 : true,
userID6 : true,
userID12 : true,
userID99 : true,
}
}
users :{
userID2 :{
post :{
postAutoID1 : {...},
postAutoID2 : {...},
...
}
}
}
postsToShowToUser :{
userID1 : {
postAutoID1 : true, //These are the post's autoID's of all the users whom
// your current user is following
postAutoID5 : true,
postAutoID3 : true,
},
}
/* // If you choose to declare a separate section of the post Details in your Database.
posts_Of_The_User :{
userID1 : {
postAutoID1 : {...//DETAILS},
postAutoID2 : {...//DETAILS},
postAutoID3 : {...//DETAILS},
....
},
} */
The idea is that whenever a user whom your current user is following makes a post. That post's autoID gets appended in the postsToShowToUser/userID.
That is, if userID2 will make a post then a call will be made to append that post's autoID in all users postsToShowToUser/userID who are following the userID2.
PS:- I strongly suggest that you move your post details out of post section. Make it a separate parent node consisting of multiple postAutoID's as key and there post data as value. It might come in handy later on, also would avoid nesting data, which would help you navigate through your Database.

Swift Firebase snapshot.allValues update

I am currently following a course on Udemy that teaches how to create a chat application with Firebase. However, I finished this course a couple of weeks ago, and then suddenly came the Swift 3.0 update. I am now struggling to convert this line in to Swift 3 Firebase:
firebase.child("Recent").queryOrderedByChild("chatRoomID").queryEqualToValue(chatRoomID).observeSingleEventOfType(.Value) { (snapshot:FIRDataSnapshot) in
var createRecent = true
if snapshot.exists() {
for recent in snapshot.value!.allValues {
if recent["userId"] as! String == userId {
createRecent = false
}
}
}
if createRecent {
CreateRecentItem(userId, chatRoomID: chatRoomID, members: members, withUserUsername: withUserUsername, withUserUserId: withUseruserId)
}
}
And I tried to do this:
firebase.child("Recent").queryOrdered(byChild: "chatRoomID").queryEqual(toValue: chatRoomID).observeSingleEvent(of: .value) { (snapshot:FIRDataSnapshot) in
var createRecent = true
if snapshot.exists() {
if let values = snapshot.value as? [String:AnyObject] {
for recent in values {
if recent["userId"] as! String == userId {
}
}
}
//}
}
}
}
But of course this returned an error. Any ideas on how I would solve this particular code-convert?
Thanks in advance.
Try using:-
firebase.child("Recent").queryOrdered(byChild: "chatRoomID").queryEqual(toValue: chatRoomID).observeSingleEvent(of: .value, with: { (snapshot:FIRDataSnapshot) in
var createRecent = true
if snapshot.exists() {
if let values = snapshot.value as? [String:AnyObject] {
for recent in values {
if let userId = recent.value["userId"] as? String{
}
}
}
}
})

Resources