Swift Firebase snapshot.allValues update - ios

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

Related

Retrieve firebase data where users root collection references a sibling collection

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.

How to know if a Firebase has fetched a value

I have three functions getNewOrder(),storeOrderDetails(_ details:[String:String]) and getUserInfo(_ userID:String).
Function getNewOrder() is called first. It fetches new orders ( .childAdded values) and sends the dictionary to storeOrderDetails(_ details:[String:String]).
storeOrderDetails(_ details:[String:String])then segregate all the values and callsgetUserInfo(_ userID:String)` by passing it userID which was present in its details.
getUserInfo(_ userID:String) then fetches users details and returns user's
information.
However, the problem is [ userInfo = getUserInfo(_ userID:String) in storeOrderDetails(_ details:[String:String]) ] userInfo is always empty. Apparently func getUserInfo(_ userID:String) goes into a completion block after it has returned empty value.
I want these three functions to execute in sequential way.
Any advice is highly appreciated.
Please follow the below Links to review my code.
https://imgur.com/hNjvyDk
https://imgur.com/J0LMXMg
func childAdded(){
let ref = Database.database().reference().child("Orders").child(todaysDate)
ref.observe(.childAdded) { (snapshot) in
var details = [String:String]()
if let orderID = snapshot.key as? String {
ref.child(orderID).observeSingleEvent(of: .value, with: { (snap) in
self.newOrderTextView.text = ""
self.customerNameLabel.text = ""
self.customerPhoneLabel.text = ""
self.orderNumberLabel.text = ""
let enumerator = snap.children
while let rest = enumerator.nextObject() as? DataSnapshot {
details[rest.key as? String ?? ""] = rest.value as? String ?? ""
}
self.storeUserDetails(details)
})
}
}
}
func storeUserDetails(_ details:[String:String]){
if details["CustomerID"] != nil {
userInfo = getUserDetails(details["CustomerID"]!)
print(userInfo)
}
if !userInfo.isEmpty{
let order = OrderDatabase()
order.customerEmail = userInfo["Email"]!
order.customerName = userInfo["Name"]!
order.orderAcceptStatus = details["OrderStatus"]!
order.customerOrderNumber = details["orderNumber"]!
order.orderID = details["orderID"]!
order.time = details["Time"]!
order.customerFirebaseID = details["CustomerID"]!
self.orderDatabase[details["orderNumber"]!] = order
self.orderTable.reloadData()
}
}
func getUserDetails(_ userID:String) -> [String:String]{
var details = [String:String]()
let userDetailsReference = Database.database().reference().child("Users")
userDetailsReference.child(userID).observeSingleEvent(of: DataEventType.value, with: { (snapshot) in
if let dictionary = snapshot.value as? NSDictionary {
self.customerNameLabel.text = dictionary.value(forKey: "Name") as? String
self.customerPhoneLabel.text = dictionary.value(forKey: "Email") as? String
details["Name"] = dictionary.value(forKey: "Name") as? String
details["Email"] = dictionary.value(forKey: "Email") as? String
}
})
return details
}
From what I can see here, I am betting that the issue you are facing has to do with the fact that the methods are asynchronous. So one thing is not completely finished and some other method gets fired too soon. There are a few ways to deal with this issue. One is completion handlers, and the other is adding observers. Below is an example of doing both for Firebase. Here I'm asking a getLocationPhotos method to get all the photos from Firebase. Notice the observers and completion handler
func getLocationPhotos(coordinate:CLLocationCoordinate2D){
dbHandler.getImageFileNames(coordinateIn: coordinate) { (filenames) in
if filenames.isEmpty {
log.debug(String.warningGet + "filenames is empty")
return
}//if filenames.isEmpty
self.imageFiles = filenames.filter { $0 != "none" }
if self.imageFiles.isEmpty {
log.error(String.errorGet + "imageFiles array is empty")
return
}//if imageFiles.isEmpty
for file in self.imageFiles {
let reference = self.storageHandler.imageReference.child(file)
let download = self.imageView.sd_setImage(with: reference)
if let i = self.imageView.image {
self.imageArray.append(i)
self.collectionView.reloadData()
}//let i
download?.observe(.progress, handler: { (snapshot) in
guard let p = snapshot.progress else {
return
}//let p
self.progressView.progress = Float(p.fractionCompleted)
if self.progressView.progress == Float(1) {
self.progressView.isHidden = true
}
})//progress
download?.observe(.success, handler: { (snapshot) in
self.progressView.progress = 1
self.progressView.isHidden = true
self.collectionView.setNeedsLayout()
})//success
download?.observe(.failure, handler: { (snapshot) in
log.error(String.errorGet + "Error occured getting data from snapshot")
})//failure
}//for file
}//dbHandler

Swift 3 Firebase retrieving key and passing to view controller

I've spend hours looking at identical questions but none of the answers I've found are helping this issue. Simple app retrieves data from Firebase Database and passes to another view controller from the tableview. The main data will pass through but I can't edit the information without an identifying "key" which I tried to set as childByAutoID() but then changed to a timestamp. Regardless of the method, all I get is the entries info not the actual key itself.
func loadData() {
self.itemList.removeAll()
let ref = FIRDatabase.database().reference()
ref.child(userID!).child("MyStuff").observeSingleEvent(of: .value, with: { (snapshot) in
if let todoDict = snapshot.value as? [String:AnyObject] {
for (_,todoElement) in todoDict {
let todo = TheItems()
todo.itemName = todoElement["itemName"] as? String
todo.itemExpires = todoElement["itemExpires"] as? String
todo.itemType = todoElement["itemType"] as? String
self.itemList.append(todo)
print (snapshot.key);
}
}
self.tableView.reloadData()
}) { (error) in
print(error.localizedDescription)
}
}
If your data looks like this:
Uid: {
MyStuff: {
AutoID: {
itemName: “Apocalypse”,
itemExpires: “December 21, 2012”,
itemType: “Catastrophic”
}
}
}
Then I would query like this:
ref.child(userID!).child("MyStuff").observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let child = child as? DataSnapshot
let key = child?.key as? String
if let todoElement = child?.value as? [String: Any] {
let todo = TheItems()
todo.itemName = todoElement["itemName"] as? String
todo.itemExpires = todoElement["itemExpires"] as? String
todo.itemType = todoElement["itemType"] as? String
self.itemList.append(todo)
self.tableView.reloadData()
}
}
})
Additionally, like I said in my comment you can just upload the key with the data if you’re using .updateChildValues(). Example:
let key = ref.child("userID!").childByAutoId().key
let feed = ["key": key,
“itemName”: itemName] as [String: Any]
let post = ["\(key)" : feed]
ref.child("userID").child("MyStuff").updateChildValues(post) // might want a completionBlock
Then you can get the key the same way you are getting the rest of the values. So your new data would look like this:
Uid: {
MyStuff: {
AutoID: {
itemName: “Apocalypse”,
itemExpires: “December 21, 2012”,
itemType: “Catastrophic”,
key: “autoID”
}
}
}
The key you are trying to look for is located in the iterator of your for loop
Inside your if-let, try to do this:
for (key,todoElement) in todoDict {
print(key) // this is your childByAutoId key
}
This should solve the problem. Otherwise show us a screen of your database structure

Firebase - how to get the key value in observeEventType = Value

This is a follow up question to Firebase - proper way to structure the DB
I have the following DB structure:
"artists" : {
"-KKMkpA22PeoHtBAPyKm" : {
"name" : "Skillet"
}
}
And I want to query the artists ref and see if an artist is already in the DB or not and if the artist IS in the DB, get the artist key (in the above example it would be -KKMkpA22PeoHtBAPyKm).
I tried this:
artistsRef.queryOrderedByChild("name").queryEqualToValue("Skillet").observeEventType(.Value, withBlock: { (snapshot) in
if snapshot.exists() {
print("we have that artist, the id is \(snapshot.key)")
} else {
print("we don't have that, add it to the DB now")
}
})
but "snapshot.key" only gives me the parent key which is "artists".
How can I get the key I need?
In if condition, you need to get allKeys to get "-KKMkpA22PeoHtBAPyKm" ...
if snapshot.exists() {
for a in (snapshot.value?.allKeys)!{
print(a)
}
} else {
print("we don't have that, add it to the DB now")
}
Here's a solution.
let ref = self.myRootRef.childByAppendingPath("artists")
ref.queryOrderedByChild("name").queryEqualToValue("Skillet")
.observeEventType(.Value, withBlock: { snapshot in
if ( snapshot.value is NSNull ) {
print("Skillet was not found")
} else {
for child in snapshot.children { //in case there are several skillets
let key = child.key as String
print(key)
}
}
})
You can get the Keys with the help of Dictionary itself.
Database.database().reference().child("artists").observe(.value, with: { (snapshot) in
if snapshot.exists() {
if let artistsDictionary = snapshot.value as? NSDictionary {
for artists in artistsDictionary.keyEnumerator() {
if let artistsKey = artists as? String {
print(artistsKey) // Here you will get the keys.
}
}
}
} else {
print("no data")
}
}) { (error) in
print(error)
}

leaky listener firebase ios

I am trying to load message box data for chat functionality.
The message box is loaded as:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if (PFUser.currentUser()!["firebaseUID"] !== nil)
{
print(PFUser.currentUser()!["firebaseUID"])
self.updateResultArray(PFUser.currentUser()!["firebaseUID"] as! String)
}
}
func updateResultArray(uid: String) {
let userName = String(PFUser.currentUser()!["username"])
//print("updateResultArray is getting called")
let userhandle = self.firebase.childByAppendingPath("users").childByAppendingPath(uid).childByAppendingPath("rooms").queryOrderedByValue()
.observeSingleEventOfType(.Value, withBlock: { roomsnapshot in
let enumerator = roomsnapshot.children
while let rest = enumerator.nextObject() as? FDataSnapshot {
self.roomArray.append(rest.key)
}
//get the latest message from all the rooms
if self.roomArray.isEmpty == false
{
for i in 0...self.roomArray.count-1
{
print("in the room loop \(self.roomArray[i])")
let messagehandle = self.messagesRef.childByAppendingPath(self.roomArray[i]).queryOrderedByKey().queryLimitedToFirst(1).observeSingleEventOfType(.Value, withBlock: {
messagesnapshot in
print("the messagesnapshot child count is \(messagesnapshot.childrenCount)")
let enumerator = messagesnapshot.children
while let rest = enumerator.nextObject() as? FDataSnapshot {
let sender = rest.value.objectForKey("sender") as? String
let reciever = rest.value.objectForKey("reciever") as? String
//print("sender is \(sender!) and reciever is \(reciever!)")
let eventhandle = self.firebase.childByAppendingPath("rooms").childByAppendingPath(self.roomArray[i]).observeSingleEventOfType(.Value, withBlock: { eventsnapshot in
if eventsnapshot.value is NSNull {
// The value is null
}
else
{
let eventAttr = eventsnapshot.value.objectForKey("eventAttributes") as? String
let eventDetails = eventsnapshot.value.objectForKey("eventDetails") as? String
//print("userName is \(userName)")
//print("sender is \(sender)")
if (userName != sender!) //for event joinee
{
let firstname1 = eventsnapshot.value.objectForKey("firstname1") as? String
self.otherNames.append(sender!)
self.resultsNameArray.append(firstname1!)
self.base4String = eventsnapshot.value.objectForKey("img1") as! String
self.resultsImageFiles.append(self.base4String)
}
else //for event creator
{
let firstname2 = eventsnapshot.value.objectForKey("firstname2") as? String
self.otherNames.append(reciever!)
self.resultsNameArray.append(firstname2!)
self.base4String = eventsnapshot.value.objectForKey("img2") as! String
self.resultsImageFiles.append(self.base4String)
}
let newlineChars = NSCharacterSet.newlineCharacterSet()
let evntArray = eventDetails!.componentsSeparatedByCharactersInSet(newlineChars).filter{!$0.isEmpty}
self.eventArray.append(evntArray[0])
self.eventdetailsArray.append(eventAttr!)
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.resultsTable.reloadData()
}
}
})
// self.firebase.removeAuthEventObserverWithHandle(eventhandle)
}
})
//self.messagesRef.removeAuthEventObserverWithHandle(messagehandle)
}
}
})
//firebase.removeAuthEventObserverWithHandle(userhandle)
}
since i am using observeSingleEventOfType i havent coded to remove handlers( i have tried that as well).
In the individual chat, the code is like this:
func refreshResults() {
print("the roomid is \(roomid)")
//update from firebase
let messagehandle = self.messagesRef.childByAppendingPath(roomid).queryOrderedByKey()
.observeEventType(.Value, withBlock: { messageTextsnapshot in
self.messageArray.removeAll()
self.senderArray.removeAll()
// print("the messageTextsnapshot child count is \(messageTextsnapshot.childrenCount)") // I got the expected number of items
let enumerator = messageTextsnapshot.children
while let rest = enumerator.nextObject() as? FDataSnapshot {
let text = rest.value.objectForKey("message") as? String
let sender = rest.value.objectForKey("sender") as? String
if text != nil && text != ""
{
self.messageArray.append(text!)
self.senderArray.append(sender!)
}
}
for subView in self.resultsScrollView.subviews {
subView.removeFromSuperview()
}
for var i = 0; i <= self.messageArray.count-1; i++ {
if self.senderArray[i] == userName {
if (self.messageArray[i].rangeOfString(self.acceptMessage) != nil)
{
let chatBubbleData = ChatBubbleData(text: self.messageArray[i], image:self.myImg, date: NSDate(), type: .AcceptMine)
self.addChatBubble(chatBubbleData)
}
else
{
let chatBubbleData = ChatBubbleData(text: self.messageArray[i], image:self.myImg, date: NSDate(), type: .Mine)
self.addChatBubble(chatBubbleData)
}
} else {
if (self.messageArray[i].rangeOfString(self.acceptMessage) != nil)
{
let chatBubbleData = ChatBubbleData(text: self.messageArray[i], image:self.otherImg, date: NSDate(), type: .Accept)
self.addChatBubble(chatBubbleData)
}
else
{
let chatBubbleData = ChatBubbleData(text: self.messageArray[i], image:self.otherImg, date: NSDate(), type: .Opponent)
self.addChatBubble(chatBubbleData)
}
}
let bottomOffset:CGPoint = CGPointMake(0, self.resultsScrollView.contentSize.height - self.resultsScrollView.bounds.size.height)
self.resultsScrollView.setContentOffset(bottomOffset, animated: false)
}
})
self.messagesRef.removeAuthEventObserverWithHandle(messagehandle)
}
There are a few other listeners similar to this. the problem is when i go back from this view(individual chat to message box, the memory consumption increases. I have cleared all arrays and closed the handlers immediately after use. but still memory consumption increases and sometimes in message box same rows are replicated again. how should i solve this. I tried using
observeSingleEventOfType but it is not a correct solution as the data sync stops.
Used this as reference:
https://www.firebase.com/blog/2015-10-15-best-practices-uiviewcontroller-ios-firebase.html
It looks like your message box object is not being released due to a retain cycle caused by the listener callback block holding a reference to the message box object. You can alleviate this by using [weak self] in blocks that you pass to other objects. For example:
.observeSingleEventOfType(.Value, withBlock:
{
[weak self] roomsnapshot in
let enumerator = roomsnapshot.children
...
This makes 'self' an optional type, and you can then add:
guard let strongSelf = self else { ... }
The problem was that i was closing the listeners on the parent and not on the child. so the listeners were still in memory.
When i closed the listeners on the full path it worked.

Resources