Retrieving data from Firebase in Swift - ios

I've been looking at the APIs from the Firebase website, however, when I print my snapshot, I get everything from my database.
I'm trying to only print the value I just set, which is my variable: test_person_1.
import UIKit
import Firebase
class Test: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let ref = FIRDatabase.database().reference()
ref.observeEventType(.ChildAdded, withBlock: { (snapshot) in
var test_person_1 = ["full_name": "Test 1", "date_of_birth": "June 1, 2000"]
var Test_person_2 = ["full_name": "Grace Hopper", "date_of_birth": "July 1, 2000"]
let usersRef = ref.child("New child")
usersRef.setValue([test_person_1])
print(snapshot) //
})
Json tree:

By the time you print the snapshot, the value hasn't been set on the server yet.
If you want to print when the data been sent to the server, add a so-called completion listener to your setValue() call:
usersRef.setValue([test_person_1]) { (error, ref) in
print(error, ref)
}
If there is no error, you know that the value on the server is what you just wrote. But if you want to read back the value from the server, you'll need to attach a listener for it:
usersRef.setValue([test_person_1]) { (error, ref) in
ref.observeSingleEventOfType(.Value) { (snapshot) in
print ("new value \(snapshot.value)")
}
}

Related

Swift : Firebase query never goes in call

Im trying to access to my firebase db with Swift
import Foundation
import FirebaseDatabase
import FirebaseAuth
import Firebase
struct FirebaseManager {
private let reference = Database.database().reference()
func fetch (_ child: String, completion: #escaping (DataSnapshot) -> Void) {
print("before reference")
reference
.child(child)
.observeSingleEvent(of: .value) { (snapdata) in
print("inside reference")
guard let response = snapdata.value else { return print("error snapdata") }
print(response)
completion(snapdata)
}
print("after reference")
}
}
To check if code is running correctly, I put some prints and this is what I see in Xcode console:
before reference
after reference
There's neither "inside reference" nor response value nor "error snapdata"
So I've deduced it never goes inside it ! And I checked: my firebase db is not empty and corresponds to child parameter string.
I don't understand where is the problem.
Same problem here : iOS Swift - Firebase query never gets called
EDIT: I also tried this:
.observeSingleEvent(of: .value, with: { (snapshot) in
print("observe single event")
if snapshot.exists() {
if let value = snapshot.value as? String {
print("inside if let", value)
}
} else {
print("else")
// username doesn't exist
}
}, withCancel:{ error in
print("cancel", error)
})
And the ouput in console is the same. Nothing appears
And this is a screenshot in rules:
You might want to use observeSingleEvent(of:with:withCancel:) instead, to see if the query gets cancelled by the database. If it does, the user does not have permission to read the data, and you should read the documentation on security rules.

Not able to convert the Realtime Database code to Firestore code

I am trying to configure the database for the firestore database, but not able to do so, facing several errors, and not getting any particular solution for that, I am using swift for ios, it is basically a chat app followed from the codelab tutorial, here's the link to the same tutorial! this tutorial is based on Firebase Realtime database, and my requirement is of Firestore database,
thanks in advance for the guidance
What I Tried for converting to firestore database
var messages: [DocumentSnapshot]! = []
fileprivate var _refHandle: CollectionReference!
var ref = Firestore.firestore().collection("messages")
func configureDatabase() {
//ref = Firestore.firestore() // Listen for new messages in the Firebase database
_refHandle = self.ref.addSnapshotListener{ querySnapshot, error in
if let documents = querySnapshot?.documents {
var messages = [DocumentSnapshot]()
for document in documents {
let message = messages(snapshot: document)
message.append(message)
message.clientTable.insertRows(at: [IndexPath(row: message.messages.count-1, section: 0)], with: .automatic)
}
// completion(messages)
}
} as! CollectionReference
}
RealtimeDatabase Code
var ref: DatabaseReference!
var messages: [DataSnapshot]! = []
fileprivate var _refHandle: DatabaseHandle?
deinit {
if let refHandle = _refHandle {
self.ref.removeObserver(withHandle: _refHandle)
}
}
func configureDatabase() {
ref = Database.database().reference()
// Listen for new messages in the Firebase database
_refHandle = self.ref.child("messages").observe(.childAdded, with: { [weak self] (snapshot) -> Void in
guard let strongSelf = self else { return }
strongSelf.messages.append(snapshot)
strongSelf.clientTable.insertRows(at: [IndexPath(row: strongSelf.messages.count-1, section: 0)], with: .automatic)
})
}
Error in firestore Code
Cannot call value of non-function type '[DocumentSnapshot]'"
at
let message = messages(snapshot: document)
This may or may not help but here's a function that reads in all messages and prints the message id (documentId) and the message text to console. Assume we have a class var messsagesArray set up to store each message and a class that hold the documentId and the message text.
Assume this structure
messages //the collection
msg_id_0 //the document id
msgText "Hello"
msg_id_1
msgText:"World"
and the code to read and print the messages
func readAllMessages() {
let messagesCollection = self.db.collection("messages")
messagesCollection.getDocuments(completion: { snapshot, err in
if let err = err {
print(err.localizedDescription)
return
}
for doc in snapshot!.documents {
let key = doc.documentID
let msg = doc.get("msg") as! String
let aMsg = MessageClass(aKey: key, msgText: msg)
self.messagesArray.append(aMsg)
}
for msg in self.messagesArray { //output the read messages to console
print(msg.key, msg.msgText)
}
})
}

