Firebase Query Error Handling - ios

I've got some functions to read data from Firebase, but sometimes I never get a response (or it's massively delayed). I read here that maybe Firebase can close the socket connection before data is received. It looks like someone had a similar issue here, but never posted a solution.
Here's a sample of my code for downloading user data from Firebase.
// loads the current user's information
static func loadUserDataWithCompletion(completion: (UserInfo) -> Void) {
let ref = FIRDatabase.database().reference()
print("loading current user data...")
let uid = (FIRAuth.auth()?.currentUser?.uid)!
ref.child("users").queryOrderedByKey().queryEqualToValue(uid).observeEventType(.ChildAdded, withBlock: { (snapshot) in
print("found user data!")
if let dictionary = snapshot.value as? [String:AnyObject] {
let info = userFromDict(dictionary)
// execute code slated for completion
completion(info)
}
})
}
Is there some way I can detect errors using observeEventType? Maybe then I'd at least get more information about why the issue is happening.

There can be three possible errors that you might face while observing your database :-
You haven't defined a proper path to your database observe query
You are parsing in a wrong or faulty manner
You don't have the permission to access that particular node at that particular time(Security Rules)
For the first two you have to take care of it yourself, But for the third condition you can use withCancel block:-
FIRDatabase.database().reference().child("users").queryOrderedByKey().queryEqual(toValue: "uid").observe(.childAdded, with: { (snapshot) in
//your code
}, withCancel: {(err) in
print(err) //The cancelBlock will be called if you will no longer receive new events due to no longer having permission.
})
As far as error handling if the user looses network connection while Writing, Your app remains responsive regardless of network latency or connectivity. See the Write data offline section of Firebase Docs

Related

firebase real time dB retrieving of specific data from multiple users in swift

so I'm working these days on a new project and I have a problem I can't solve, I hope someone can help me.
I'm working on an iOS app, I'm storing all user data on Firebase Real time dB.
My main goal is to get specific data from all users from particular positions,
For example:
Inside users, I have different UIDs of all the users in the dB.
In each one of them, there is a username, I would like to retrieve the username of each user.
In the future, I would like to store the location for each user under "Location". and then I would like to get all users that their location is "New-York".
I'll be glad to get some ideas on how to figure it out!
Thanks!
users
XLS37UqjasdfkKiB
username: "Donald"
ei8d4eYDafjQXyZ
username: "Barak"
etcj0lbSX5Oasfj
username: "Abdul"
rglmlG6Rasdgk5j
username: "Ron"
You can:
Load all JSON from /Users.
Loop over snapshot.children.
Then get the value of each child's username property.
These are also the most common navigation tactics when it comes to Firebase Realtime Database, so you'll apply these to pretty much any read operation.
Something like this:
Database.database().reference().child("isers").observe(.value, with: { (snapshot) in
if !snapshot.exists() {
print("No users found")
} else {
for case let userSnapshot as DataSnapshot in snapshot.children {
guard let dict = userSnapshot.value as? [String:Any] else {
print("Error")
return
}
let username = dict["username"] as? String
print(username)
}
}
})
Also see:
the documentation on reading data.
the documentation on listening for lists of data with a value event.
How to properly use queryOrderedByValue
Get Children of Children in Firebase Using Swift
And more from searching on [firebase-realtime-database][swift] children.

Is there a way to tell when a document has been synced with the database from the cache?

