I have global observer in ViewController and need some different observers inside it for specific values like one below. Is it possible to remove observer after value change once?
var ref = Firebase(url: "https://<FIREBASE-APP>.firebaseio.com/")
let handle = ref.observeEventType(.Value, withBlock: { snapshot in
//Here VALUE Changes to NEW_VALUE
if snapshot.value as! String == NEW_VALUE {
//IS IT POSSIBLE TO REMOVE HANDLE HERE????
...something here
}
})
//NOT HERE
...ref.removeObserverWithHandle(handle)
This is one of the cases where you need to take an extra step in Swift, since it doesn't realize that you can safely access handle inside the block.
One way of working around this is:
let ref = Firebase(url: "https://yours.firebaseio.com/")
var handle: UInt = 0
handle = ref.observeEventType(.Value, withBlock: { snapshot in
print(snapshot)
if snapshot.exists() && snapshot.value as! String == "42" {
print("The value is now 42")
ref.removeObserverWithHandle(handle)
}
})
By explicitly initializing the handle variable, we remove the error from the Swift compiler. But given that the handle will have been set before our block is invoked, we can safely call ref.removeObserverWithHandle(handle) inside the block.
Related
I have a TableView that I am updating from time to time with a Timer so that the data of the TableView changes if necessary... What happens is that the data is updated but it is duplicated and it does not eliminate the data that it had previously, then it is generating a giant TableView.
How could I make them update but delete the data I had previously and leave only the new cells when the tableview is updated?
This is the code that I use in the timer to update the TV:
#objc func updatetableview(){
databaseRef.child("A_Usuarios").queryOrdered(byChild: "TipoUsuario").queryEqual(toValue: "Empresa").observe(.childAdded, with: { (snapshot) in
let key = snapshot.key
self.snap = (snapshot.value as? NSDictionary)!
self.snap.setValue(key, forKey: "Uid")
self.city = self.snap["Ciudad"] as? String ?? ""
self.activo = self.snap["Activo"] as? String ?? ""
if self.city == self.cdad && self.activo != "No" {
if(key == self.loggedInUser?.uid){
print("Same as logged in user, so don't show!")
}
else
{
self.usersArray.append(self.snap)
//insert the rows
self.tableview.insertRows(at: [IndexPath(row:self.usersArray.count-1,section:0)], with: UITableView.RowAnimation.automatic)
}
}
}) { (error) in
print(error.localizedDescription)
}
I hope you can help me, thank you very much!
You need to clear the array before doing another observe
#objc func updatetableview(){
usersArray.removeAll()
....
}
BTW .observe(.childAdded is supposed to do the job , so you may need n't to do this
The observer you have used returns all the values in the given path. This closure is called when a new child is added. But it gets all the available in that path, not only the newly added data.
This is why it is generating a giant TableView.
So you need to get the newly added data only using queryLimited(toLast: UInt)
databaseRef.child("A_Usuarios").queryOrdered(byChild: "TipoUsuario").queryEqual(toValue: "Empresa").queryLimited(toLast: 1).observe(.childAdded, with: { (snapshot) in
In firebase, if you are observing same node 5 times then it will give you 5 events. So you are adding same record 5 times. So you need to check that you are already observing that node before observing. Following code creating a problem. Every time you call updateTableView. It is adding new observer and that why you get same records multiple times.
databaseRef.child("A_Usuarios").queryOrdered(byChild: "TipoUsuario").queryEqual(toValue: "Empresa").observe(.childAdded, with: { (snapshot) in
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.
As the title says I have a weird problem to retrieve simple data from Firebase, but I really can't figure out where I'd go wrong.
This is my schema:
And this the code:
import Firebase
let DB_BASE = Database.database().reference()
class FirebaseService {
static let instance = FirebaseService()
private var REF_BASE = DB_BASE
private var REF_SERVICE_STATUS = DB_BASE.child("Service_Status")
struct ServiceStatus {
var downloadStatus: Bool
var uploadStatus: Bool
}
func getServiceStatus() -> (ServiceStatus?) {
var serviceStatus: ServiceStatus?
REF_SERVICE_STATUS.observeSingleEvent(of: .value) { (requestSnapshot) in
if let unwrapped = requestSnapshot.children.allObjects as? [DataSnapshot] {
for status in unwrapped {
serviceStatus.downloadStatus = status.childSnapshot(forPath: "Download_Status").value as! Bool
serviceStatus.uploadStatus = status.childSnapshot(forPath: "Upload_Status").value as! Bool
}
// THANKS TO JAY FOR CORRECTION
return sponsorStatus
}
}
}
}
but at the end serviceStatus is nil. Any advice?
I think you may be able to simplify your code a bit to make it more manageable. Try this
let ssRef = DB_BASE.child("Service_Status")
ssRef.observeSingleEvent(of: .value) { snapshot in
let dict = snapshot.value as! [String: Any]
let down = dict["Download_Status"] ?? false
let up = dict["Upload_Status"] ?? false
}
the ?? will give the down and up vars a default value of false if the nodes are nil (i.e. don't exist)
Oh - and trying to return data from a Firebase asynchronous call (closure) isn't really going to work (as is).
Remember that normal functions propagate through code synchronously and then return a value to the calling function and that calling function then proceeds to the next line of code.
As soon as you call your Firebase function, your code is going to happily move on to the next line before Firebase has a chance to get the data from the server and populate the return var. In other words - don't do it.
There are always alternatives so check this link out
Run code only after asynchronous function finishes executing
I'm new to firebase and I want to know if is any possible way to return data in observer block. I have class ApiManager:NSObject and in this class I want to create all my firebase function that will return some kind of data from database. This is one of my function in this class
func downloadDailyQuote() -> [String:String] {
let reference = Database.database().reference().child("daily")
reference.observeSingleEvent(of: .value) { (snap) in
return snap.value as! [String:String] //I want to return this
}
return ["":""] //I don't want to return this
}
And if I now do something like let value = ApiManager().downloadDailyQuote(), value contains empty dictionary. Is any solution for that?
Update: When you call .observeSingleEvent, you call the method asynchronously. This means that the method will start working, but the response will come later and will not block the main thread. You invoke this method, but there is no data yet and therefore you return an empty dictionary.
If you use the completion block, then you will get the data as soon as the method action is completed.
func downloadDailyQuote(completion: #escaping ([String:String]) -> Void) {
let reference = Database.database().reference().child("daily")
reference.observeSingleEvent(of: .value) { (snap) in
if let dictionaryWithData = snap.value as? [String:String] {
completion(dictionaryWithData)
} else {
completion(["" : ""])
}
}
}
I have a ref that exists and I use observeEventType to query the data. But the ref might not have data in it due to the user deleting it. I test it using snapshot.exists(). In the situation below snapshot.exists() will return false/no. Since it's false I want to do something else but the code never runs
How do I so something else when snapshot.exists() returns false/no?
//there is no data at levelTwo so there's nothing to observe
let levelTwoRef = dbRef.child("players").child("uid").child("levelTwo")
levelTwoRef.observeEventType(.ChildAdded, withBlock: {
(snapshot) in
if snapshot.exists(){
if let dict = snapshot.value as? [String:AnyObject]{
let power = dict["power"] as? String
let score = dict["score"] as? String
}
//this will never run because the data has been deleted
} else{
do something else as an alternative //why isn't this running??
}
})
Firebase has a .hasChild function that you can run on a child to see if it exists:
func hasChild(_ childPathString: String) -> Bool
It takes a child as a String argument and returns True or False depending on wether it exists or not.
The way to check if a child exists is to first set a path to the child before the child your looking for. In the situation from the question the child to look for is "levelTwo" and the child before that is uid:
//there is no data at levelTwo so there's nothing to observe
let levelTwoRef = dbRef.child("players").child("uid").child("levelTwo")
Assuming you know the uid ref definitely exists set a constant for the uid ref instead of the levelTwo ref
// uid path definitely exists
let uidRef = dbRef.child("players").child("uid")
Run .value on the uid ref and inside the callback check to see if the levelTwo ref exists:
uidRef?.observeSingleEvent(of: .value, with: {
(snapshot) in
if snapshot.hasChild("levelTwo"){
// true -levelTwo ref Does exist so do something
}else{
// false -levelTwo ref DOESN'T exist so do something else
}
}
You are running that inside observeEventType with the type .ChildAdded, which would return the snapshot of each new path that is created. If you just need to retrieve the value once you should use observeSingleEventOfType (link), with .Value as the event type.