We just finished a long series of episodes introducing everyone to what we like to call “Modern Persistence”. In this world persistence is done with SQLite, a fantastic technology that is probably the most widely deployed piece of software in history, and it has truly stood the test of time and aged wonderfully. Further, we leveraged some advanced querying aspects of SQL in order to efficiently fetch exactly the data we need from our database. This often required joining tables together to fetch data from multiple places at once, as well as performing aggregations so that we could perform many computations on our corpus of data at once.
And finally, we leveraged our modern tools built on top of SQLite in order to construct queries in a type-safe and schema-safe manner, and they allowed us to seamlessly fetch data from the database and observe changes to the database so that when the database changes, our views immediately update. And there are no restrictions to where we can use these tools. We can use them in a SwiftUI view, or an @Observable
model, and although we didn’t get a chance to show it off, we can even use these tools in a UIKit view controller.
So that series of episodes set the foundation of what we like to call “Modern Persistence”, but there is so, so much more we would like to cover on the topic. And so this week we are continuing the modern persistence series by discussing something known as “callbacks”. Callbacks are little hooks into the lifecycle of the database so that you can be notified or take action when something happens with your data. Perhaps the most canonical example of this is maintaining an “updated at” timestamp on your models. You want to have a timestamp that automatically updates each time the data is saved in the database. It would be nice if this could just happen automatically for you in the background so that you don’t have to remember to literally update the timestamp each time you are about save a record.
And callbacks have far-reaching applications beyond just “updated at” timestamps. You can use callbacks to perform an action when a table is emptied out, like if you wanted to make that a table always contains at least one row. You can also use callbacks to implement an undo management system so that each change to the database can be undone or redone. Callbacks also form the basis of how we are able to magically layer on CloudKit synchronization to an existing database.
So, we hope you can see that callbacks can be quite powerful, but what is the modern way to handle callbacks? Let’s start by really defining what a callback is, and then see what are some common approaches to callbacks before finally showing off what we think is the best way to deal with this concept.
Let’s begin!