We have now released 15 videos in our “Modern Persistence” series. Along the way we have built a large, complex app that is based off the Reminders app from Apple. We have multiple SQLite tables representing the various domain models of our app. There are relationships between those models, such as one-to-many relationships and many-to-many relationships. We use a type-safe and schema-safe query builder to write very complex queries to pull data from the database and populate our features’ views. And each feature automatically subscribes to the database so that any changes to the data will cause the view to automatically re-render.
And we really do think that all of the techniques and tools we have shown off so far do bring one closer to having “Modern Persistence” in their apps. But there is one huge topic that we have yet to touch in this series, and some might say it is a lie to call any of this “Modern Persistence” without discussing this topic, and that’s synchronization.
Modern apps almost always need the ability to have the user’s data distributed across all of their devices. If you are working on a reminders app, it is almost certainly a requirement that the reminders that a user creates on their phone is also synchronized to their iPad.
But distributing the schema of your database across many devices comes with many significant problems that you have to deal with. For just a taste of these problems, consider the following:
What happens when the user edits a record on each of their devices before there has been time to synchronize changes? How are we supposed to deal with record conflicts?
What happens when a user’s iPhone and iPad are running different versions of the app with different schemas? Special care needs to be taken so that records created with the older device can be synced to the new device, and so that records created by the new device do not lose data when synced to the old device.
SQLite has an important data integrity tool called “foreign key constraints.” This makes it so that if table B depends on table A, then you are not allowed to construct rows of B unless it has a corresponding A in the database. But what happens if during synchronization you receive child records from another device before you receive the parent record? How are we supposed to handle that in a world of foreign key constraints?
And if that already sounds complicated, let me tell you…that ain’t nothing.
There is a laundry list of synchronization nuances that one has to think about when distributing their schema across multiple devices. And luckily for everyone watching this episode right now, we spent the last 5 months and many hundreds of hours building a tool that allows you to synchronize your SQLite databases across multiple devices that automatically handles these edge cases for you.
It’s called SQLiteData, and the 1.0 of the library was released just a few weeks ago. It supersedes the SharingGRDB library we released many months ago, and we are going to use it now to add synchronization to the reminders app we have been building during our “Modern Persistence” series.
And this is going to be really informative for everyone because this will give us an opportunity to update an existing app to work with synchronization rather than starting a brand new app from scratch, which can be a little easier.
So, let’s get started by showing the steps we need to take in order to make our current SQLite schema more friendly to synchronization.