Fetch posts by postId from firebase with swift 4 - swift

All I'm trying to achieve is to have the posts inside my radius append to the collection view. Currently I'm getting all the postId when the user enters the radius but nothing is being append to the collection view and I can't figure out why. Any help is very much appreciated.
var posts = [Post]()
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
guard let user = self.user else { return }
let ref = Database.database().reference().child("posts").child(key)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionaries = snapshot.value as? [String: Any] else { return }
var post = Post(user: user, dictionary: dictionaries)
post.id = key
self.posts.append(post)
self.posts.sort(by: { (post1, post2) -> Bool in
return post1.creationDate.compare(post2.creationDate) == .orderedDescending
})
}, withCancel: { (error) in
print("There was an error getting the posts:", error)
})
})
}
fileprivate func fetchPostUserIds() {
Database.database().reference().child("users").observe(.value, with: { (snapshot) in
guard let userIdsDictionary = snapshot.value as? [String: Any] else { return }
userIdsDictionary.forEach({ (key, value) in
let uidKey = key
self.geoFireRef = Database.database().reference().child("posts").child(uidKey)
self.geoFire = GeoFire(firebaseRef: self.geoFireRef)
})
}) { (err) in
print("Failed to get user Id", err)
}
}

Related

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)
}
}

How to load datas not in realtime in firebase?

I have the following problem to solve:
All datas are loaded in realtime (this time even multiple times per post), but I just want to refresh with a refresher I already have.
This is my refresher:
// Refresher
func refresh() {
refresher = UIRefreshControl()
refresher.attributedTitle = NSAttributedString(string: "Aktualisieren")
refresher.addTarget(self, action: #selector(DiscoveryViewController.refreshData) , for: UIControl.Event.valueChanged)
tableView.addSubview(refresher)
}
#objc func refreshData(sender: Any) {
loadTopPosts()
refresher.endRefreshing()
}
And with this I load all posts:
func loadTopPosts() {
ProgressHUD.show("Lade...", interaction: false)
self.postArray.removeAll()
self.tableView.reloadData()
// Aktuelle Location des aktuell eingeloggten Users laden
guard let currentUserUid = UserApi.shared.CURRENT_USER_ID else { return }
let databaseRef = LocationApi.shared.geoRef
databaseRef.getLocationForKey(currentUserUid) { (location, error) in
if error != nil {
ProgressHUD.showError("Posts konnten nicht geladen werden")
} else if location != nil {
print("Location for \(currentUserUid) is [\(location!.coordinate.latitude), \(location!.coordinate.longitude)]")
// Alle Posts im vorgegebenen Umkreis laden
let REF_GEO_POSTS = Database.database().reference().child("geolocation_posts")
let geoRef = GeoFire(firebaseRef: REF_GEO_POSTS)
// Lade den aktuell eingestellten Radius aus der Datenbank
self.observeRadius(completion: { (radius) in
let currentRadius = radius
// Üperprüfe, welche Posts im Umkreis erstellt wurden
let circleQuery = geoRef.query(at: location!, withRadius: Double(currentRadius)!)
circleQuery.observe(.keyEntered, with: { (postIds, location) in
self.observePost(withPostId: postIds, completion: { (posts) in
guard let userUid = posts.uid else { return }
self.observeUser(uid: userUid, completion: { (users) in
let postArray = UserPostModel(post: posts, user: users)
self.postArray.append(postArray)
self.postArray.sort(by: {$0.post!.secondsFrom1970! > $1.post!.secondsFrom1970!})
self.tableView.reloadData()
self.tableView.setContentOffset(CGPoint.zero, animated: true)
ProgressHUD.dismiss()
})
})
})
})
if self.postArray.count == 0 {
ProgressHUD.dismiss()
}
} else {
ProgressHUD.showError("Posts konnten nicht geladen werden")
}
}
}
Here are the functions where I over serve datas from firebase:
let REF_POSTS = Database.database().reference().child("posts")
func observePost(withPostId id: String, completion: #escaping (PostModel) -> Void) {
REF_POSTS.child(id).observeSingleEvent(of: .value) { (snapshot) in
guard let dic = snapshot.value as? [String: Any] else { return }
let newPost = PostModel(dictionary: dic, key: snapshot.key)
completion(newPost)
}
}
let REF_USERS = Database.database().reference().child("users")
func observeUser(uid: String, completion: #escaping (UserModel) -> Void) {
REF_USERS.child(uid).observeSingleEvent(of: .value) { (snapshot) in
guard let dic = snapshot.value as? [String: Any] else { return }
let newUser = UserModel(dictionary: dic)
completion(newUser)
}
}
func observeRadius(completion: #escaping (String) -> Void) {
guard let currentUserUid = UserApi.shared.CURRENT_USER_ID else { return }
let REF_RADIUS = Database.database().reference().child("users").child(currentUserUid).child("radius")
REF_RADIUS.observeSingleEvent(of: .value) { (radius) in
let currentRadius = radius.value as? String
completion(currentRadius!)
}
}
What I now want to do is to disable the realtime function (updating the tableView only if I refresh). So if I refresh, everything will be displayed correctly.
How to solve this problem?
Thanks in advance for your help!
There is observe, that keeps notifying every time an update happens in the database. And there is observeSingleEvent, that will only provide you data when requested.
https://firebase.google.com/docs/database/ios/read-and-write
And also, your geofire reference will keep notifying you for every update, if you don't want it to do that, remove it like this:
"If you're not interested in getting updates on new/moving users after the initial query, this is also a great moment to remove your observer by calling removeObserverWithFirebaseHandle or removeAllObservers."
in your case it's posts, and that was mentioned here:
https://stackoverflow.com/a/50722984/8869493

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

