Problem while retrieving info from Firebase Database IOS - swift

I'm trying to retrieve data from my Database but I'm having some problems, this is my database structure:
And here is my code
var ref : DatabaseReference
var idString = [String]()
ref = Database.database().reference()
ref.child("idUsers").observe(.value){ (snapshot) in
let id = snapshot.value as? String
if let ids = id {
idString.append(ids)
print(ids)
}
}
But no data is going into my array, I have been trying some solutions but no one works, please help! These are my rules by the way, they are public by default.
{
"rules": {
".read": true,
".write": true
}
}

Since idUsers contains multiple child nodes, you need to loop over the results in your code. Something like this:
ref.child("idUsers").observe(.value){ (snapshot) in
for userSnapshot in in snapshot.children.allObjects as? [DataSnapshot] {
let id = userSnapshot.value as? String
print(id)
idString.append(id)
}
print(ids)
}

**For creating database structure**
`override func viewDidLoad() {
var ref: DatabaseReference!
ref = Database.database().reference()
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
Auth.auth().signIn(withEmail: email, password: password) { [weak self] user, error in
guard self != nil else { return }
ref.child("users").setValue(["test1": "abc",
"test2": "efg",
"test3": "hij"])
}
}
}`
**// For retrieving data from firebase**
ref.observe(DataEventType.value, with: { (snapshot) in
let postDict = snapshot.value as? [String : AnyObject] ?? [:]
for item in postDict {
print(item)
}
})

Related

Exclude certain child from retrieving Firebase

I would like to retrieve all children within a certain collection except one specific child. I have the database "users" which consists of multiple user id's.
"users"
|- "userId1"
|- "userId2"
|- "userId3"
and so on.
I now want to retrieve only "userId1" and "userId3" and exclude "userId2". I already know, how to get just the userId2, because I have also stored it in another database, "blocked-users". This database consists of userIds as well as is build like this:
"blocked-users"
|- "userId1"
|- "userId2"
And that is the reason why I want to exclude userId2 from retrieving the users. Say userId1 is the currentUser and has blocked userId2, I want to prevent userId1 from finding userId2. How can I do this?
This is how I am getting the id of userId2:
guard let currentUid = Auth.auth().currentUser?.uid else { return }
BLOCKED_USERS.child(currentUid).observe(.childAdded) { (snapshot) in
print("this is the user, that should not be shown: ", snapshot.key)
How can I now exclude the snapshot.key from being fetched within this function?:
var userCurrentKey: String?
USER_REF.queryLimited(toLast: 10).observeSingleEvent(of: .value) { (snapshot) in
guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
guard let allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }
allObjects.forEach({ (snapshot) in
let uid = snapshot.key
Database.fetchUser(with: uid, completion: { (user) in
self.users.append(user)
self.tableView.reloadData()
})
})
self.userCurrentKey = first.key
}
This right here would be the entire function I call for fetching all the users:
func fetchUsers() {
guard let currentUid = Auth.auth().currentUser?.uid else { return }
if userCurrentKey == nil {
BLOCKED_USERS.child(currentUid).observe(.childAdded) { (snapshot) in
print("these are the users that have blocked this user: ", snapshot.key)
var dontShowThisUser = snapshot.key
USER_REF.queryLimited(toLast: 10).observeSingleEvent(of: .value) { (snapshot) in
guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
guard let allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }
allObjects.forEach({ (snapshot) in
let uid = snapshot.key
Database.fetchUser(with: uid, completion: { (user) in
self.users.append(user)
self.tableView.reloadData()
})
})
self.userCurrentKey = first.key
}
}
} else {
USER_REF.queryOrderedByKey().queryEnding(atValue: userCurrentKey).queryLimited(toLast: 5).observeSingleEvent(of: .value, with: { (snapshot) in
guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
guard var allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }
allObjects.removeAll(where: { $0.key == self.userCurrentKey })
allObjects.forEach({ (snapshot) in
let uid = snapshot.key
if uid != self.userCurrentKey {
Database.fetchUser(with: uid, completion: { (user) in
self.users.append(user)
if self.users.count == allObjects.count {
self.tableView.reloadData()
}
})
}
})
self.userCurrentKey = first.key
})
}
}
I have found a way. In the Firebase rules I solve this problem like this:
"$users": {
"$uid": {
".read": "auth != null && !(root.child('blocked-users').child(auth.uid).hasChild($uid))",
".write": "auth != null"
}
}
This reads like this: When auth is not null and the user is not in the blocked-users collection of the user they want to find, the user is allowed to read the user's data.
In general there is no way to exclude one (or some) nodes from a query. You will either have to load the entire users node and filter out the users you don't want in your application code, or you'll have to load the users you do want one by one.
The only variation on this is if you can use a query to slice the child nodes that you want and don't want. For example, if you have 99 users (user01 to user99 for simplicity) and won't read all but one user (say user42), you could do that with two queries:
usersRef.queryOrderedByKey().queryEnding(beforeValue: "user42")
and
usersRef.queryOrderedByKey().queryStarting(afterValue: "user42")
I don't think there's a gain by using this approach here though, as this is likely to be (slightly) less efficient and be more code to handle the two queries, than it'd be to filter the one node in the application code.

