Last active
January 1, 2020 02:41
-
-
Save lamprosg/fe45931d4bfa5ef62730144e8b546665 to your computer and use it in GitHub Desktop.
(iOS) Caching
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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