I have an iOS App that uses Firebase to store user information. I cannot seem to get the observe() block ran in anyway. The closure is never executed.
When I debug the code, I see that the observe() block is skipped hence I cannot validate the user information.
From the Firebase console, I can verify a table with a name equal to hashCode exists and the table in fact has a checked field.
I use Xcode 9.3 and Swift 4.1
What am I missing here?
Thanks
override func viewDidLoad() {
super.viewDidLoad()
// UI changes
self.view.backgroundColor = appBgColour
// check if the user has registered before
if let asciiCode = defaults.string(forKey: "asciiCode") {
let hashCode = userCode.myHash() // myHash() is an extension to String
dbReference = Database.database().reference(withPath: databaseReferenceName)
dbReference.child("\(hashCode)/checked").observe(.value) { (snapshot) in
if snapshot.exists() {
// a table with this user's Hash'ed code exists
self.isRegistrationNeeded = false
} else {
// user needs to register with a new list
self.isRegistrationNeeded = true
}
}
}
}
Related
I hold all the usernames inside a separate node to run searches on when users search for a username. I deleted a name from the node eg. pizzaMan. The problem is even though it deletes, if I run a search on the deleted name from within my app it says it's available but when I physically look inside the database it shows it's still physically there (meaning it shouldn't be available). How is that possible?
#IBAction func deleteUsernameButtonTapped(_ sender: UIButton) {
// the user's username is pizzaMan
let username = usernamesRef?.child("pizzaMan")
userName?.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
let key = snapshot.key
username?.child(key).removeValue()
print("username: \(key) has been deleted\n")
}
})
}
The username pizzaMan has been deleted but physically inside the database it shows it's still there.
let checkUsernameTextField = UITextField()
checkUsernameTextField.addTarget(self, action: #selector(handleSearchForUsername), for: .editingChanged)
#objc func handleSearchForUsername() {
// now search for pizzaMan inside a textField
usernamesRef?.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
print("name is NOT avail")
} else {
print("name IS available") // this prints when searching for pizzaMan even though it's inside the db??
}
})
}
If I try to obtain it lets me and just writes over the old value with whatever the new value is but it still shouldn't show up inside the database once removed.
Your problem lies inside your delete func if I am understanding your issue.
Let me reiterate what I think you are saying. You want pizzaMan's node to be completely removed, yes? Try this:
#IBAction func deleteUsernameButtonTapped(_ sender: UIButton) {
// the user's username is pizzaMan
if let username = usernamesRef as? DatabaseReference{
username.child("usernames/pizzaMan").removeValue()
}
else{
print("Errors")
}
}
Is that what you intend to do?
I honestly I have tried to figure out when to call ref.removeAllObservers or ref.removeObservers, but I'm confused. I feed I'm doing something wrong here.
var noMoreDuplicators = [String]()
func pull () {
if let myIdi = FIRAuth.auth()?.currentUser?.uid {
let ref = FIRDatabase.database().reference()
ref.child("users").queryOrderedByKey().observe(.value, with: { snapshot in
if let userers = snapshot.value as? [String : AnyObject] {
for (_, velt) in userers {
let newUser = usera()
if let thierId = velt["uid"] as? String {
if thierId != myIdi {
if let userName = velt["Username"] as? String, let name = velt["Full Name"] as? String, let userIdent = velt["uid"] as? String {
newUser.name = name
newUser.username = userName
newUser.uid = userIdent
if self.noMoreDuplicators.contains(userIdent) {
print("user already added")
} else {
self.users.append(newUser)
self.noMoreDuplicators.append(userIdent)
}
}
}
}
}
self.tableViewSearchUser.reloadData()
}
})
ref.removeAllObservers()
}
}
Am I only supposed to call removeAllObservers when observing a single event, or...? And when should I call it, if call it at all?
From official documentation for observe(_:with:) :
This method is used to listen for data changes at a particular location. This is
the primary way to read data from the Firebase Database. Your block
will be triggered for the initial data and again whenever the data
changes.
Now since this method will be triggered everytime the data changes, so it depends on your usecase , if you want to observe the changes in the database as well, if not then again from the official documentation:
Use removeObserver(withHandle:) to stop receiving updates.
Now if you only want to observe the database once use observeSingleEvent(of:with:) , again from official documentation:
This is equivalent to observe:with:, except the block is
immediately canceled after the initial data is returned
Means that you wont need to call removeObserver(withHandle:) for this as it will be immediately canceled after the initial data is returned.
Now lastly , if you want to remove all observers , you can use this removeAllObserver but note that:
This method removes all observers at the current reference, but does
not remove any observers at child references. removeAllObservers must
be called again for each child reference where a listener was
established to remove the observers
Actually, you don't need to call removeAllObservers when you're observing a single event, because this observer get only called once and then immediately removed.
If you're using observe(.value) or observe(.childAdded), and others though, you would definitely need to remove all your observers before leaving the view to preserve your battery life and memory usage.
You would do that inside the viewDidDisappear or viewWillDisappear method, like so:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Remove your observers here:
yourRef.removeAllObservers()
}
Note: you could also use removeObserver(withHandle:) method.
I want to know if it is possible to delete an item in the firebase database through database rules or something server side. Right now I am using client side app to delete values from firebase after a timer is reached. I want to avoid using client side for deletion of the item as I do not want multiple users to try to delete the items from the database.
Second I want to know if there is way to make a unique observers query in firebase where I only get new data instead of querying all the old data. I have it setup with a map where as something is added it queries for that data and adds it to the map. However it is giving me all of the data from the database and causing multiple additions of annotations on my mapview. Now I can remove the annotations before they are added, but that is very hacky and not User friendly.
Code:
class FBService {
static let instance = FBService()
var firRef: FirDatabaseReference!
private init() {
self.fireRef = FIRDatabase.database().reference()
}
fun getMyAnnotations(location: CLLocation, completion: ([annotationLocation]? -> ())) {
let now = NSDate().timeIntervalSince1970
let locationRef = fireRef.child("annotations").queryOrderedByChild("timestamp").queryStartingAtValue(now)
// Before I was doing fireRef.Child("annotations").queryOrderedByChild("latitude")
locationRef.observeEventType(.Value) { (snapshot) in
completion(self.someMethod(snapshot))
}
}
func someMethod(snapshot: FIRDataSnapshot) {}
}
class MainVC: UIViewController {
override func viewDidLoad() {
FBService.instance.getMyAnnotations(userLocation) { (annotations) in
if let annotations = annotations {
// Before I was doing mapView.removeAnnotations(mapView.annotations), which removes annotations and re-plops them as they come back.
for anno in annotations {
let pin = CustomAnnotation(coordinate: CLLocationCoordinate2DMake(anno.location.latitude, anno.location.longitude), name: anno.name)
mapView.addAnnotation(anno)
}
}
}
}
}
In my app, as soon as it opens I check to see if the user is already authenticated in the viewdidload of the initial view. If they are already authenticated, I perform a segue to the main view. I'm even printing the uid to the log at this time and it's printing correctly.
override func viewDidLoad() {
super.viewDidLoad()
if ref.authData != nil {
let uid = ref.authData.uid
print(uid)
I then do the same later in the app to get some of the user's info when they click on their profile settings. I write the exact same code to fetch their uid, but this time the uid is returning nil and is crashing with the error
"fatal error: unexpectedly found nil while unwrapping an Optional value"
Is this a firebase or simulator issue?
Edit: This issue has only occurred twice. Otherwise, the code itself works as intended, which makes me wonder whether it is a firebase or simulator issue.
You want to use the observeAuthEventWithBlock method, which is a realtime authentication listener.
override func viewDidAppear() {
let ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com")
ref.observeAuthEventWithBlock({ authData in
if authData != nil {
// user authenticated
print(authData)
self.performSegueWithIdentifier("LoginToOtherView", sender: nil)
} else {
// No user is signed in
}
})
}
About the exact error you are encountering I am not sure, but a Swift-yer way of doing your code (and avoiding your error) would be to call:
if let uid = ref.authData.uid {
print(uid)
}
This code safely unwraps both authData and the UID.
I was having this problem and it took me hours. Then I realized that I'd just forgotten to do this:
ref = FIRDatabase.database().reference()
before
var ref: FIRDatabaseReference!
I have a problem removing a Firebase observer in my code. Here's a breakdown of the structure:
var ref = Firebase(url:"https://MY-APP.firebaseio.com/")
var handle = UInt?
override func viewDidLoad() {
handle = ref.observeEventType(.ChildChanged, withBlock: {
snapshot in
//Do something with the data
}
}
override func viewWillDisappear(animated: Bool) {
if handle != nil {
println("Removed the handle")
ref.removeObserverWithHandle(handle!)
}
}
Now when I leave the viewcontroller, I see that "Removed the handle" is printed, but when I return to the viewcontroller, my observer is called twice for each event. When I leave and return again, it's called three times. Etc. Why is the observer not being removed?
I do also call ref.setValue("some value") later in the code, could this have anything to do with it?
Thought I was having this bug but in reality I was trying to remove observers on the wrong reference.
ORIGINAL CODE:
let ref: FIRDatabaseReference = FIRDatabase.database().reference()
var childAddedHandles: [String:FIRDatabaseHandle] = [:]
func observeFeedbackForUser(userId: String) {
if childAddedHandles[userId] == nil { // Check if observer already exists
// NOTE: - Error is caused because I add .child(userId) to my reference and
// do not when I call to remove the observer.
childAddedHandles[userId] = ref.child(userId).observeEventType(.ChildAdded) {
[weak self] (snapshot: FIRDataSnapshot) in
if let post = snapshot.value as? [String:AnyObject],
let likes = post["likes"] as? Int where likes > 0 {
self?.receivedFeedback(snapshot.key, forUserId: userId)
}
}
}
}
func stopObservingUser(userId: String) {
// THIS DOES NOT WORK
guard let cah = childAddedHandles.removeValueForKey(userId) else {
print("Not observing user")
return
}
// Error! I did not add .child(userId) to my reference
ref.removeObserverWithHandle(cah)
}
FIXED CODE:
func stopObservingUser(userId: String) {
// THIS WORKS
guard let cah = childAddedHandles.removeValueForKey(userId) else {
print("Not observing user")
return
}
// Add .child(userId) here
ref.child(userId).removeObserverWithHandle(cah)
}
Given it's April 2015 and the bug is still around I'd propose a workaround for the issue:
keep a reference of the handles (let's say in a dictionary and before initiating a new observer for the same event type check if the observer is already there.
Having the handles around has very low footprint (based on some official comments :) ) so it will not hurt that much.
Observers must be removed on the same reference path they were put upon. And for the same number of times they were issued, or use ref.removeAllObservers() for each path.
Here's a trick I use, to keep it tidy:
var fbObserverRefs = [FIRDatabaseReference]() // keep track of where observers defined.
...then, put observers in viewDidLoad():
fbObserverRefs.append(ref.child("user/\(uid)"))
fbObserverRefs.last!.observe(.value, with: { snap in
// do the work...
})
...then, in viewWillDisappear(), take care of removing any issued observers:
// Only true when popped from the Nav Controller stack, ignoring pushes of
// controllers on top.
if isBeingDismissed || isMovingFromParentViewController {
fbObserverRefs.forEach({ $0.removeAllObservers() })
}