How to get the first name of the logged in User from firebase using Swiftui?

How to to get the first name of the current user which is logged in.
This is how my try looks like:
var ref: DatabaseReference!
ref = Database.database().reference()
let db = Firestore.firestore()
let userID = Auth.auth().currentUser?.uid
print(userID)
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let username = value?["firstname"] as? String ?? ""
print(username)
// ...
}) { (error) in
print(error.localizedDescription)
}
Try the following:
let userId = Auth.auth().currentUser?.uid else { return }
let docRef = db.collection("users").document(userId)
docRef.getDocument(source: .cache) { (document, error) in
if let document = document {
let name = document.get("firstname")
print("Cached document data: \(name)")
} else {
print("Document does not exist in cache")
}
}
You are using cloud firestore but in your code, you are using the Realtime database. You need to check the following docs related to cloud firestore:
https://firebase.google.com/docs/firestore/quickstart

Swift - Remove key and values from dictionary [String : Any]

I am trying to removed block users from a dictionary [String : Any] that I am grabbing from the database. At first I grab the list of UID's that the current user has blocked:
var updatedBlockList: Any?
func findBlockUsers() {
// find current users list of blocked users
guard let currentUserUid = Auth.auth().currentUser?.uid else { return }
let blockedUsers = Database.database().reference().child("users").child(currentUserUid)
blockedUsers.observeSingleEvent(of: .value, with: { (snapshot) in
guard let userIdsDictionary = snapshot.value as? [String: Any] else { return }
userIdsDictionary.forEach({ (key, value) in
guard let userDictionary = value as? [String: Any] else { return }
var blockedList : Any
blockedList = userDictionary.keys
print(blockedList)
self.updateBlockList(blockedList: blockedList)
})
})
}
func updateBlockList(blockedList: Any) {
updatedBlockList = blockedList
print(updatedBlockList)
}
If I print updatedBlockList I get: ["gdqzOXPWaiaTn93YMJBEv51UUUn1", "RPwVj59w8pRFLf55VZ6LGX6G2ek2", "xmigo8CPzhNLlXN4oTHMpGo7f213"]
I now want to take those UID's (which will be the key in UserIdsDictionary and remove them after I pull ALL the users:
fileprivate func fetchAllUserIds() {
let ref = Database.database().reference().child("users")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let userIdsDictionary = snapshot.value as? [String: Any] else { return }
userIdsDictionary.forEach({ (key, value) in
// attempting to remove the blocked users here without any luck
var updatedKey = key as String?
updatedKey?.remove(at: self.updatedBlockList as! String.Index)
print(updatedKey!)
guard let userDictionary = value as? [String: Any] else { return }
let user = User(uid: key, dictionary: userDictionary)
self.fetchPostsWithUser(user: user)
})
}) { (err) in
print("Failed to fetch following user ids:", err)
}
}
I get this error when trying to remove: Could not cast value of type 'Swift.Dictionary.Keys' (0x1de2f6b78) to 'Swift.String.Index'
I'm sure i'm going about this the wrong way, but I know i'm close. The end goal is to take the blocked users UID's and remove them from the dictionary. Any help would be very much appreciated!!
Your forEach loop on userIdsDictionary is the wrong approach here so rather than trying to fix that code I would use a different approach and loop over the updatedBlockList
for item in updatedBlockList {
if let userID = item as? String {
userIdsDictionary.removeValue(forKey: userID)
}
}
For anyone wondering, here is the final changes that were made to make it work.
var updatedBlockList = [String]()
func findBlockUsers() {
// find current users list of blocked users
guard let currentUserUid = Auth.auth().currentUser?.uid else { return }
let blockedUsers = Database.database().reference().child("users").child(currentUserUid)
blockedUsers.observeSingleEvent(of: .value, with: { (snapshot) in
guard let userIdsDictionary = snapshot.value as? [String: Any] else { return }
userIdsDictionary.forEach({ (key, value) in
guard let userDictionary = value as? [String: Any] else { return }
let blockedList = Array(userDictionary.keys)
print(blockedList)
self.updateBlockList(blockedList: blockedList)
})
})
}
func updateBlockList(blockedList: [String]) {
updatedBlockList = blockedList
print(updatedBlockList)
}
fileprivate func fetchAllUserIds() {
let ref = Database.database().reference().child("users")
ref.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in
guard var userIdsDictionary = snapshot.value as? [String: Any], let self = self else { return }
for item in self.updatedBlockList {
userIdsDictionary.removeValue(forKey: item)
}
userIdsDictionary.forEach({ (key, value) in
guard let userDictionary = value as? [String: Any] else { return }
let user = User(uid: key, dictionary: userDictionary)
self.fetchPostsWithUser(user: user)
})
}) { (err) in
print("Failed to fetch following user ids:", err)
}
}

I cant see values from firebase swift

