Fetch all uid from firebase database using swift code - swift

I have this database (see the link above with the image) and a code with a path, that leads to the destination using:
databaseReference.child("users").child(currentUser!.uid).child("todo-list")
Under the first node "users", the next level is a keyvalue for userID, and later there are more sub-levels "todo-list" etc.
I would like to fetch the data from all uid available. How can I make the query?
Here is the code:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
databaseReference = Database.database().reference()
currentUser = Auth.auth().currentUser
let todoListRef = databaseReference.child("users").child(currentUser!.uid).child("todo-list")
todoListRef.observe(DataEventType.value, with: { (DataSnapshot) in
self.itemsToLoad.removeAll()
let enumerator = DataSnapshot.children
while let todoItem = enumerator.nextObject() as? DataSnapshot
{
let item = todoItem.value as AnyObject
self.itemsToLoad.append(item)
}
self.itemsToLoad = self.itemsToLoad.reversed()
self.tableView.reloadData()
})
}

It's quite similar to the code you already have. Instead of observing /users/$uid, you observe the entire /users node. And then you add an extra loop in the closure, to iterate over all the users.
So:
let usersRef = databaseReference.child("users")
usersRef.observe(DataEventType.value, with: { (usersSnapshot) in
let userEnumerator = usersSnapshot.children
while let user = userEnumerator.nextObject() as? DataSnapshot {
let uid = user.key
let todoEnumerator = user.childSnapshot(forPath: "todo-list").children
while let todoItem = todoEnumerator.nextObject() as? DataSnapshot {
let item = todoItem.value as AnyObject
self.itemsToLoad.append(item)
}
}
})
The added while loops over the users, while the inner while loops over the todo's for each user. I removed the code related to the table view, since what you want to do there doesn't depend on Firebase.

Related

How to save data to a specific uid for a specific logged in user in swift/firebase

I'm trying to allow a user to save data to a tableView using an alert that transfer data from the View Controller that the alert is in (CreatePlaylistVC) to another ViewController(CreatedPlaylistVC) that the tableView is in, saving for each specific account for a specific uid.
I've tried setting the value to the uid but this did work for me although it did save to the database under that specific uid.
CreatePlaylistVC
ref = Database.database().reference()
alert.addAction(UIAlertAction(title:"OK", style:.default, handler: {
action in
if let playlistName = alert.textFields?.first?.text {
let userID = Auth.auth().currentUser?.uid
self.ref?.child("PlaylistName").child(userID!).setValue(playlistName)
CreatedPlaylistVC
var ref:DatabaseReference?
var databaseHandle:DatabaseHandle?
override func viewDidLoad() {
//Set the firebase reference
ref = Database.database().reference()
//Retrieve the posts and listen fro changes
databaseHandle = ref?.child("PlaylistName").observe(.childAdded, with: { (snapshot) in
//Try to covert the value of the data to a string
let post = snapshot.value as? String
if let actualPost = post {
//Append the data to our playlistNameArray
self.playlistNameArray.append(actualPost)
//Reload the tableView
self.tableView.reloadData()
}
})
}
The expected results is to save the data only for the specified uid or currently logged in user. But it is saving for each user even though in the database it is saved to the right uid.
When using .childAdded for observe, it will go through every existing child under "PlaylistName" which in this case will be every user that has saved something.
Might have to reconsider your structure. Or use childByAutoID.
Edit: To use child by auto ID
// Your ["Name": Playlist] will get nested into an autogenerated child
self.ref?.child("PlaylistName").child(userID!).childByAutoID().setValue(["Name":playlistName])
//You will be listening for any new additions that your current user has made
databaseHandle = ref?.child("PlaylistName").child(Auth.auth().currentUser!.uid).observe(.childAdded, with: { (snapshot) in
// this loops through a list of playlist names your user creates
for child in snapshot.children{
let snap = child as! DataSnapshot
let value = snap.value as? Dict<String,Any>
let post = value["Name"] as! String
if let actualPost = post {
self.playlistNameArray.append(actualPost)
self.tableView.reloadData()
}
}
})
But my suggestion, if it suits your application, would be to change your data structure because it can keep your structure a lot flatter.
self.ref?.child(userID!).child("PlaylistName").setValue(playListName)
databaseHandle = ref?.child(userID!).observe(.childAdded, with: { (snapshot) in
let value = snapshot.value as? Dict<String,Any>
let post = value["PlaylistName"]
if let actualPost = post {
self.playlistNameArray.append(actualPost)
self.tableView.reloadData()
}
})

How can I pick three random elements out of a dictionary in Swift 4.1

I am having a problem picking three random elements out of a dictionary.
My dictionary code:
query.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let childSnap = child as! DataSnapshot
var dict = childSnap.value as! [String: Any]
}
})
You can use an array if keys are integers.
if you want to use a dictionary only then below mentioned code might be helpful for you
var namesOfPeople = [Int: String]()
namesOfPeople[1] = "jacob"
namesOfPeople[2] = "peter"
namesOfPeople[3] = "sam"
func makeList(n: Int) -> [Int] {
print(namesOfPeopleCount)
return (0..<n).map { _ in namesOfPeople.keys.randomElement()! }
}
let randomKeys = makeList(3)
You can try this for older version Of Swift where randomElement() is not available
let namesOfPeopleCount = namesOfPeople.count
func makeList(n: Int) -> [Int] {
return (0..<n).map{ _ in Int(arc4random_uniform(namesOfPeopleCount)
}
#Satish answer is fine but here's one which is a bit more complete and selects a random user from a list of users loaded from Firebase ensuring a user is only selected once.
We have have an app with two buttons
populateArray
selectRandomUser
and we have a UserClass to store our user data for each user.
class UserClass {
var uid = ""
var name = ""
init(withSnapshot: DataSnapshot) {
let dict = withSnapshot.value as! [String: Any]
self.uid = withSnapshot.key
self.name = dict["Name"] as! String
}
}
and an array to store the users in
var userArray = [UserClass]()
When the populateArray button is clicked this code runs
func populateArray() {
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let user = UserClass(withSnapshot: snap)
self.userArray.append(user)
}
print("array populated")
})
}
and then to select a random user use this code.
func selectRandomUser() {
if let someUser = userArray.randomElement() {
print("your random user: \(someUser.name)")
let uid = someUser.uid
if let index = userArray.index(where: { $0.uid == uid } ) {
userArray.remove(at: index)
}
} else {
print("no users remain")
}
}
This code ensures the same user is not selected twice. Note that this is destructive to the array containing the users so if that's unwanted, make a copy of the array after it's populated and work with that.