the retrieved data doesn't append in my array swift4

I'm trying to append "the retrieved data -Keys- from firebase" into an array but it doesn't work
This is the for loop output #2 the retrieved keys
This the keys from firebase
This is the code
let ref = Database.database().reference()
ref.child("Faculty ").observe(.value, with: { (snapshot) in
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let FacultyName = child.key as! String
print(FacultyName)
self.NamesofFac.append(FacultyName)
}
}
})
for i in 0...self.NamesofFac.count {
print(self.NamesofFac.count)
print(" line")
print(self.NamesofFac)
The problem you are having is the Firebase Observe function give a callback in the form of a (snapshot).
It takes a bit of time to go to the web to get the data, therefore, firebase returns the data asynchronously. Therefore your code in your for loop will run before your firebase data has been returned. At the time your for loop code runs the array is still blank. But the for loop code in a separate function as you see in my sample code and call it straight after your for loop inside your firebase observe call.
Try this instead:
override func viewDidLoad() {
getFirebaseData()
}
func getFirebaseData() {
let ref = Database.database().reference()
ref.child("Faculty ").observe(.value, with: { (snapshot) in
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let FacultyName = child.key as! String
print(FacultyName)
self.NamesofFac.append(FacultyName)
}
printNames()
}
})
}
func printNames() {
for i in 0...self.NamesofFac.count {
print(self.NamesofFac.count)
print(" line")
print(self.NamesofFac)
}
}
This was it won't print the names until they have been fully loaded from firebase.
PS: Your naming conventions are incorrect. You seem to be naming variables with a capital letter. Variables should be camel case. Classes should start with a capital.

How do you make a function that has to do a query on the firebase database and return the value of the query as an int?

We are currently making an iOS app and with firebase as its database. Please find below our code.
static func getTilesPerRow () -> Int{
let user = Auth.auth().currentUser
guard let uid = user?.uid else {
return -2
}
var ref: DatabaseReference!
ref = Database.database().reference()
let userRef = ref.child("user").child(uid)
var num = -1
let queue = DispatchQueue(label: "observer")
userRef.child("tilesPerRow").observe(DataEventType.value, with: { (snapshot) in
// Get user value
print("now inside the observe thing------------------")
let value = snapshot.value as? NSDictionary
num = snapshot.value as? Int ?? 0
print("just updated the number to ", num)
print("the snapshot is ", snapshot)
print("the value is ", value)
print("the real value is", snapshot.value)
print("just making sure, the number that was set is ", num)
}) { (error) in
print("there was an error!!!!!!!!!!!!!!!!!")
print(error.localizedDescription)
}
print("about to return from the function ", num)
return num
}
Currently while running this code, we get the following output.
about to return from the function -1
now inside the observe thing------------------
just updated the number to 5
the snapshot is Snap (tilesPerRow) 5
the value is nil
the real value is Optional(5)
just making sure, the number that was set is 5
Our expected output is:
now inside the observe thing------------------
just updated the number to 5
the snapshot is Snap (tilesPerRow) 5
the value is nil
the real value is Optional(5)
just making sure, the number that was set is 5
about to return from the function 5
The problem here is that we are trying to grab the value of what the query has found, but because .observe() is asynchronous, the function finishes before .observe() updates the value of num. How do we return the correct value?
You don't.
To get the asynchronous operation result you use blocks.
static func getTilesPerRow (#escaping completion: (Int?)->Void ) {
let user = Auth.auth().currentUser
guard let uid = user?.uid else {
completion(nil)
}
var ref: DatabaseReference!
ref = Database.database().reference()
let userRef = ref.child("user").child(uid)
userRef.child("tilesPerRow").observeSingleEvent(DataEventType.value, with: { (snapshot) in
// Get user value
print("now inside the observe thing------------------")
let value = snapshot.value as? NSDictionary
let num = snapshot.value as? Int ?? 0
completion(num)
}) { (error) in
print("there was an error!!!!!!!!!!!!!!!!!")
print(error.localizedDescription)
completion(nil)
}
}
When the results are ready you will get notified through the block. Upon success you get the actual num you are looking for or nil upon any error occurred.
Even you can distinguish that what sort of error occurred by adding extra parameter on your parameter list in completion block.
You also could use protocol, but thats require more knowledge like, in which class this code reside, who is the caller this sort of things. Set the protocol target to the caller, and upon completion called method will fire different protocol method based on the error or successful case occurred.
Happy coding.