I am building an instant messaging app, akin to WhatsApp, and I need to display the send status of the message: Sending, Sent; sending being if it is the Firebase cache, due to being offline, and sent if the message is on the online database.
Is there a way to tell if a specific document has been uploaded to the database?
I currently have a live update listener listening on the messages collection in firestore that fires when either the cache or database updates. It can tell where the update came from but that returns all of the messages and doesn't specify any individual documents.
Here is a snippet of the completion block for the listener.
let source = snapshot.metadata.isFromCache ? "local cache" : "server"
print ("metadata: data fetched from DB: \(source)")
for diff in snapshot.documentChanges {
print("diffData: \(diff.type.rawValue)")
print("messageId: \(diff.document.documentID)")
switch diff.type {
case .added:
print("added")
case .removed:
print("removed")
case .modified:
print("modified")
}
}
var messages: [ChatEntry] = []
let deletedIds = [String]()
let permanentlyDeletedIds = [String]()
for document in snapshot.documents {
if document.exists {
let message = ChatEntry(fromSnapshot: document)
print("MessageId: \(message?.id ?? "null")")
print("messageText: \(message?.message ?? "Null")")
}
}
The callback runs when either the cache or DB updates, but if one updates, then the other updates then the document changes is emptied. meaning the I can't tell what happened.
Any advice would be appreciated, Thanks in advance.
You can find precisely that information in the SnapshotMetadata of a QuerySnapshot, which has an isFromCache property that:
Returns YES if the snapshot was created from cached data rather than guaranteed up-to-date server data.
You'll want to listen for metadata changes to be able to update the UI once the data is synchronized to the server.
My suggestion on this this would be to:
Set the status to "Sending" when you write a value to Firebase from the client - regardless of if you are online or offline (Firebase already handles this process for you so don't reinvent it).
Use your existing callback to notify you of new messages matching that one which you can then mark as "Sent" once you receive them (via the callback).
To make this easier you may even want to create a temporary array of ID's that you expect will be coming back from Firebase once written to the Firebase DB. This will allow you to easily validate that the message sent was the one you expected back.
Let me know if this helps.
Andrew

Issue Pulling UID from Firebase in Swift

I've seen many youtube videos and previously answered questions on this exact subject, but for some reason my code is not working.
I want to display the user's name that is stored, but I can't pull the UID from FireBase. At this point, the user should be logged in so I don't think it's an issue of forcing the UID. Any help would be great.
Code
Firebase Data
You can get your uid as:
var ref : DatabaseReference = Database.database().reference().child("klaw-unpw").child("users")
And in your viewdidload add this code:
ref.observe(.value, with: { (snapshot) in
print(snapshot.value)
}) { (error) in
print(error)
}
But it will be good if you will start your structure with "users" instead of "klaw-unpw".
Hope it will work for you.

Showing posts from those users you follow - swift & firebase

I have made a following system where users can follow each other but it is not very useful at the moment. I cannot seem to get my head around how to only show the posts of the users you follow - so I am here to ask you how you would do it.
Let me just show you how the database is set up:
As you can see I have a tap called Users - in there all the users ID are stored. Whenever a user wants to follow another user, the tap Following is made and under there the user that the current user pressed follow - his/her ID is store under Following. And then the current users ID is added to the user he/she follows under Followers. Pretty simple but complicated to explain :D I hope you get where I'm going.
So now I want only to show posts of the users I am following. How would you do that? Would you do it like this: When a user is making a post it is added to his own name under a tap called Posts AND added to all the users names under PostsToFollow? I just think it would take up a lot of space in the database? Isn't there a better way to do this?
Well here is a part of my code that is getting me all of the current users followers. How can I use that code to, whenever I am creating a new post, add that to their postsToFollow?
func startObersvingDB() {
FIRDatabase.database().reference().child("Users").child(IDgotten).child("Followers").observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
var newUpdates = [FollowersStruct]()
for update in snapshot.children {
print(update)
let updateObject = FollowersStruct(snapshot: update as! FIRDataSnapshot)
newUpdates.append(updateObject)
}
self.updates = newUpdates.reverse()
self.tableView.reloadData()
}) { (error: NSError) in
print(error.description)
}
}
To Frank:
I've made this for adding the entire post to all the users that are following my user. I cannot do that because I have a like and comment button on the posts so the likes on one users posts will not be the same as other users posts and that is why I think the idea of adding the key to the post in the followers walls is great. But how do I do that?
I have made this so far, trying to accomplish what you said :-) :
let feed = Sweet(content: update, addedByUser: name!, profilePhoto: uid, likesForPost: ["user id": false], date: timePosted, category: 1, workoutComment: "", workoutTime: "")
let feedRef = FIRDatabase.database().reference().child("feed-items").childByAutoId()
feedRef.setValue(feed.toAnyObject())
FIRDatabase.database().reference().child("Users").child(uid).child("Followers").observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
var newFollowers = [FollowersStruct]()
for updateFollowers in snapshot.children {
let updateObjectFollowers = FollowersStruct(snapshot: updateFollowers as! FIRDataSnapshot)
newFollowers.append(updateObjectFollowers)
}
self.followings = newFollowers
print(self.followings.count)
for i in 0..<self.followings.count {
let followers = self.followings[i]
let feedRefFollowers = FIRDatabase.database().reference().child("Users").child(followers.Id).child("Wall").childByAutoId()
feedRefFollowers.setValue(feed.toAnyObject())
}
}) { (error: NSError) in
print(error.description)
}
Writing the post under the user's own name and all his follower's names is a typical example of fanning out the data. This indeed duplicates the data, but the benefit is that your read operations will be much faster and have significantly simpler code.
If you don't want to duplicate the entire post, you can also just write the key of the new post under each follower's "wall". Then when you need to show a list of posts for a user, you read their "wall" and client-side join each post. This is the approach taken by in our classic Firefeed example app.
Either of these approaches is a form of denormalization: duplicating part of your data to get better read performance.
I highly recommend reading this article on NoSQL data modeling and checking out our video series on Firebase for SQL developers. Oh... and this blog post on client-side fan-out also won't hurt.

Firebase data retrieval sporadically not returning

I am building a project with Firebase that involves users. There is a known Firebase bug where even after a user has been deleted from the Firebase Authentication section, the user is still able to access the app and still has read/write permissions to the database. I believe it has something to do with the token being stored by Firebase on the user's device.
As a work around to this, I have implemented a "user checkup" where my app will check to see if the userID of the logged in user exists in my real-time database. This way all i have to do is delete the userID node from the real-time database and that will cause this checkup to fail and thus log the user out permanently.
The problem i am having is that the call to observeSingleEvent(of: .value...) is sporadically not returning. By this i mean that sometimes it returns immediately as is expected, however sometimes it just doesn't return at all. For example yesterday morning it wasn't working at all. Yesterday afternoon and night it worked fine. Now today it is no longer working again. Code below...
func checkDatabaseForFIRUser(withId: String, callback: #escaping (Bool)-> Void) {
let fireUsersRef = fireRootRef.child("all-users").child(withId)
fireUsersRef.observeSingleEvent(of: .value, with: {(snapshot)-> Void in
print("user check result -> \(snapshot)")
if snapshot.exists() {
print("SNAP EXISTS")
callback(true)
} else {
print("SNAP NOT EXISTS")
callback(false)
}
}, withCancel: {(error)-> Void in
print("CANCEL BLOCK ERROR = \(error) and localized description = \(error.localizedDescription)")
})
}
I have also noticed that during the times when I am unable to retrieve data I am also unable to write anything to the database, it is as if nothing at all works. My Firebase Rules have been set to "Public" so anyone can read/write. And I have a working internet connection on my device
EDIT: everything works fine on Simulator. Issue is only occurring on the actual device
EDIT #2: This issue seems to have subsided, I haven't noticed it for at least a few weeks now
I bet that by the time you are calling observeSingleEvent, you are log out, so probably that's why it is not returning anything.

Resources