Description

Cache doesn't claim to be unique in this area, but it's not another monster
library that gives you a god's power. It does nothing but caching, but it does it well. It offers a good public API
with out-of-box implementations and great customization possibilities. Cache
utilizes Codable
in Swift 4 to perform serialization.
Read the story here Open Source Stories: From Cachable to Generic Storage in Cache
Key features
- [x] Work with Swift 4
Codable
. Anything conforming toCodable
will be saved and loaded easily byStorage
. - [x] Hybrid with memory and disk storage.
- [X] Many options via
DiskConfig
andMemoryConfig
. - [x] Support
expiry
and clean up of expired objects. - [x] Thread safe. Operations can be accessed from any queue.
- [x] Sync by default. Also support Async APIs.
- [x] Extensive unit test coverage and great documentation.
- [x] iOS, tvOS and macOS support.
Usage
Storage
Cache
is built based on Chain-of-responsibility pattern, in which there are many processing objects, each knows how to do 1 task and delegates to the next one, so can you compose Storages the way you like.
For now the following Storage are supported
MemoryStorage
: save object to memory.DiskStorage
: save object to disk.HybridStorage
: save object to memory and disk, so you get persistented object on disk, while fast access with in memory objects.SyncStorage
: blocking APIs, all read and write operations are scheduled in a serial queue, all sync manner.AsyncStorage
: non-blocking APIs, operations are scheduled in an internal queue for serial processing. No read and write should happen at the same time.
Although you can use those Storage at your discretion, you don't have to. Because we also provide a convenient Storage
which uses HybridStorage
under the hood, while exposes sync and async APIs through SyncStorage
and AsyncStorage
.
All you need to do is to specify the configuration you want with DiskConfig
and MemoryConfig
. The default configurations are good to go, but you can customise a lot.
Generic, Type safety and Transformer
All Storage
now are generic by default, so you can get a type safety experience. Once you create a Storage, it has a type constraint that you don't need to specify type for each operation afterwards.
If you want to change the type, Cache
offers transform
functions, look for Transformer
and TransformerFactory
for built-in transformers.
Each transformation allows you to work with a specific type, however the underlying caching mechanism remains the same, you're working with the same Storage
, just with different type annotation. You can also create different Storage
for each type if you want.
Transformer
is necessary because the need of serialising and deserialising objects to and from Data
for disk persistency. Cache
provides default Transformer
for Data
, Codable
and UIImage/NSImage
Codable types
Storage
supports any objects that conform to Codable protocol. You can make your own things conform to Codable so that can be saved and loaded from Storage
.
The supported types are
- Primitives like
Int
,Float
,String
,Bool
, ... - Array of primitives like
[Int]
,[Float]
,[Double]
, ... - Set of primitives like
Set<String>
,Set<Int>
, ... - Simply dictionary like
[String: Int]
,[String: String]
, ... Date
URL
Data
Error handling
Error handling is done via try catch
. Storage
throws errors in terms of StorageError
.
There can be errors because of disk problem or type mismatch when loading from storage, so if want to handle errors, you need to do try catch
Configuration
Here is how you can play with many configuration options
On iOS, tvOS we can also specify protectionType
on DiskConfig
to add a level of security to files stored on disk by your app in the app’s container. For more information, see FileProtectionType
Sync APIs
Storage
is sync by default and is thread safe
, you can access it from any queues. All Sync functions are constrained by StorageAware
protocol.
Entry
There is time you want to get object together with its expiry information and meta data. You can use Entry
meta
may contain file information if the object was fetched from disk storage.
Custom Codable
Codable
works for simple dictionary like [String: Int]
, [String: String]
, ... It does not work for [String: Any]
as Any
is not Codable
conformance, it will raise fatal error
at runtime. So when you get json from backend responses, you need to convert that to your custom Codable
objects and save to Storage
instead.
Async APIs
In async
fashion, you deal with Result
instead of try catch
because the result is delivered at a later time, in order to not block the current calling queue. In the completion block, you either have value
or error
.
You access Async APIs via storage.async
, it is also thread safe, and you can use Sync and Async APIs in any order you want. All Async functions are constrained by AsyncStorageAware
protocol.
Expiry date
By default, all saved objects have the same expiry as the expiry you specify in DiskConfig
or MemoryConfig
. You can overwrite this for a specific object by specifying expiry
for setObject
Observations
Storage allows you to observe changes in the cache layer, both on
a store and a key levels. The API lets you pass any object as an observer,
while also passing an observation closure. The observation closure will be
removed automatically when the weakly captured observer has been deallocated.
Storage observations
Key observations
Handling JSON response
Most of the time, our use case is to fetch some json from backend, display it while saving the json to storage for future uses. If you're using libraries like Alamofire or Malibu, you mostly get json in the form of dictionary, string, or data.
Storage
can persist String
or Data
. You can even save json to Storage
using JSONArrayWrapper
and JSONDictionaryWrapper
, but we prefer persisting the strong typed objects, since those are the objects that you will use to display in UI. Furthermore, if the json data can't be converted to strongly typed objects, what's the point of saving it ? ?
You can use these extensions on JSONDecoder
to decode json dictionary, string or data to objects.
This is how you perform object converting and saving with Alamofire
What about images
If you want to load image into UIImageView
or NSImageView
, then we also have a nice gift for you. It's called Imaginary and uses Cache
under the hood to make you life easier when it comes to working with remote images.
Installation
Cocoapods
Cache is available through CocoaPods. To install
it, simply add the following line to your Podfile:
Carthage
Cache is also available through Carthage.
To install just write into your Cartfile:
You also need to add SwiftHash.framework
in your copy-frameworks script.
Author
Contributing
We would love you to contribute to Cache, check the CONTRIBUTING file for more info.
License
Cache is available under the MIT license. See the LICENSE file for more info.