I have a application where I get values from a reference. The issue I have now is at times the reference might not the present and if the refrence is not present, the user is just suppose to be in idle state. How ever, I do not know how to achieve this task as the particular refrence could be absent from the firebase.
Below is my code for checking the ref data
func refreshActiveTrip() -> Observable<Trip> {
guard let trip = getCurrentTrip() else {
return Observable.error(RxError.noElements)
}
tripRef = Database.database().reference(forTransportChampionId: (getChampion()?.id)!, tripId: trip.id!)
return Observable<Trip>.create({ (observer) -> Disposable in
let disposable = Disposables.create {
self.tripRef?.removeAllObservers()
}
self.tripRef?.observe(.value, with: { (snapshot) in
if let data = snapshot.value as? [String: AnyObject] {
let trip = Trip(dictionary: data as NSDictionary)
self.saveCurrentTrip(trip)
if !disposable.isDisposed {
observer.onNext(trip)
}
}
})
return disposable
})
}
tripRef could be null i.e it does not exsit, how can i check this and an idle value
How about using the exists()
FIRDatabase.database().reference()
ref.child("the_path").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
print("It exists!!")
}else{
print("Nope, doesn't exist!")
}
})
Firebase Doc Reference
Related
I am wanting to capture all the values in my childByAutoId in firebase. Essentially, it stores all the items that a person has shortlisted. However, I do not seem to be capturing this, and I assume it is because I am not calling the snapshot correctly to factor the auto id's.
Database:
userID
-> Favourited
-> Auto Id
-> itemName: x
-> Auto Id
-> itemName: x
-> Auto Id
-> itemName: x
Code:
func retrieveItems() {
guard let userId = Auth.auth().currentUser?.uid else { return }
let ref = Database.database().reference().child("users/\(userId)/Favourited")
ref.observe(.value, with: { (snapshot) in
if snapshot.childrenCount>0 {
self.favUsers.removeAll()
for likes in snapshot.children.allObjects as! [DataSnapshot] {
let likesObject = likes.value as? [String: AnyObject]
let itemName = likesObject!["itemName"]
let likesList = Names(id: likes.key, itemName: itemName as! String?)
self.favUsers.append(likesList)
}
} else {
print("not yet")
}
})
self.favList.reloadData()
}
Could someone have a look and let me know what I may be doing wrong? Thank you!
This happens because Firebase loads data asynchronously, and right now you're calling reloadData before the self.favUsers.append(likesList) has ever run.
The call to reloadData needs to be inside the close/completion handler that is called when the data comes back from Firebase:
ref.observe(.value, with: { (snapshot) in
if snapshot.childrenCount>0 {
self.favUsers.removeAll()
for likes in snapshot.children.allObjects as! [DataSnapshot] {
let likesObject = likes.value as? [String: AnyObject]
let itemName = likesObject!["itemName"]
let likesList = Names(id: likes.key, itemName: itemName as! String?)
self.favUsers.append(likesList)
}
self.favList.reloadData() // 👈 Move this here
} else {
print("not yet")
}
})
I also recommend checking out some of these answers asynchronous data loading in Firebase.
I have created an application where users can search for posts (user-generated) within the app. Recently it has gotten to the point where I can't load all the users at once without it crashing.
The old function would simply load the entire post node and then filter as shown below. Is it possible to replicate something similar to this with a lot more data?
self.posts.filter { (post) -> Bool in
return
post.title.lowercased().contains(searchText.lowercased())
}
I have created a new function from reading the firebase documentation. Which is intended to handle more data.
New function
var users = [User]()
fileprivate func fetchUsers(username: String) {
let ref = Database.database().reference().child("users").queryOrdered(byChild: "username").queryStarting(atValue: username).queryEnding(atValue: "\(username)\\uf8ff")
ref.observeSingleEvent(of: .value) { (snapshot) in
guard let dictionaries = snapshot.value as? [String: Any]
else { return }
dictionaries.forEach({ (key, value) in
if key == Auth.auth().currentUser?.uid {
return
}
guard let userDictionary = value as? [String: Any] else { return }
let user = User(uid: key, dictionary: userDictionary)
self.users.append(user)
})
self.users.sort(by: { (user1, user2) -> Bool in
return user1.username.compare(user2.username) == .orderedAscending
})
self.collectionView?.reloadData()
}
}
Firebase database
"users" : {
"0Udc81ELBJP7UpYiESqPj5gLDx9A" : {
"username" : "First_User"
},
"13ThJZlpkgUCcZ4zaRzzaAHFjat2" : {
"username" : "Test_User"
}
},
This function, however, is lethargic and doesn't autocomplete as it uses to (similar to how Instagram does theirs). You need to type full words into the search bar before anything comes up.
I assume there is a way to have an autocompleted search bar as it's stock standard on most apps these days. Any help fixing this is appreciated.
My guess is that you're calling fetchUsers each time the user changes the text in the search box.
In that case you'll need to clear the previous contents from the list of users whenever fetchUsers runs. So something like:
func fetchUsers(username: String) {
let ref = Database.database().reference().child("users").queryOrdered(byChild: "username").queryStarting(atValue: username).queryEnding(atValue: "\(username)\\uf8ff")
ref.observeSingleEvent(of: .value) { (snapshot) in
guard let dictionaries = snapshot.value as? [String: Any]
else { return }
self.users.removeAll() // clear all previous results
dictionaries.forEach({ (key, value) in
if key == Auth.auth().currentUser?.uid {
return
}
guard let userDictionary = value as? [String: Any] else { return }
let user = User(uid: key, dictionary: userDictionary)
self.users.append(user)
})
self.users.sort(by: { (user1, user2) -> Bool in
return user1.username.compare(user2.username) == .orderedAscending
})
self.collectionView?.reloadData()
}
}
I have two observers, the second observer is dependent on the first observers value. I can't seem to get the first observer to work, I am not getting any errors on Xcode. The first function has to check the Users profile for information and then use that information to search for different information in the database. Here is my code:
func loadposts() {
ref = Database.database().reference()
let trace = Performance.startTrace(name: "test trace")
trace?.incrementCounter(named:"retry")
let userID = Auth.auth().currentUser?.uid
print(userID!)
ref.child("Users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let one1 = value?["Coupon Book"] as? String ?? ""
print("one1: \(one1)")
self.bogus.set(one1, forKey: "bogus")
}) { (error) in
print(error.localizedDescription)
}
delay(0.1) {
print("bogus: \(self.bogus.string(forKey: "bogus"))")
Database.database().reference().child("Coupons").child(self.bogus.string(forKey: "bogus")!).observe(.childAdded) { (Snapshot : DataSnapshot) in
if let dict = Snapshot.value as? [String: Any] {
let captiontext = dict["company name"] as! String
let offerx = dict["offer count"] as! String
let logocomp = dict["logo"] as! String
let actchild = dict["childx"] as! String
let post = Post(captiontext: captiontext, PhotUrlString: actchild, offertext: offerx, actualphoto: logocomp)
self.posts.append(post)
self.tableview.reloadData()
print(self.posts)
}
}
}
trace?.stop()
}
Any help is appreciated.
self.bogus.string(forKey: "bogus"))" is nil because observeSingleEvent is an async method, so to get the required results you need to call the second observer inside the first observer or you can use the completion handler
You can use the completionHandler like this:
guard let uid = Auth.auth().currentUser?.uid else {
return
}
func firstObserverMethod(completionCallback: #escaping () -> Void) {
ref.child("Users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
if let value = snapshot.value as? [String: Any] {
let one1 = value["Coupon Book"] as? String
print("one1: \(one1)")
self.bogus.set(one1, forKey: "bogus")
completionCallback()
}
}) { (error) in
print(error.localizedDescription)
}
}
Now using the above method:
firstObserverMethod {
print("bogus: \(self.bogus.string(forKey: "bogus"))")
guard let bogusString = self.bogus.string(forKey: "bogus") else {
print("bogus is not set properly")
return
}
Database.database().reference().child("Coupons").child(bogusString).observe(.childAdded) { (Snapshot : DataSnapshot) in
if let dict = Snapshot.value as? [String: Any] {
let captiontext = dict["company name"] ?? ""
let offerx = dict["offer count"] ?? ""
let logocomp = dict["logo"] ?? ""
let actchild = dict["childx"] ?? ""
let post = Post(captiontext: captiontext, PhotUrlString: actchild, offertext: offerx, actualphoto: logocomp)
self.posts.append(post)
DispatchQueue.main.async {
self.tableview.reloadData()
}
print(self.posts)
}
}
}
Note: You should use optional binding to get the values from optional
Since you are using the result of the 1st observer in the reference of your 2nd observer, it's a very bad idea to add the 2nd observer right below the first observer. And adding a delay won't be a viable solution : these two calls are asynchronous, which means that the reason why you are not getting might very likely be because the 2nd observer is triggered even before the 1st has returned any data.
The solution here, would be using a completion handler, or you could just incorporate your 2nd observer inside the completion block of the 1st, to be make sure that the proper order (1st observer -> 2nd observer) will always be respected.
It would look somehow like this:
func loadposts() {
// ...
// 1st Observer here
ref.child("Users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get your value here
guard let one1 = snapshot.childSnapshot(forPath: "Coupon Book").value as? String else { return }
// 2nd Observer here. Now you can use one1 safely:
Database.database().reference().child("Coupons").child(one1).observe(.childAdded) { (Snapshot : DataSnapshot) in
// ...
}
})
}
Now, a couple of things that you could also improve in your code, while not directly related to the question:
I would suggest you to make use of guard statements instead force-unwrapping, which may end up in crashing your app at some point.
For example, you could check whether your current user exist or not like so:
guard let currentUserID = Auth.auth().currentUser?.uid else {
return
}
// Now you can use safely currentUserID
Also, when you try to get the data out of the snapshot, it's not a good idea either, to use force-casting. You would better write it in this way:
yourRef.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
guard let text = child.childSnapshot(forPath: "text").value as? String, let somethingElse = child.childSnapshot(forPath: "otherValue").value as? NSNumber else {
return
}
// And so on, depending of course on what you have in your database.
}
I'm trying to run a function to be able to retrieve data from the realtime database with Firebase, however whenever I run the function; the observerSingleEvent part of my function will not run, I have tried putting a print statement within and it is not being run nor is the fields being read to the variable, any help would be beneficial.
func checkIfNewDay() -> Bool {
print(self.currDate)
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
let userID = FIRAuth.auth()?.currentUser?.uid
print("outside function")
ref.child("user").child(userID!).child("dates").observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
print("inside function")
let value = snapshot.value as? NSDictionary
print("just to make sure its going inside the function. Delete after")
self.lastDate = value?["lastSaveDate"] as? String ?? "Date Invalid"
self.newLastDate = String(self.lastDate)
if self.newLastDate != "Date Invalid" {
print(self.lastDate)
} else {
print("Error, date not able to be recieved from the database")
self.catchGetDateError = true
self.saveCurrentDate()
}
})
if (!self.catchGetDateError) {
print(self.newLastDate, "newLastDate")
print(self.currentDate, "currentDate")
if (self.newLastDate == self.currentDate) {
print("Day has not moved on.")
return false
} else {
print("Day has moved on!")
return true
}
}
return true
}
I apologise for the really long function - was quite a weird one to write.
From comments I think I have understood, what do you want.
For getting this results like sync, you need to implement escaping. Like this:
func checkIfNewDay(completion: #escaping (_ isNew: Bool) -> Void) {
print(self.currDate)
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
let userID = FIRAuth.auth()?.currentUser?.uid
print("outside function")
ref.child("user").child(userID!).child("dates").observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
print("inside function")
let value = snapshot.value as? NSDictionary
print("just to make sure its going inside the function. Delete after")
self.lastDate = value?["lastSaveDate"] as? String ?? "Date Invalid"
self.newLastDate = String(self.lastDate)
if self.newLastDate != "Date Invalid" {
print(self.lastDate)
if (self.newLastDate == self.currentDate) {
print("Day has not moved on.")
completion(false)
} else {
print("Day has moved on!")
completion(true)
}
} else {
print("Error, date not able to be recieved from the database")
self.catchGetDateError = true
self.saveCurrentDate()
completion(false)
}
})
}
So, now you can use your func:
checkIfNewDay(completion: { isNew in
// do whatever you want. isNew will have info, that you need.
})
You should have it, because .observe functions work async. You should understand the idea.
Hope it helps.
I have this code below in order to retrieve a list of restaurants and their data. However, it's not storing the data, and every time I try to return the array it returns nil. But if I print it, prints the data. Any suggestions?
func getRestaurants()-> Array<Restaurant>{
var baruri = [Restaurant]()
dataBaseRef.child("AthensRestaurants/Restaurants").observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
print(snap)
if let restaurantData = snap.value as? Dictionary<String, AnyObject> {
let restaurant = Restaurant(restaurantData: restaurantData)
baruri.append(restaurant)
print(baruri)
}
}
}
})
return baruri
}
The firebase observe is an asynchronous callback function, so it will run after it is finished. In other words, your return baruri will always runs before the value got back. You can use completion handler to get the value you want.
var restaurants = [Restaurant]()
func getRestaurants(completion: #escaping (Array<Restaurant>) -> Void){
var baruri = [Restaurant]()
dataBaseRef.child("AthensRestaurants/Restaurants").observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
print(snap)
if let restaurantData = snap.value as? Dictionary<String, AnyObject> {
let restaurant = Restaurant(restaurantData: restaurantData)
baruri.append(restaurant)
print(baruri)
completion(baruri)
}
}
}
})
}
// Call this function with call back
getRestaurants { restaurants in
self.restaurants = restaurants
}