Appending to an array in Firebase - Asynchronous and reloadData() - ios

I am having trouble with the asychronous nature of Firebase, particulary appending to an array from within an observe function.
Any suggestions or help would be much appreciated :)
The comp that is appended to the users array in the selectUsersComp function disappears when the firebase observe function is exited, even though I reload the data in the collectionView.
I have tried using Dispatch.main.async but it has not helped. I have a Firebase observe function inside another Firebase observe function. Does this change things?
fileprivate func fetchStartedComps() {
let ref = Database.database().reference().child("startedComps")
ref.queryOrdered(byChild: "creationDate").observe(.value, with: {
(snapshot) in
guard let dictionaries = snapshot.value as? [String : Any] else { return }
dictionaries.forEach({ (key, value) in
guard let compDictionary = value as? [String: Any] else { return }
let comp = StartedComp(Id: key, dictionary: compDictionary)
self.selectUsersComp(comp: comp)
})
self.filteredStartedComps = self.startedComps
self.collectionView?.reloadData()
}) { (err) in
print("Failed to fetch comps for search", err)
}
}
func selectUsersComp(comp: StartedComp) {
guard let userId = Auth.auth().currentUser?.uid else { return }
let ref = Database.database().reference().child("startedComps").child(comp.title).child("invitedUsers")
ref.observe(.value, with: { (snapshot) in
guard let dictionaries = snapshot.value as? [String : Any] else { return }
dictionaries.forEach({ (key, value) in
if key == userId {
self.startedComps.append(comp)
}
})
self.collectionView?.reloadData()
}) { (err) in
print(err)
}
}

Related

Load new messages Swift 4.2 & Firebase

I have created a messaging system for my app and am paginating the messages within the chat log but I'm having an issue that if a new message is sent the user will have to leave the screen and re open the controller to view the new messages they have sent/received. I have tried to reload the collection view and observe the messages again with no luck. Any help is appreciated.
Observing the messages. With Pagination. (working great! On initial load.)
var messages = [Message]()
fileprivate func observeMessages() {
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let userId = user?.uid else { return }
if currentKey == nil {
let userMessageRef = Database.database().reference().child("user-message").child(uid).child(userId).queryLimited(toLast: 10).observeSingleEvent(of: .value) { (snapshot) in
guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
guard var allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }
allObjects.forEach({ (snapshot) in
let messageId = snapshot.key
let ref = Database.database().reference().child("messages").child(messageId)
ref.observe(.value, with: { (snapshot) in
guard let dict = snapshot.value as? [String: Any] else { return }
let message = Message(dictionary: dict)
self.messages.append(message)
self.messages.sort(by: { (message1, message2) -> Bool in
return message1.timeStamp.compare(message2.timeStamp) == .orderedDescending
})
self.collectionView?.reloadData()
})
})
self.currentKey = first.key
}
} else {
let userMessageRef = Database.database().reference().child("user-message").child(uid).child(userId).queryOrderedByKey().queryEnding(atValue: self.currentKey).queryLimited(toLast: 4).observeSingleEvent(of: .value) { (snapshot) in
guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
guard var allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }
allObjects.forEach({ (snapshot) in
if snapshot.key != self.currentKey {
let messageId = snapshot.key
let ref = Database.database().reference().child("messages").child(messageId)
ref.observe(.value, with: { (snapshot) in
guard let dict = snapshot.value as? [String: Any] else { return }
let message = Message(dictionary: dict)
self.messages.append(message)
self.messages.sort(by: { (message1, message2) -> Bool in
return message1.timeStamp.compare(message2.timeStamp) == .orderedDescending
})
self.collectionView?.reloadData()
})
}
})
self.currentKey = first.key
}
}
}
From Firebase database documentation
In some cases you may want a callback to be called once and then immediately removed, such as when initializing a UI element that you don't expect to change. You can use the observeSingleEventOfType method to simplify this scenario: the event callback added triggers once and then does not trigger again.
I suggest you to change to observeEventType:withBlock whichs allow you to observe all changes events.
Hope this helps.
The way I set mine up was to call the function in viewDidLoad and then again in viewDidAppear. I'm still learning as well, but you may want to try that, it would probably look something like this:
override func viewDidLoad() {
super.viewDidLoad()
observeMessages(for: userID) { (messages) in
self.messages = messages
self.collectionView.reloadData()
}
}
And again in viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
observeMessages(for: userID) { (messages) in
self.messages = messages
self.collectionView.reloadData()
}
}

Images fetched from firebase are duplicated in the process of adding them to tableViewCells?

The following code fetches images from firebase, but incorrectly duplicates two images. I think that is due to the placement of the self.tableView.reloadData() None of the placements I've tried work. Can anyone give me suggestions?
func fetchAllUsersImages() {
print("inside func")
self.ref.child("Posts").child(self.userID).child(self.postNum).observe(.childAdded, with: { snapshot in
if let snapShotValue = snapshot.value as? [String: String] {
for (_, value) in snapShotValue {
if let imageURL = URL(string: value) {
print(imageURL, "image url here")
do {
let imageAsData = try Data(contentsOf: imageURL)
let image = UIImage(data: imageAsData)
let ImageObject = Image()
ImageObject.image = image
self.arrayOfImgObj.append(ImageObject)
} catch {
print("imageURL was not able to be converted into data")
}
}
}
}
})
}
Make sure you clear the array when start calling the function because you are appending data to the array. Secondly, reload table after finished the for loop.
func fetchAllUsersImages() {
self.arrayOfImgObj.removeAll() // clean the array
self.ref.child("Posts").child(self.userID).child(self.postNum).observe(.childAdded, with: { snapshot in
if let snapShotValue = snapshot.value as? [String: String] {
for (_, value) in snapShotValue {
}
tableView.reloadData() // reload view
}
})
}