Getting values from Firebase snapshot in Swift

Im successfully getting data from Firebase but I can't manage to push it into array to use. My database is as follows:
users
-Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2
-email : "mike#gmail.com"
-lists
-LJiezOzfDrqmd-hnoH-
-owner: Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2
-LJif-UgPgbdGSHYgjY6
-owner: Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2
shopping-lists
-LJh6sdBJtBCM7DwxPRy
-name: "weekly shopping"
-owner: "mike#gmail.com"
I have a home page after login that shows existing shopping lists on table if they exist. On viewDidLoad() I get shopping list IDs from the user and use those IDs as a reference to get details from shopping-lists.
However, I cant manage to save these data into an array as it gets deleted after closure. How can I do that in a clean way?
override func viewDidLoad() {
super.viewDidLoad()
SVProgressHUD.show()
tableView.allowsMultipleSelectionDuringEditing = false
// Sets user variable - must have
Auth.auth().addStateDidChangeListener { auth, user in
guard let user = user else { return }
self.user = User(authData: user)
// If new user, write into Firebase
self.usersRef.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.hasChild(self.user.uid) {
self.usersRef.child(user.uid).setValue(["email": user.email!])
}
})
// Get shopping lists data from "users/lists"
self.usersRef.child(user.uid).child("lists").observe(.value, with: { snapshot in
// Get list IDs
if snapshot.exists() {
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
self.listNames.append(child.key)
}
}
}
// Use list IDs - to get details
for item in self.listNames {
let itemRef = self.shoppingListsRef.child(item)
itemRef.observeSingleEvent(of: .childAdded, with: { (snapshot) in
if let value = snapshot.value as? [String: Any] {
let name = value["name"] as? String ?? ""
let owner = value["owner"] as? String ?? ""
let shoppingList = ShoppingList(name: name, owner: owner)
self.items.append(shoppingList)
}
})
}
})
self.tableView.reloadData()
SVProgressHUD.dismiss()
}
}
(the question is a bit unclear so several parts to this answer to cover all possibilities. This is Swift 4, Firebase 4/5)
You don't really need to query here since you know which nodes you want by their key and they will always be read in the in order of your listNames array. This assumes self.listNames are the keys you want to read in.
for item in listNames {
let itemRef = shoppingListsRef.child(item)
itemRef.observe(.value, with: { (snapshot) in
if let value = snapshot.value as? [String: Any] {
let name = value["name"] as? String ?? ""
let owner = value["owner"] as? String ?? ""
print(name, owner)
}
})
}
Generally, queries are used when you are searching for something within a node - for example if you were looking for the node that contained a child name of 'weekly shopping'. Other than that, stick with just reading the nodes directly as it's faster and has less overhead. Keep reading...
I also removed the older NSDictionary and went with the Swift [String: Any] and modified your error checking
However, the real issue is reading that node with an .observe by .value. Remember that .value reads in all children of the node and then the children need to be iterated over to get each separate DataSnapshot. Also, .observe leaves an observer on the node notifying the app of changes, which I don't think you want. So this will answer the question as posted, (and needs better error checking)
for item in listNames {
let queryRef = shoppingListsRef
.queryOrdered(byChild: "name")
.queryEqual(toValue: item)
queryRef.observe(.value, with: { (snapshot) in
for child in snapshot.children { //even though there is only 1 child
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let name = dict["name"] as? String ?? ""
let owner = dict["owner"] as? String ?? ""
print(name, owner)
}
})
}
And the answer...
This is probably more what you want...
for item in listNames {
let queryRef = shoppingListsRef
.queryOrdered(byChild: "name")
.queryEqual(toValue: item)
queryRef.observeSingleEvent(of: .childAdded, with: { snapshot in
let dict = snapshot.value as! [String: Any]
let name = dict["name"] as? String ?? ""
let owner = dict["owner"] as? String ?? ""
print(name, owner)
})
}
note the .childAdded instead of .value which presents the snapshot as a single DataSnapshot and doesn't need to be iterated over and the .observeSingleEvent which does not leave an observer attached to each node.
Edit
Based on additonal information, it would be best too change the structure to this
shopping-lists
-LJh6sdBJtBCM7DwxPRy
-name: "weekly shopping"
-uid: "Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2"
and then when the user logs in just query the shopping lists node for any uid that's theirs.

