Skip to content

Instantly share code, notes, and snippets.

@lamprosg
Last active January 1, 2020 02:41
Show Gist options
  • Save lamprosg/fe45931d4bfa5ef62730144e8b546665 to your computer and use it in GitHub Desktop.
Save lamprosg/fe45931d4bfa5ef62730144e8b546665 to your computer and use it in GitHub Desktop.
(iOS) Caching
// To be able to use strings as caching keys, we have to use
// NSString here, since NSCache is only compatible with keys
// that are subclasses of NSObject:
let cache = NSCache<NSString, MyClass>()
//Problem:
//can only store class instances, and it’s only compatible with NSObject-based keys
//NSCache simple use here:
//https://www.hackingwithswift.com/example-code/system/how-to-cache-data-using-nscache
//---------
//Hashable protocol:
//https://www.hackingwithswift.com/example-code/language/how-to-conform-to-the-hashable-protocol
//https://developer.apple.com/documentation/swift/hashable
//Creating our own cache which will accept value types as well
final class Cache<Key: Hashable, Value> {
private let wrapped = NSCache<WrappedKey, Entry>()
func insert(_ value: Value, forKey key: Key) {
let entry = Entry(value: value)
wrapped.setObject(entry, forKey: WrappedKey(key))
}
func value(forKey key: Key) -> Value? {
let entry = wrapped.object(forKey: WrappedKey(key))
return entry?.value
}
func removeValue(forKey key: Key) {
wrapped.removeObject(forKey: WrappedKey(key))
}
}
//Our WrappedKey type will, wrap our Key values in order to make them NSCache compatible
private extension Cache {
final class WrappedKey: NSObject {
let key: Key
init(_ key: Key) { self.key = key }
override var hash: Int { return key.hashValue }
override func isEqual(_ object: Any?) -> Bool {
guard let value = object as? WrappedKey else {
return false
}
return value.key == key
}
}
final class Entry {
let value: Value
init(value: Value) {
self.value = value
}
}
}
//Let's make a subscript for easy use
extension Cache {
subscript(key: Key) -> Value? {
get { return value(forKey: key) }
set {
guard let value = newValue else {
// If nil was assigned using our subscript,
// then we remove any value for that key:
removeValue(forKey: key)
return
}
insert(value, forKey: key)
}
}
}
//Article loader which loads articles models
class ArticleLoader {
typealias Handler = (Result<Article, Error>) -> Void //Swift 5 Result type
private let cache = Cache<Article.ID, Article>()
func loadArticle(withID id: Article.ID,
then handler: @escaping Handler) {
if let cached = cache[id] {
return handler(.success(cached))
}
performLoading { [weak self] result in
let article = try? result.get()
article.map { self?.cache[id] = $0 }
handler(result)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment