observerSingleEvent function not being ran with Firebase - swift

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.

Related

SwiftUI Firebase Firestore Query Function

I have an array of SKU numbers that I'm returning from Google Firestore "[428024, 4298212]".
I have written a function assigning the array of SKU's to a variable, but I am lost as to how to return that variable from the function.
let db = Firestore.firestore()
func getItems() -> [Int] {
let userID = Auth.auth().currentUser?.uid ?? "nil"
if (session.session != nil) {
self.data.removeAll()
db.collection("users").document(userID).getDocument { (document, error) in
if let document = document, document.exists {
let itemID = document.get("items") as! Array<Int>
print(itemID as Any)
// Prints "[428024, 4298212]"
return itemID
} else {
print("Document does not exist")
}
}
}
}
I'm getting the error "Unexpected non-void return value in void function, though I can see that the array of SKU's are being returned when it runs the "print(itemID as Any)" line.
Is there any mistake in how I have the function written?
Querying the document through Firestore is written using a completion handler and trying to return any value from within this handler to your original function will deliver this error. Instead, you'll need to adjust your original function getItems() to account for this as such:
let db = Firestore.firestore()
#State private var itemIDs: [Int] = []
func getItems(completion: #escaping (_ itemIDs: [Int]?) -> ()) {
let userID = Auth.auth().currentUser?.uid ?? "nil"
if (session.session != nil) {
self.data.removeAll()
db.collection("users").document(userID).getDocument { (document, error) in
if let document = document, document.exists {
let itemIDs = document.get("items") as! Array<Int>
completion(itemIDs) // call completion handler to return value
} else {
print("Document does not exist")
}
}
}
}
func callingYourFunction() {
self.getItems() { itemIDs in
if let ids = itemIDs {
// itemIDs exists -> do whatever else you originally intended to do with the ids
self.itemIDs = ids
}
}
}
Take a look at here if you want to learn more about closures and completion handlers! Hopefully this helps.

Else on If Else statement won't get triggered, can't understand why

I have this block of code:
func fetchFriends() {
if let window = UIApplication.shared.keyWindow {
guard let userId = Auth.auth().currentUser?.uid else { return }
DispatchQueue.main.async {
FirestoreService.shared.fetchFriendList(userId) { (fetchedFriends) in
//// WONT GET HERE ////
if fetchedFriends != nil {
self.fetchedFriends = fetchedFriends! // Can force unwrap here because we already know that fetchedFriends in not nil.
self.friendsTable.reloadData()
}else {
self.fetchedFriends = []
self.friendsTable.reloadData()
}
}
}
}
}
This block of code is using this function:
func fetchFriendList(_ id: String, completion: #escaping([Friend]?)->()) {
var fetchedFriends: [Friend] = []
db.collection(USERS_COLLECTION).document(id).getDocument { (doc, err) in
if err == nil && doc != nil {
guard let results = doc?.data()?[USER_FOLLOWING] as? [String: Any] else { return }
for result in results { // Getting the data in firebase
if let resultValue = result.value as? [String: Any] { // Getting only the value of the MAP data, we do not need the key.
//Getting the fields from the result
guard let id = resultValue[FRIEND_ID] as? String else { return }
guard let profilePic = resultValue[FRIEND_PROFILE_PIC] as? String else { return }
guard let username = resultValue[FRIEND_NAME] as? String else { return }
guard let email = resultValue[FRIEND_MAIL] as? String else { return }
//Creating a new Friend object from the fields
let friend = Friend(id: id, profilePicture: profilePic, username: username, email: email)
fetchedFriends.append(friend)
}
completion(fetchedFriends)
}
}else {
print(err!.localizedDescription)
completion(fetchedFriends)
}
}
}
Whats happening here, is that I'm going into a user's document, getting it's 'Friends' from a Map I have in the document, creating a Friend Array and sending it in the completion to the first function.
In the first function, I'm checking if what I got is nil, if not, I'm assigning it to an array, else, if it is nil, I want the array to be empty.
The purpose here is to show the "Friends" in the tableView if the user has any.
My problem is this situation:
For start, the list of friends is empty, adding a friend and viewing the list, the friend I just added is showing, which is good. the problem is, when I'm removing this friend from the list (and it is deleted in the Database in Firestore), showing the list again does not deletes him from the list and still showing it.
It seems that after removing a friend from the "following" section, and showing the list again, after FirestoreService.shared... it just returns and won't get to the "Won't get here" line.
The FetchFriends() function does gets called everytime I'm opening the FriendsList.
This is a picture of the list I'm referring to, this demouser is removed from the friends list but still showing up.
EDIT: Just noticed that when I have more than one user on the list, it does gets deleted and it works as I want. When I have just one user (or just one left on the list) it won't delete it.
fetchFriendList never calls the callback with a nil value:
var fetchedFriends: [Friend] = []
Therefore your else branch is unnecessary and the completion handler could be #escaping ([Friend]) -> Void without optionals.
By the way, there is also a situation when your method does not call completion at all:
guard let results = doc?.data()?[USER_FOLLOWING] as? [String: Any] else { return }
In general, there are many unsafe places. For example, when err is nil and doc is nil, then your else will crash unwraping err!.
A better alternative:
guard err == nil, let doc = doc else {
print(err?.localizedDescription)
completion([])
return
}
let results = (doc.data()?[USER_FOLLOWING] as? [String: Any]) ?? [:]
let fetchedFriends = results.compactMap { result in
guard
let resultValue = result.value as? [String: Any],
let id = resultValue[FRIEND_ID] as? String,
let profilePic = resultValue[FRIEND_PROFILE_PIC] as? String,
let username = resultValue[FRIEND_NAME] as? String,
let email = resultValue[FRIEND_MAIL] as? String
else { return nil }
return Friend(id: id, profilePicture: profilePic, username: username, email: email)
}
completion(fetchedFriends)