retrieve posts / query firebase swift 4

i am attempting to retrieve a list of Posts ("Planits - in my apps language") from firebase. My goal is to display a specific users posts within a table view on their profile. I have written a function to retrieves posts and query them by a sender ID so that the user see's their posts on their profile. But at the end of the query when i try to print out the appended array, i keep getting an empty array, so i can not go further on to populate the table view. Please any suggestions on where i went wrong, attached is a screen shot of my firebase nodes and the function i wrote. thanks
func retrievePost(){
ref = Database.database().reference()
let myPlanitsRef = self.ref.child("planits")
let query = myPlanitsRef.queryOrdered(byChild: "senderId").queryEqual(toValue: "uid")
print(query)
query.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
for child in snapshot.children {
let snap = child as! DataSnapshot
print(DataSnapshot.self)
let dict = snap.value as! [String: Any]
let myPostURL = dict["images"] as! String
self.images.append(myPostURL)
}
//print(myPostURL) - DOES NOT PRING ANYTHING
//print(self.images) - DOES NOT PRING ANYTHING
}
}) { (error) in
print(error)
}
}
override func viewDidLoad() {
super.viewDidLoad()
retrievePost()
print(images) // PRINTS []

How can I retrieve data from firebase (Swift)

I am beginner and I would like to use swift as my programming language.
user can add register users and transfer money to each other, therefore when user type the photo number and email, the app can check if the typed number is registered in the firebase system
Any simple way to search if the user exist, thanks a lot
Here's the structure of the database
var ref: DatabaseReference!
var tref: DatabaseReference!
var handle : DatabaseHandle!
var usersArray = [NSDictionary?]()
var filteredUsers = [NSDictionary?]()
var user : NSDictionary?
override func viewDidLoad() {
super.viewDidLoad()
self.picker.isHidden = true
tref = Database.database().reference()
ref = Database.database().reference()
self.handle = self.ref?.child("users").child((Auth.auth().currentUser?.uid)!).child("contact").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String {
self.pickerdata.append(item)
self.picker.reloadAllComponents()
}
})
self.picker.delegate = self
self.picker.dataSource = self
tref.child("users").queryOrdered(byChild: "phone").observe(.childAdded, with: {(snapshot) in
self.usersArray.append(snapshot.value as? NSDictionary)
})
print(usersArray.count)
}
#IBAction func ContactChange(_ sender: UITextField) {
filteredContent(searchText: contactText.text!)
print(filteredUsers.count)
print(usersArray.count)
print("ARRAY")
}
func filteredContent(searchText: String){
self.filteredUsers = self.usersArray.filter{ user in
let username = user!["phone"] as? String
return (username?.lowercased().contains(searchText.lowercased()))!
}
}
func findUsers(text: String){
self.handle = ref.child("users").queryOrdered(byChild: "phone").queryStarting(atValue: contactText.text!).queryEnding(atValue: contactText.text!+"\u{f8ff}").observe(.value, with: { snapshot in
if let item = snapshot.value as? String {
self.contact.append(item)
} else{
print("error")
}
})
}
The code above doesn't work much. Thanks so much for helping
The question is a little unclear but I think what the OP is asking is:
how to see if a user exists by their phone number?
If that's what's being asked, a node can be retrieved by query'ing for a child of that node. For example, let's create a query on the users node to find the child that contains the name: Kam querying by phone
let usersRef = firebaseRef.child("users")
let query = usersRef.queryOrdered(byChild: "phone").queryEqual(toValue: "2330504")
query.observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
//need to iterate in case we find more than one match
for child in snapshot.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let name = dict["name"] as! String
print("found \(name)")
}
} else {
print("not found")
}
}
When this code is run it would print
found Kam
assuming the phone number is 2330504