how to get latest message from firebase database in ios

I have don't know what the problem please help me. When I get a particular message from firebase database then the value is getting but my app got a crash on one line.So please tell me what I do wrong in my code.below is my function.We also provide the screenshot of the error.
func getLatestMessageFromFirebase(token:String,completionmessage: #escaping (_ message:String) -> Swift.Void)
{
print("getModelFromFirebase")
var message:String=""
ref.child("chatmessage/devicetoken/").child(token).queryLimited(toLast: 1).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
if value?["message"] as? String != ""
{
DispatchQueue.main.async
{
message = (value?["message"] as? String)! //My app stop on this line
completionmessage(message)
}
}
})
{ (error) in
print(error.localizedDescription)
}
}
func callAPI()
{
let response = (jsonResult.object(forKey: "chatListArr") as? NSArray)!
if response.count > 0
{
for i in 0..<response.count
{
let dict = response[i] as! NSDictionary
let chatlist = ChatList(dict: dict)
self.arr_list.append(chatlist)
}
for i in 0..<self.arr_list.count
{
let chatlist = self.arr_list[i]
self.getLatestMessageFromFirebase(token: chatlist.token, completionmessage: { (message) in
self.arr_list[i].msg = message
})
}
self.table_view.reloadData()
}
}
Please help me.
Thanks in Advance.
First of all you should clean your code up a bit, you do a couple of things which would be considered anti patterns
func getLatestMessageFromFirebase(token:String,completionmessage: #escaping (_ message:String) -> Swift.Void)
{
ref.child("chatmessage/devicetoken/").child(token).queryLimited(toLast: 1).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
for snap in snapshot.children.allObjects as [DataSnapshot] {
let value = snap.value as? [String: Any] ?? [:] // A good way to unwrap optionals in a single line
if let message = value["message"] as? String {
DispatchQueue.main.async {
completionmessage(message)
}
}
}
})
{ (error) in
print(error.localizedDescription)
}
}
With the above code your app shouldnt crash. And if there is a message AND it is a string (which might have been your problem before) then your callback will fire.

How do I add a google maps marker when Pressing a button?

Is it possible to fetch all child nodes from parent in firebase db?
Trying to say this:
I want to get all posts at the same time.
It fetches videos but only the current users videos. I want to fetch ALL users videos at the same time.
Here's some code to get more understanding of what I'm doing:
fileprivate func fetchAllPost() {
fetchPosts()
fetchAllPostsFromUserIds()
}
fileprivate func fetchAllPostsFromUserIds() {
guard let uid = FIRAuth.auth()?.currentUser?.uid else { return }
FIRDatabase.database().reference().child("posts").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let userIdsDictionary = snapshot.value as? [String: Any] else { return }
userIdsDictionary.forEach({ (key, value) in
FIRDatabase.fetchUserWithUid(uid: key, completion: { (user) in
self.fetchPostsWithUser(user: user)
})
})
}) { (err) in
print("failed to fetch following users ids:", err)
}
}
var posts = [Post]()
fileprivate func fetchPosts() {
guard let currentUserID = FIRAuth.auth()?.currentUser?.uid else { return }
FIRDatabase.fetchUserWithUid(uid: currentUserID) { (user) in
self.fetchPostsWithUser(user: user)
}
}
fileprivate func fetchPostsWithUser(user: User) {
let ref = FIRDatabase.database().reference().child("posts/\(user.uid)/")
ref.observeSingleEvent(of: .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 }
var post = Post(user: user, dictionary: dictionary)
post.id = key
guard let uid = FIRAuth.auth()?.currentUser?.uid else { return }
FIRDatabase.database().reference().child("likes").child(key).child(uid).observe(.value, with: { (snapshot) in
if let value = snapshot.value as? Int, value == 1 {
post.hasLiked = true
} else {
post.hasLiked = false
}
self.posts.append(post)
self.posts.sort(by: { (p1, p2) -> Bool in
return p1.creationDate.compare(p2.creationDate) == .orderedDescending
})
self.collectionView?.reloadData()
}, withCancel: { (err) in
print("Failed to fetch info for post")
})
print(self.posts)
})
}) { (error) in
print("Failed to fetch posts", error)
}
}
I don't know Swift, but you could fetch FIRDatabase.database().reference().child("posts") and then iterate over children.

Search for a spesific userID in Firebase with Swift?

I'm kind of new with Firebase, I tried with firebase doc but i couldn't handle it. I want to query for specific currentUserID in "locations", if there is no currentUserID, then I want to create a child for current user. How can I do that?
// I tried this and it didn't work.
dataRef.child("locations").child(id).observe(.value, with: { (refIDSnapshot) in
guard let dicID = refIDSnapshot.value as? [String: Any] else {
return
}
if dicID.isEmpty {
dataRef.child("locations").child(id).setValue(locValues)
}
})
// I also tried this
dataRef.queryOrdered(byChild: "locations").queryEqual(toValue: id).observe(.value, with: { (snapshot) in
guard let dic1 = snapshot.value as? [String: Any] else {
return
}
if dic1.isEmpty {
dataRef.child("locations").child(id).setValue(locValues)
}
//Firebase Database Structure
"ProjectName":{
"locations":{
"hVfS9EU6LWatAynJNepayDmum4A2":{
"-KZCxgfgBjEOusuo-Fay":{
"longitude":"22.12431412",
"latitude":"13.23423423"
}
}
}
}
Edit: // This worked for me
dataRef.child("locations").child(id).observe(.value, with: { (refIDSnapshot) in
let dicID = refIDSnapshot.value as? [String: Any]
if dicID == nil {
dataRef.child("locations").child(id).childByAutoId().setValue(locValues)
}
})

Resources