Swift iOS Mobile App Caching Controller Update Logic, Cache in Swift

Based on my last post’s Caching System Architecture for Mobile Apps with a WordPress REST back-end, let’s review in more details what is required to build a cache controller in Swift.

The flow:

  • At first run, the app calls all API endpoints requred to download data from the website:
    • Use “Get All” endpoints for each entity, or use paged calls for large datasets
    • Get and parse JSON, save into local DB
    • Use 1 DB table per entity with all its field
  • Store [last update date & time] to local app settings
  • On App launch and on app resume, check if last update time is older than X days.
  • If it is, call API endpoints “Get All” with ?after=[last update date & time]
  • Get and parse JSON, update local DB with new/changed records

We need to store a “last update date” for each entity in UserDefailts. So, for example to save and get the last update date for the Post entity I can do:

UserDefaults.standard.set(Date(), forKey: “PostsLastUpdate”)
UserDefaults.standard.object(forKey: “PostsLastUpdate”) as? Date

In an initial version, I checked all records for updates ater a given interval using the get all entities and going through the full DB each time. This works but is not the best solution. With a complete implementation on server side, you only request updates to the database with a parameter that will get you all posts modified “after” a certain date. Out of the box, this won’t return posts that reside in the trash, so I build a secondary endpoint to return deleted posts. Therefore you need to invoke 2 endpoints:
<ul><li>one for modified and new records,</li>
<li>and one for deleted records.</li>

The implementation can probably be optimized to return both in one call. In order to check for updates, the workflow looks like this.

First, you set a negative number: we subtract this number of days to check if the cache is current or should be updated. This can also be a constant in your global constants file or part of a more generic cache controller class.

let intervalInDays: Int = -1

Then we check if last update was within the days interval. If so, we can kill the process and return void.

if let lastUpdate = UserDefaults.standard.object(forKey: “ItinerariesLastUpdate”) as? Date {
// Updated within the last X days? Skip this update
if(lastUpdate.isGreaterThanDate(Date().addDays(intervalInDays))) {
// Cache is up-to-date, nothing to do.
return
}
}

If the process continues, we call “Get All” the endpoint with an “after” parameter to only get updated items, and parse JSON to store in the local database. Then we explicitely save the DB (context).

Then, we call the endpoint to “get Deleted” posts (using the same “after” parameter) to only get deleted items. This will return an array of post IDs, so you need to make a query to remove those records from your local DB. Save the DB again.

Indeed, it looks like we need to explicitly save the database in swift when we do this kind of stuff, and it’s also good to add a call to write the DB to disk in “will app terminate” within app delegate.

At the end of the process, if no errors were returned and the DB saved correctly to disk, you have to update the Last Update Date like this:

UserDefaults.standard.set(Date(), forKey: “PostsLastUpdate”)