Checking a null refrence Firebase swift

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

Swift, FIrebase - Can't remove observers by removeAllObservers

I've been trying to remove observers for a while but somehow I have never succeeded. I've checked other posts here but I can’t find the reason why.
Here are my codes:
var ref: DatabaseReference!
let timeStamp: Double = NSDate().timeIntervalSince1970
override func viewDidLoad() {
super.viewDidLoad()
self.ref = Database.database().reference()
guard let userId = Auth.auth().currentUser?.uid else { return }
self.ref.child("users").child(userId).child("contactList").observe(.value, with: { (snapshot) in
guard let children = snapshot.value as? [String: Any] else { return }
for child in children {
guard let dictionary = child.value as? [String: Any] else { return }
guard let timeStamp = dictionary["timeStamp"] as? String else { return }
guard let timeStampDouble = Double(timeStamp) else { return }
if timeStampDouble > self.timeStamp {
self.navigationController?.pushViewController(HomeController(), animated: true)
}
}
}) { (err) in
print("Failed to fetch user: ", err)
}
}
deinit {
self.ref.child("users").removeAllObservers()
}
I would appreciate any advise!
Calling removeAllObservers on a node, removes all observers from that node only. It doesn't remove observers from child nodes.
So your code:
self.ref.child("users").removeAllObservers()
This only removes the observers from users. It does not remove the observers from users/$userId/contactList. To remove the latter, you will have to call removeAllObservers on that specific node, which means you'll need to track what nodes you have attached observers to.

Can someone tell me why I keep getting error "unexpected non-void return value in void function"

I get an error when trying to return a true or false Bool value... Can someone help ? I was trying to check if user exists in Firebase database.
func checkIfUserExists(email: String) -> Bool{
FIRDatabase.database().reference().child("users").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]{
switch email {
case dictionary["email"] as! String!:
return true
break
default:
return false
break
}
}
}, withCancel: nil)
}
Your return is inside a closure, not your function. What comes to mind is you could return a closure in your function like this
func checkIfUserExists(email: String, completion:(success:Bool) -> ())
And then once your FIRDatabase closure ends instead of return true or false you
completion(success:true) // or false
And use it like this.
checkIfUserExists("email#mail.com") { (success) in
if success
{
// Email found
}
else{
}
}
This link could be helpful
change it this way
func checkIfUserExists(email: String, results:(exists:Bool)->Void){
FIRDatabase.database().reference().child("users").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]{
switch email {
case dictionary["email"] as! String!:
results(true)
break
default:
results(false)
break
}
}
}, withCancel: nil)
}
The function can be called like this
checkIfUserExists(email: "some email", results:{ exists
if exists {
//do something
}
else{
// user do not exist... do something
}
})
#ohr answer should be the acepted one, this is only a clarification