Basic idea:
struct Version<ContextType, ValueType> {
let context: ContextType
let value: ValueType
}
class SingleCache<ContextType, ValueType> {
typealias CacheVersion = Version<ContextType, ValueType>
typealias CompletionHandler = (_ oldValue: CacheVersion?, _ newValue: CacheVersion?) -> ()
typealias FetchCompletionHandler = (ValueType?)->()
var curVersion: CacheVersion?
let fetcher: (_ context: ContextType, _ completion: @escaping FetchCompletionHandler) -> ()
let equalityChecker: (ContextType, ContextType) -> Bool
let resolver: (_ oldVersion: CacheVersion?, _ newVersion: CacheVersion?) -> CacheVersion?
init(fetcher: @escaping (_ context: ContextType, _ completion: @escaping FetchCompletionHandler) -> (),
equalityChecker: @escaping (ContextType, ContextType) -> Bool,
resolver: @escaping (_ oldVersion: CacheVersion?, _ newVersion: CacheVersion?) -> CacheVersion? = { $1 }) {
self.fetcher = fetcher
self.equalityChecker = equalityChecker
self.resolver = resolver
}
private func fetchAndReturn(for context: ContextType, completion: @escaping CompletionHandler) {
fetcher(context) { [weak self] newValue in
guard let `self` = self else { completion(nil, nil); return }
let oldVersion = self.curVersion
let newVersion: CacheVersion?
if let value = newValue {
newVersion = Version(context: context, value: value)
} else {
newVersion = nil
}
self.curVersion = self.resolver(oldVersion, newVersion)
completion(oldVersion, newVersion)
}
}
func get(for context: ContextType, completion: @escaping CompletionHandler) {
if let version = curVersion, equalityChecker(context, version.context) {
completion(version, version)
} else {
fetchAndReturn(for: context, completion: completion)
}
}
}
/// Testing
let dump: [String: [String]] = [
"ios": ["Ashik", "Dulal", "Muzahid"],
"android": ["Sourav"]
]
let cache = SingleCache<String, [String]>(
fetcher: { (context, completion) in
print("fetching ...")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
completion(dump[context])
})
},
equalityChecker: { (oldStr, newStr) -> Bool in
return oldStr == newStr
})
cache.get(for: "ios") {
print($1?.value)
cache.get(for: "ios") {
print($1?.value)
cache.get(for: "android") {
print($0?.value, $1?.value)
print(cache.curVersion)
cache.curVersion = Version(context: "backend", value: ["rinku", "masum"])
cache.get(for: "backend") {
print($0, $1)
}
}
}
}