Originally published on my blog MullinStack
How does Firestore’s offline persistence work? 🔥
One of the recent React Native projects I worked on uses the power of Cloud Firestore. The core idea of the app is to allow the user to perform online and offline tasks and to have a transparent sync process for the user.
That is the exact power that Cloud Firestore provides, yet there are a few gotchas and findings I would like to mention about working with the offline mode. They might save you time and headaches.
Let’s suppose that Blarz (the user) is using MyFavouritesOfflineApp
, which handles his favorite movies and books. So we have two main Firebase/Firestore collections: books and movies.
Blarz logs in, and once he is on the home page, the app will start fetching all his favorite books and movies.
Reading actions
Basically, Blarz is downloading all sorts of documents while he is interacting with the app.
The download of those documents occurs either through queries or individual document fetches. Once this happens, all those documents stay cached on Blarz’s phone.
Note: Bear in mind that if you haven’t programmatically set to retrieve those collections or if the user has not interacted with any query or document, the data won’t still be available in offline mode either.
Writing actions
Let’s continue with the prior scenario to have a better understanding.
Suppose that Blarz wants to update one of his favorite movies. This is a request to make a change to the movie collection and the document associated with the movie that Blarz wants to update.
Remember that the data stays on the cache, so you will see the change immediately even when the data hasn’t gone to the server.
Gotchas When Working in Offline Mode
I have to admit it: I only discovered these gotchas after debugging for a while after starting to work on a reported bug on Jira. Certainly, there wasn’t enough offline testing, so I didn’t realize all of that.
While using Firestore’s offline mode, we need to avoid the usage of await on certain things such as creating or updating things on the Firebase.
The promise generated by the await
expression will not be complete until the document write has been successful on the server. This will block your UI even when the changes are made in the cache.
There are two ways to handle this.
- Avoid await and use callbacks or promises instead (recommended)
If you want to make sure that a writing server has happened, it is fine to use either callbacks or promises. But you don’t need to block the thread by using the await expression.
So instead of this:
const user = {
id:1,
name:blarz
};
const userResponse = await FirebaseService.Collection.User().add(user);
Change it for the usage of promises. Something like the following:
const user = {
id:1,
name:blarz
};
const userRef = FirebaseService.Collection.User().add(user)
.then(docRef => {
console.log(`The user with id ${docRef.id} was added succcesfully`);
}).catch(error=>console.log('There was an error adding a user', error))
By doing that, you will unlock your UI and the app will work just fine in offline mode.
- Check connection status
This might not be a fancy solution, but if you still want to have the sugar syntax of the await expression, you can just check the connection status and depend on it to decide what to do.
Get IDs Before They Were Added
This was another battle. I needed to return the user document.
Due to the issue with the await expression, I needed to find a way to return a document with its ID before it was added.
I had something like this:
async addUser(user: User):Promise<User> {
const { id, ...data } = user;
const result = await FirebaseService.Collection.User().add(data);
user.id = result.id;
return user;
}
After I removed the await
expression, the code looks like the following:
async addUser(user: User):Promise<User> {
const { id, ...data } = user;
const userRef = FirebaseService.Collection.User().doc();
user.id = userRef.id;
userRef
.set(data)
.then(() => console.log(`The user was created successfully`))
.catch((error) =>
console.log('There was an error adding the user', error)
);
return user;
}
This code snippet is basically creating a user reference — a document reference. Then you can get the user’s ID without even creating it on Cloud Firestore. Remember that we are still working in offline mode.
The last code lines add the user using a promise. We are not waiting for the server’s response, so we return the new user we have just created.
That’s all.
Final Thoughts
Cloud Firestore provides us with the power of working both online and offline in a fancy way. We don’t need to worry about anything when it comes to working in offline mode because for iOS and Android, offline persistence is enabled by default.
With that said, there a few gotchas you need to keep in mind while working offline. One of the most important is avoiding the use of await expressions.
This is important because if you don’t, the UI will be locked since you will be awaiting the response even when those changes are done and on the cache already.
The solution for that is to use callbacks or promises. 🔥
If this post turned out helpful share it to reach more devs. Also feel free to reach me on my blog and Medium
Top comments (0)