SKCache — another ordinary cache. Almost.

Steliyan Hadzhidenev
Dev Labs
Published in
5 min readNov 15, 2017

--

Once upon a time there was a developer who wanted to save everything into one cache.

The idea came up when I was searching the internet for a caching library which can store each known type in Swift (like Int, String, Arrays, Sets, etc.). I looked and searched but couldn’t find what I needed anywhere. So I set a challenge — to create a caching library, which can store every possible type in Swift.

Having the idea now it was time to develop it. The problem was, I had no clue where to start. Every day I was like

Ideas were flying in and out of my head. I was overwhelmed and in the end burned out.

So one day I sat alone in my room with a glass of whiskey (yes, sometime alcohol helps me think better) and went over all the notes I made. It was then that I decided to introduce the SKObject. It will store a value of a type known in Swift under a specific type. People might think what I was thinking, but actualy the pricipe is straightforward. If you write

let object = SKObject(value: 'your value to store', forKey: 'your key under which the object will be stored')

you’ve already created the object which will be cached. Easy, right?

First challenge was completed, but now it was time for the tricky part — where to store all of the objects I want to cache? Should I use a dictionary or some other container? After a bit of a research and experimenting with different structures I finally found the perfect container — the NSCache. A dictionary or an array wouldn’t work in my case, because of the cost limit management which NSCache offers. Neither of them have it.

At first I was storing each object separately under the defined key. Choosing this approach I met problems with quickly reaching the limit of the NSCache and purging all existing objects. Again it was time for brain power. After reading some articles and reviewing other cache libraries I came up with the idea to collect all SKObjects in one array and store that array in the NSCache. Using such logic I never reached the limit and the items were never purged.

As the NSCache mimics the functionality of a dictionary I needed a unique identifier for the objects to be stored under. I chose to use the bundle indentifier of the project. Reason — using some predefined string wasn’t an appropriate approach due to some accessibility issues, so using the unique signiture of the project I was able to preserve the information without accedently overwriting it.

Adding or retrieving an object from the cache is actually pretty easy and straight forward. Assuming we have our SKObject we just need to call

SKCache.shared.add(object: ‘instance of your SKObject’)

to add it and

SKCache.shared.get(forKey: 'the key of the desired object')

to retrieve it.

You might be thinking is it that easy and I’ll give a simple answer — Yes!

After sorting out where to save the objects I thought I was ready to deploy my library, but I was wrong. Many other cache libraries also offer a disk cache. I asked myself “Should I be the same as the others?”. Some cached objects must live for one session so it is not necessary to store them on the disk. But others, like images or big data, must be saved for later reuse. So I decided to include a possibility to save objects to the disk.

As a starting point I decided how to represent the objects from the cache into objects on the disk. Then Swift 4 wasn’t out yet, but when Apple released it, it gave me more possibilities and at the end I came up with an idea to store all objects in files in the applications cache directory.

After sorting this out, a issue arised. Is it memory efficient to store only files to the disk? I might end up without any free disk space at the end. To solve this problem I decided to add an expiry date to each cache object, so when I’m loading the cache I can easily discard those which are expired and this way free disk space. When creating your SKObject an expiry date is automatically assigned to it, but if you want to manually add it, you can simply specify it in the constructor like:

let object = SKObject(value: 'your value to store', forKey: 'your key under which the object will be stored', expiryDate: 'your expiry date')

By default SKObjects will never expire. You can change that option by changing the SKCache.shared.expiration. SKCache offers different possibilities for the expiry date:

/// Enum to indicate the live time of each time in the cache
///
/// - never: option to never expire the cache object
/// - everyDay: option to expire at the end of the day
/// - everyWeek: option to expiry after a week
/// - everyMonth: option to set the expiry date each month
/// - seconds: option to set the expiry after some secondspublic enum ExpiryDate {
case never
case everyDay
case everyWeek
case everyMonth
case seconds(TimeInterval)
.
.
.
}

Finally when I had my saving and loading methods for the cache, I needed a proper place where to call them. After a bit of analysing, the most appropriate place to load the cache was when an application starts. I implement SKCache.load() in the AppDelegate like:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

.
.
.

do {
try SKCache.load()
} catch {
print(error)
}

return true
}

Of course you can place the loading somewhere else and it will still works. Surrounding the method with try-catch block will ensure that if an error occurs you will be able to see it and analyse it.

Ok we have the loading of the object, and you might ask about saving the cache to the disk. Just implement SKCache.save() like:

func applicationDidEnterBackground(_ application: UIApplication) {

.
.
.

do {
try SKCache.save()
} catch {
print(error)
}
}

and you will save all of your objects to the disk space. Again by surrounding the method with try-catch block will ensure that if an error occurs you will be able to see it and analyse it. Implementing save() and load() methods will enable the disk cache for the library.

The functionality to save objects to the disk finally completed my cache library.

Currently SKCache is available only via Cocoapods. To install it, simply add the following line to your Podfile:

pod 'SKCache'

Any and all pull requests are welcome.. Check it out on GitHub.

--

--