Intro
It wasn’t hard, but I wouldn’t say it’s plug-and-go. It took me a few weeks to make the foundation running correctly, as the major difficult being lacking examples and tutorials. Comparing to Powersync + Supabase or just use Firebase, this definitely requires some set up.
Implementation
There was an official blog post with git exmaple about how to achieve Appwrite + RxDB. However, when I tried it, it was kind of buggy, and it doesn’t do real-time sync. So, I ended up building my own from scratch by reading both Appwrite and RxDB documentations.
Major Hurdles
State management handling with RxDB
I am using LegendState, there are occasions where I try to .set() the instance of my rxdb collection because it causes a loop in my view. So, I have to make sure I use .get() for the rxdb collection, and use LegendState to read and monitor updates. On the other hand, in my viewModel, I have all the functions that manipulate the rxdb directly. The Appwrite replica modification happens in the viewModel functions.
Realtime sync updaing view in loop
Related to the last point. I’m not familiar with LegendState, but I’m getting better now. I added debounce to resolve the issue.
Sync delay messes up local state
Because RxDB Appwrite replication can only perform one write at a time, whenever I performed multiple actions in a short amount of time, the local states have a racing issue. It is because I have subscribed the collection in real-time, yet the data locally changed and received isn’t in sync. Therefore, I created an updatedAt to handle the conflicts.
Deleted fields
You can set deleted field for the replication. It is instructed that to perform deletion using .remove(). However, this delete method would result in no “undo delete”. It is because RxDB hides whatever is soft deleted via its .remove() method, the client cannot access the item just by checking !deleted. There’s a get/findone method for just retrieving a deleted file, kind of troublesome, but I get that rxdb needs its own soft delete mechanism. Therefore, I added an extra deleted field for myself. So, in totoal, there are 3 deleted fields - rxDeleted, isDeleted, deleted(Appwrite default).
Now, when a user deleted an item, I only toggle isDeleted, so it can go to the bin for recovering. On client side, set up a 30 days if isDeleted check to perform rxdb softdelete that toggle rxDeleted. Then, on serverside, perform another check say 30 days of rxDeleted, really delete them.
Soft delete via rxDeleted is necessary for RxDB to sync devices deleted field. But, I also want the user can easily recover the item, so I added isDeleted on top of that.
Conclude
It wasn’t too easy for me, it took me weeks to solve all these, as I started with SwiftUI where Apple CoreData took care every syncing procedures for me. This is the first I actually need to do more for just syncing. But, I’m glad I did it.
Now, I can work on the next big thing!