Fetch posts from postId firebase & swift

I want to fetch the specific posts by they're postId. Is this something that is possible to do with firebase and swift?
Currently I'm getting a key from geofire which has all the postId keys that I want in it. Is it possible to get the post data relating to these postIds Here is my current attempt. My current attempt doesn't append any of the posts to my collectionView
var posts = [Post]()
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)
guard let uid = Auth.auth().currentUser?.uid else { return }
_ = circleQuery?.observe(.keyEntered, with: { (key, location) in
print(key)
let ref = Database.database().reference().child("posts")
var postId = key
ref.child(uid).child(postId).observe(.value, with: { (snapshot) in
self.collectionView?.refreshControl?.endRefreshing()
guard let dictionaries = snapshot.value as? [String: Any] else { return }
dictionaries.forEach({ (key, value) in
guard let dictionary = value as? [String: Any] else { return }
guard let user = Auth.auth().currentUser?.uid as? User 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
})
})
}, withCancel: { (error) in
print("There was an error getting the posts:", error)
})
self.locationManager.stopUpdatingLocation()
})
}
use snapshot haschild to check
let ref = Database.database().reference().child("posts")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.hasChild("\(self.yourValueToCompare!)"){
print("Exists in Database")
}else{
print("Does not Exists in Database")
}
})

Firebase - Swift - Delete a child UID from a snapshot

I am trying to delete up the studentUser UID from my staffUsers. The UID I want to delete is nested in the staffUsers -> studentSession1List.
I have the UID listed with A Bool of True on creation. This "studentSession1List" will have lots of studentUsers in the list. I only want the studentUser that is logged in to have their UID(55FDLm9n6LccZBB7skaCbvfSHRz1) removed from the list.
let dataref = Database.database().reference()
dataref.child("staffUsers").queryOrdered(byChild: "studentSession1List").observe(.value, with: { (snapshot) in
for snap in snapshot.children {
guard let studentUID = Auth.auth().currentUser?.uid else { return }
let snapDataSnapshot = snap as! DataSnapshot
var snapValues = snapDataSnapshot.value as? [String: AnyObject]
if var snapWithReg = snapValues?["studentSession1List"] as? [String: Bool] {
print("This is the staff member")
print(snapWithReg)
print(snapWithReg.count)
snapWithReg.removeValue(forKey: studentUID)
}
}
}) { (error) in
print(error.localizedDescription)
}
Here is the output:
Full Function for Deleting and Adding the Student
func didSelect(for cell: StudentSearchCell) {
guard let indexpath = collectionView?.indexPath(for: cell) else { return }
let staffUser = self.users[indexpath.item]
let selectedUserId = staffUser.uid
guard let studentUID = Auth.auth().currentUser?.uid else { return }
let dataRef = Database.database().reference()
dataRef.child("staffUsers").queryOrdered(byChild: "studentSession1List").observe(.value, with: { (snapshot) in
for snap in snapshot.children {
guard let studentUID = Auth.auth().currentUser?.uid else { return }
let snapDataSnapshot = snap as! DataSnapshot
var snapValues = snapDataSnapshot.value as? [String: AnyObject]
if (snapValues? ["studentSession1List"] as? [String: Bool]) != nil {
dataRef.child("staffUsers").child(snapDataSnapshot.key).child("studentSession1List").child(studentUID).removeValue(completionBlock: { (error, ref) in
if error != nil {
print("Error: \(String(describing: error))")
return
}
print("Removed successfully")
})
}
}
}) { (error) in
print(error.localizedDescription)
}
// Add student to staff list
let ref = Database.database().reference().child("staffUsers").child(selectedUserId).child("studentSession1List")
let values = [studentUID: true]
ref.updateChildValues(values) { (err, ref) in
if let err = err {
print("Failed to follow user:", err)
return
}
}
// Add selected staff to student list
let studentRef = Database.database().reference().child("studentUsers").child(studentUID).child("studentSession1List")
studentRef.removeValue()
let studentValues = [selectedUserId: true]
studentRef.updateChildValues(studentValues) { (err, studentRef) in
if let err = err {
print("Failed to follow user:", err)
return
}
}
self.navigationController?.popViewController(animated: true)
}
I think you need to reach the child that you want to remove using the following code and then remove it.
Edit1:
Since inside staffUsers we have keys inside which studentSession1List is present inside which the value (studentUID) is present that we want to remove, so inside your already written code I have added the new code, please check
let dataref = Database.database().reference()
dataref.child("staffUsers").queryOrdered(byChild: "studentSession1List").observe(.value, with: { (snapshot) in
for snap in snapshot.children {
guard let studentUID = Auth.auth().currentUser?.uid else { return }
let snapDataSnapshot = snap as! DataSnapshot
var snapValues = snapDataSnapshot.value as? [String: AnyObject]
if var snapWithReg = snapValues?["studentSession1List"] as? [String: Bool] {
//Added code here
dataref.child("staffUsers").child(snapDataSnapshot.key).child("studentSession1List").child(studentUID).removeValue(completionBlock: { (error, ref) in
if error != nil {
print("Error: \(error)")
return
}
print("Removed successfully")
})
}
}
}) { (error) in
print(error.localizedDescription)
}
Edit2:
To delete the code once , we can use observeSingleEvent
observeSingleEvent(of: .value, with: { (snapshot) in
}, withCancel: nil)