Screen Shot 2019-04-01 at 4.45.08 PMenter image description hereI want to get the value from firebase that is stored I want to print it into a text label,
func fetchData(){
var ref: DatabaseReference!
ref = Database.database().reference()
guard let currentUid = Auth.auth().currentUser?.uid else {return}
Database.database().reference().child("users").child(currentUid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? Dictionary<String , AnyObject> else {return}
let uid = snapshot.value
let user = User(uid:uid as! String, dictionary: dictionary)
self.user = user
}) { (error) in
print(error.localizedDescription)
}
}
This is my another code to set the text label
func fetchData(){
var ref: DatabaseReference!
ref = Database.database().reference()
guard let currentUid = Auth.auth().currentUser?.uid else {return}
Database.database().reference().child("users").child(currentUid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? Dictionary<String , AnyObject> else {return}
let uid = snapshot.value
let user = User(uid:uid as! String, dictionary: dictionary)
self.userNameLabel.text = user.username
self.user = user
print(snapshot)
}) { (error) in
print(error.localizedDescription)
}
}
extra code to understand what I am doing
// user info stored
let userID = Auth.auth().currentUser?.uid
let userData = ["userName": userName,
"userAge ": userAge] as [String? : Any]
let values = [userID: userData]
let ref = Database.database().reference()
ref.child("users").childByAutoId().setValue(values)
}
I think you should use callbacks to communicate asynchronously with your controller.
In your example you get the uid by using "snpashot.value" but you should take the key of your dictionary instead.
Here is a similar example :
///Function that returns as callback the user stored in Firebase with a certain id.
func getUserFor(id: String, callback: #escaping (Bool, User?) -> Void) {
//Get the user in Firebase "user" collection with the specific id.
ref.child(Constants.userKey).child(id).observeSingleEvent(of: .value, with: { (userSnapshot) in
guard let userDictionnary = userSnapshot.value as? [String:Any] else { return callback(false, nil) }
let userId = userSnapshot.key
let userEmail = userDictionnary[Constants.emailKey] as? String
let user = User(id: userId, email: userEmail)
return callback(true, user)
}) { (error) in
print(error.localizedDescription)
return callback(false, nil)
}
}
I have found the answer I wasn't setting the user stored info in the firebase the right way.
let userID = Auth.auth().currentUser?.uid
let userData = ["userName": userName,
"userAge ": userAge] as [String? : Any]
let ref = Database.database().reference()
ref.child("users/\(userID ?? "")").setValue(userData)
}
//ref.child("users/(userID??"")").setValue(userData) // this was the error

Posts being loaded for all users in collection view firebase & swift

All I'm trying to do is fetch all the posts to the collection view that are within a certain radius of the current user. Currently I'm getting all the current users posts within the location but that is all. I can't figure out how to convert it to fetch all the posts from all the users.
FetchPostUserIds Is returning a snapshot of all the users and there UID
The geoFire query is only returning the postId from the current user. It shouldn't be I assume
Note: Updated Code
var PostKey: String?
var geoFire: GeoFire?
var regionQuery: GFRegionQuery?
var foundQuery: GFCircleQuery?
var geoFireRef: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
guard let uid = Auth.auth().currentUser?.uid else { return }
geoFireRef = Database.database().reference().child("posts").child(uid)
geoFire = GeoFire(firebaseRef: geoFireRef)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0] as CLLocation
let currentUserLocation = userLocation
let circleQuery = geoFire?.query(at: currentUserLocation, withRadius: 100.0)
_ = circleQuery?.observe(.keyEntered, with: { (key, location) in
self.PostKey = key
self.locationManager.stopUpdatingLocation()
})
}
fileprivate func fetchPostsWithUser(user: User) {
guard let key = PostKey else { return }
let ref = Database.database().reference().child("posts").child(user.uid).child(key)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
self.collectionView?.refreshControl?.endRefreshing()
guard let dictionary = snapshot.value as? [String: Any] else { return }
var post = Post(user: user, dictionary: dictionary)
post.id = key
self.posts.append(post)
self.posts.sort(by: { (post1, post2) -> Bool in
return post1.creationDate.compare(post2.creationDate) == .orderedDescending
})
self.collectionView?.reloadData()
}) { (error) in
print(error)
}
}
fileprivate func fetchPostUserIds() {
let ref = Database.database().reference().child("users")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let userIdKey = snapshot.value as? [String: Any] else { return }
userIdKey.forEach({ (key, value) in
Database.fetchUserWithUID(uid: key, completion: { (user) in
self.fetchPostsWithUser(user: user)
})
})
}) { (error) in
print(error)
}
}
Try to debug and look what you got in the snapshot in your function, and also what fetchUserWithUID return
fileprivate func fetchPostUserIds() {
let ref = Database.database().reference().child("users")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let userIdKey = snapshot.value as? [String: Any] else { return }
userIdKey.forEach({ (key, value) in
Database.fetchUserWithUID(uid: key, completion: { (user) in
self.fetchPostsWithUser(user: user)
})
})
}) { (error) in
print(error)
}
}
Maybe with some more information I can help you
You pass paremeter user: User to method fetchPostsWithUser, but you always use the current user
guard let uid = Auth.auth().currentUser?.uid else { return }
also note these
let ref = Database.database().reference().child("users")
ref.observe(.value, with: { (snapshot) in
will load every change so think about singleObserve