We now have a fully functioning app, and everything is powered by SQLite. We can insert new facts into the database and the view will immediately refresh with the newest data. And the same happens when removing a fact.
However, there are a few things to not like about the code we have right now. First, we had to put some of our feature’s logic in the view. In particular, we are leveraging the task(id:)
view modifier to listen for changes to state that cause the SQL query to change, and then at that moment we go tell the model to re-fetch the data from the database. That’s a very roundabout and indirect way to do things.
Further, we have a mutable array of facts right in our model, but we aren’t meant to actually mutate it directly. In fact, doing so will not save the data in the database, and so will definitely lead to bugs in your code. You have to instead remember to make any mutations through the database connection, and then the observation we set up will take care of updating the array of facts. It kinda seems like the array of facts should be read-only if possible.
And finally, there is a lot of boilerplate needed to get all of this set up correctly. Every feature that wants access to data in the database will need to:
…use the
task(id:)
view modifier to listen for state changes that change the SQL query,then inside that modifier invoke a method on the model to re-execute the query,
and then inside that method set up SQL observation so that changes to the database will update the state held in the model.
That is quite a bit, and it will be easy to get wrong.
Well, what if we told you that it is possible to hide all of this boilerplate behind a custom persistence strategy that can be used with the @Shared
property wrapper. That will allow you to hold onto the collection of facts in your feature as if it’s a regular array, but secretly it is being powered by SQLite.
Let’s take a look.