Firebase Event listener doesn't work and I can't receive the right snapshot

I'm new here, please excuse me if I didn't manage to create a good
and well formatted question.
My problem is that I can't get a snapshot of a specific child in Firebase.
I need to put an event listener which will return me the value( the token of the user) when there is a change in the database:
notifications.child("tokens").child("id_user_token").child(user).
I also post an image of the database tree so you can see what's in there.
My purpose is to save the token retrieved from the Database and to create a HashMap(to build the notification) which will contain the token, a title and a message. As you can see from the code I pass all the parameters with the init() function. I try to call this function in a ViewController.swift like this:
"FirebaseMessagingService.init(user: "2", title: "Test",message: "Test")"
As you can see from the database image there is the user with id = 2 but when I try to print the snapshot it prints null and I can't understand what's wrong with the code.Image of the Database tree
Here is the exported JSON
import Foundation
import FirebaseMessaging
import FirebaseDatabase
class FirebaseMessagingService{
var mDatabase : DatabaseReference
var notifications = DatabaseReference()
init(user: String,title: String, message:String){
self.mDatabase = Database.database().reference()
self.notifications = Database.database().reference()
notifications = mDatabase.child("notificationRequests")
var notification = NSMapTable<AnyObject, AnyObject>()
notifications.child("tokens").child("id_user_token").child(user).observeSingleEven t(of: .value, with: { snapshot in
var token = Messaging.messaging().fcmToken! as String
print("\n\n\n\n\(snapshot)")
token = (snapshot.value as? String)!
notification.setValue(token, forKey: "toUser")
notification.setValue(title, forKey: "title")
notification.setValue(message, forKey: "message")
}){ (error) in
print("\n\n\n\n")
print(error.localizedDescription)
}
Try changing your code as follows
import FirebaseDatabase
class FirebaseMessagingService{
var ref: DatabaseReference!
init(user: String, title: String, message: String) {
self.ref = Database.database().reference()
let notifRef = self.ref.child("notificationRequests")
let thisUserRef = notifRef.child("tokens").child("id_user_token").child(user)
print(thisUserRef)
thisUserRef.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})
}
}
It should at least, print thisUserRef which should be
yourDataBase/notificationRequests/tokens/id_user_token/2
It should also print the snapshot
snap( 2: cq3Xy...... )
Let us know what prints and if the paths are correct when it does print.
Also, in your question you stated your goal to be
I need to put an event listener which will return me the value( the
token of the user) when there is a change in the database:
The code your provided won't do that as it. You're using
.observeSingleEvent
Which does just that - observes a single event, one time. It will not leave a listener on the node for future events.
If you want to observe all events then use
.observe(.value... (or .childAdded etc)
However, I don't think you meant that you wanted to observe that specific node for changes (or maybe you did). You may have meant that when there is a change in the database elsewhere, you will then need to get the uid of that user, which the code in your question is trying to do.
Last thing:
You really shouldn't be defining this as a class. It should really be a function that is called like so within your viewController
class ViewController: UIViewController {
var ref: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
self.showUid(user: "2", title: "some title", message: "some message")
}
func showUid( user: String, title: String, message: String) {
let notifRef = self.ref.child("notificationRequests")
let thisUserRef = notifRef.child("tokens").child("id_user_token").child(user)
print(thisUserRef)
thisUserRef.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})
}
}

Resources