Skip to content
This repository has been archived by the owner on Aug 8, 2024. It is now read-only.

Feature: Remove value from cache #194

Merged
merged 4 commits into from
Jul 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions Carlos.xcworkspace/xcshareddata/swiftpm/Package.resolved
Original file line number Diff line number Diff line change
@@ -1,22 +1,40 @@
{
"object": {
"pins": [
{
"package": "CwlCatchException",
"repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git",
"state": {
"branch": null,
"revision": "682841464136f8c66e04afe5dbd01ab51a3a56f2",
"version": "2.1.0"
}
},
{
"package": "CwlPreconditionTesting",
"repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git",
"state": {
"branch": null,
"revision": "02b7a39a99c4da27abe03cab2053a9034379639f",
"version": "2.0.0"
}
},
{
"package": "Nimble",
"repositoryURL": "https://github.com/Quick/Nimble.git",
"state": {
"branch": null,
"revision": "2b1809051b4a65c1d7f5233331daa24572cd7fca",
"version": "8.1.1"
"revision": "af1730dde4e6c0d45bf01b99f8a41713ce536790",
"version": "9.2.0"
}
},
{
"package": "Quick",
"repositoryURL": "https://github.com/Quick/Quick.git",
"state": {
"branch": null,
"revision": "0038bcbab4292f3b028632556507c124e5ba69f3",
"version": "3.0.0"
"revision": "bd86ca0141e3cfb333546de5a11ede63f0c4a0e6",
"version": "4.0.0"
}
}
]
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/Quick/Quick.git", .upToNextMajor(from: "3.0.0")),
.package(url: "https://github.com/Quick/Nimble.git", .upToNextMajor(from: "8.1.0"))
.package(url: "https://github.com/Quick/Quick.git", .upToNextMajor(from: "4.0.0")),
.package(url: "https://github.com/Quick/Nimble.git", .upToNextMajor(from: "9.2.0"))
],
targets: [
.target(
Expand Down
9 changes: 9 additions & 0 deletions Sources/Carlos/CacheLevels/BatchAllCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ public final class BatchAllCache<KeySeq: Sequence, Cache: CacheLevel>: CacheLeve
}
}

public func remove(_ key: KeyType) -> AnyPublisher<Void, Error> {
let all = key.map(cache.remove)
return all.publisher
.setFailureType(to: Error.self)
.collect(all.count)
.map { _ in () }
.eraseToAnyPublisher()
}

public func clear() {
cache.clear()
}
Expand Down
33 changes: 25 additions & 8 deletions Sources/Carlos/CacheLevels/Composed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ extension CacheLevel {
*/
public func compose<A: CacheLevel>(_ cache: A) -> BasicCache<A.KeyType, A.OutputType> where A.KeyType == KeyType, A.OutputType == OutputType {
BasicCache(
getClosure: { key in
self.get(key)
getClosure: { [weak self] key in
guard let self = self else {
return Empty(completeImmediately: true).eraseToAnyPublisher()
}

return self.get(key)
.catch { _ -> AnyPublisher<OutputType, Error> in
Logger.log("Composed| error on getting value for key \(key) on cache \(String(describing: self)).", .info)

Expand All @@ -27,20 +31,33 @@ extension CacheLevel {
.eraseToAnyPublisher()
}.eraseToAnyPublisher()
},
setClosure: { value, key in
Publishers.Zip(
setClosure: { [weak self] value, key in
guard let self = self else {
return Empty(completeImmediately: true).eraseToAnyPublisher()
}

return Publishers.Zip(
self.set(value, forKey: key),
cache.set(value, forKey: key)
)
.map { _ in () }
.eraseToAnyPublisher()
},
clearClosure: {
self.clear()
removeClosure: { [weak self] key in
guard let self = self else {
return Empty(completeImmediately: true).eraseToAnyPublisher()
}

return Publishers.Zip(self.remove(key), cache.remove(key))
.map { _ in () }
.eraseToAnyPublisher()
},
clearClosure: { [weak self] in
self?.clear()
cache.clear()
},
memoryClosure: {
self.onMemoryWarning()
memoryClosure: { [weak self] in
self?.onMemoryWarning()
cache.onMemoryWarning()
}
)
Expand Down
1 change: 1 addition & 0 deletions Sources/Carlos/CacheLevels/Conditioned.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ extension CacheLevel {
BasicCache(
getClosure: conditionedClosure(get, condition: condition),
setClosure: set,
removeClosure: remove,
clearClosure: clear,
memoryClosure: onMemoryWarning
)
Expand Down
27 changes: 25 additions & 2 deletions Sources/Carlos/CacheLevels/DiskCacheLevel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public final class DiskCacheLevel<K: StringConvertible, T: NSCoding>: CacheLevel

_ = try? fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: [:])

cacheQueue.async { () -> Void in
cacheQueue.async {
self.calculateSize()
self.controlCapacity()
}
Expand Down Expand Up @@ -117,13 +117,36 @@ public final class DiskCacheLevel<K: StringConvertible, T: NSCoding>: CacheLevel
.eraseToAnyPublisher()
}

public func remove(_ key: K) -> AnyPublisher<Void, Error> {
AnyPublisher.create { [weak self] promise in
guard let path = self?.pathForKey(key) else {
return
}

do {
Logger.log("DiskCacheLevel| Removing \(key.toString()) at path: \(path) on the disk cache", .info)
try self?.fileManager.removeItem(atPath: path)

self?.calculateSize()

promise(.success(()))
} catch {
Logger.log("DiskCacheLevel| Failed to remove \(key.toString()) at path: \(path) on the disk cache", .error)

promise(.failure(error))
}
}
.subscribe(on: cacheQueue)
.eraseToAnyPublisher()
}

/**
Asynchronously clears the contents of the cache

All the cached files will be removed from the disk storage
*/
public func clear() {
cacheQueue.async { () -> Void in
cacheQueue.async {
self.itemsInDirectory(self.path).forEach { filePath in
_ = try? self.fileManager.removeItem(atPath: filePath)
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/Carlos/CacheLevels/Fetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public protocol Fetcher: CacheLevel {}

/// Extending the Fetcher protocol to have a default no-op implementation for clear, onMemoryWarning and set
extension Fetcher {
/// No-op
public func remove(_ key: KeyType) -> AnyPublisher<Void, Error> {
Empty(completeImmediately: true).eraseToAnyPublisher()
}

/// No-op
public func clear() {}

Expand Down
19 changes: 14 additions & 5 deletions Sources/Carlos/CacheLevels/MemoryCacheLevel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ public final class MemoryCacheLevel<K: StringConvertible, T: AnyObject>: CacheLe
.eraseToAnyPublisher()
}

public func remove(_ key: K) -> AnyPublisher<Void, Error> {
AnyPublisher.create { [weak self] promise in
Logger.log("MemoryCacheLevel| Removing \(key.toString()) on memory level.", .info)

self?.internalCache.removeObject(forKey: key.toString() as NSString)

promise(.success(()))
}
}

/**
Clears the contents of the cache
*/
Expand All @@ -53,12 +63,11 @@ public final class MemoryCacheLevel<K: StringConvertible, T: AnyObject>: CacheLe
- parameter key: The key for the value
*/
public func set(_ value: T, forKey key: K) -> AnyPublisher<Void, Error> {
Logger.log("MemoryCacheLevel| Setting a value for the key \(key.toString()) on the memory cache \(self).", .info)
internalCache.setObject(value, forKey: key.toString() as NSString, cost: value.cost)
AnyPublisher.create { [weak self] promise in
self?.internalCache.setObject(value, forKey: key.toString() as NSString, cost: value.cost)

return Just(())
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
promise(.success(()))
}
}

/**
Expand Down
7 changes: 7 additions & 0 deletions Sources/Carlos/CacheLevels/NSUserDefaultsCacheLevel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ public final class NSUserDefaultsCacheLevel<K: StringConvertible, T: NSCoding>:
.eraseToAnyPublisher()
}

public func remove(_ key: K) -> AnyPublisher<Void, Error> {
AnyPublisher.create { [weak self] promise in
self?.userDefaults.removeObject(forKey: key.toString())
promise(.success(()))
}
}

/**
Completely clears the contents of this cache

Expand Down
10 changes: 7 additions & 3 deletions Sources/Carlos/CacheLevels/PoolCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ public final class PoolCache<C: CacheLevel>: CacheLevel where C.KeyType: Hashabl
Logger.log("Creating a new request \(request) for key \(key)")

return request
.handleEvents(receiveCompletion: { _ in
self.lock.locked {
self.requestsPool[key] = nil
.handleEvents(receiveCompletion: { [weak self] _ in
self?.lock.locked {
self?.requestsPool[key] = nil
}
})
.eraseToAnyPublisher()
Expand All @@ -69,6 +69,10 @@ public final class PoolCache<C: CacheLevel>: CacheLevel where C.KeyType: Hashabl
internalCache.set(value, forKey: key)
}

public func remove(_ key: C.KeyType) -> AnyPublisher<Void, Error> {
internalCache.remove(key)
}

/// Clears the managed cache
public func clear() {
internalCache.clear()
Expand Down
9 changes: 8 additions & 1 deletion Sources/Carlos/Carlos.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,16 @@ public protocol CacheLevel: AnyObject {
/// - Parameter value: The bytes to set on the cache level
/// - Parameter key: The key of the value you're trying to set
///
/// - Returns: A `Publisher that will reflect the status of the set operation
/// - Returns: A `Publisher` that will reflect the status of the set operation
func set(_ value: OutputType, forKey key: KeyType) -> AnyPublisher<Void, Error>

/// Remove value from cache for a given key.
///
/// - Parameter key: They key of the value to be removed
///
/// - Returns: A `Publisher` that reflects a status of the remove operation.
func remove(_ key: KeyType) -> AnyPublisher<Void, Error>

/// Asks to clear the cache level
func clear()

Expand Down
7 changes: 7 additions & 0 deletions Sources/Carlos/Core/BasicCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public final class BasicCache<A, B>: CacheLevel {

private let getClosure: (_ key: A) -> AnyPublisher<B, Error>
private let setClosure: (_ value: B, _ key: A) -> AnyPublisher<Void, Error>
private let removeClosure: (_ key: A) -> AnyPublisher<Void, Error>
private let clearClosure: () -> Void
private let memoryClosure: () -> Void

Expand All @@ -22,11 +23,13 @@ public final class BasicCache<A, B>: CacheLevel {
public init(
getClosure: @escaping (_ key: A) -> AnyPublisher<B, Error>,
setClosure: @escaping (_ value: B, _ key: A) -> AnyPublisher<Void, Error>,
removeClosure: @escaping (_ key: A) -> AnyPublisher<Void, Error>,
clearClosure: @escaping () -> Void,
memoryClosure: @escaping () -> Void
) {
self.getClosure = getClosure
self.setClosure = setClosure
self.removeClosure = removeClosure
self.clearClosure = clearClosure
self.memoryClosure = memoryClosure
}
Expand Down Expand Up @@ -54,6 +57,10 @@ public final class BasicCache<A, B>: CacheLevel {
setClosure(value, key)
}

public func remove(_ key: A) -> AnyPublisher<Void, Error> {
removeClosure(key)
}

/**
Asks the cache to clear its contents

Expand Down
8 changes: 6 additions & 2 deletions Sources/Carlos/Core/FetcherValueTransformation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ extension Fetcher {
*/
public func transformValues<A: OneWayTransformer>(_ transformer: A) -> BasicFetcher<KeyType, A.TypeOut> where OutputType == A.TypeIn {
BasicFetcher(
getClosure: { key in
self.get(key)
getClosure: { [weak self] key in
guard let self = self else {
return Empty(completeImmediately: true).eraseToAnyPublisher()
}

return self.get(key)
.flatMap(transformer.transform)
.eraseToAnyPublisher()
}
Expand Down
25 changes: 21 additions & 4 deletions Sources/Carlos/Operations/KeyTransformation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,35 @@ extension CacheLevel {
*/
public func transformKeys<A: OneWayTransformer>(_ transformer: A) -> BasicCache<A.TypeIn, OutputType> where KeyType == A.TypeOut {
BasicCache(
getClosure: { key in
transformer.transform(key)
getClosure: { [weak self] key in
guard let self = self else {
return Empty(completeImmediately: true).eraseToAnyPublisher()
}

return transformer.transform(key)
.flatMap(self.get)
.eraseToAnyPublisher()
},
setClosure: { value, key in
transformer.transform(key)
setClosure: { [weak self] value, key in
guard let self = self else {
return Empty(completeImmediately: true).eraseToAnyPublisher()
}

return transformer.transform(key)
.flatMap { transformedKey in
self.set(value, forKey: transformedKey)
}
.eraseToAnyPublisher()
},
removeClosure: { [weak self] in
guard let self = self else {
return Empty(completeImmediately: true).eraseToAnyPublisher()
}

return transformer.transform($0)
.flatMap(self.remove)
ivanlisovyi marked this conversation as resolved.
Show resolved Hide resolved
ivanlisovyi marked this conversation as resolved.
Show resolved Hide resolved
.eraseToAnyPublisher()
},
clearClosure: clear,
memoryClosure: onMemoryWarning
)
Expand Down
1 change: 1 addition & 0 deletions Sources/Carlos/Operations/Normalize.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extension CacheLevel {
return BasicCache<KeyType, OutputType>(
getClosure: get,
setClosure: set,
removeClosure: remove,
clearClosure: clear,
memoryClosure: onMemoryWarning
)
Expand Down
1 change: 1 addition & 0 deletions Sources/Carlos/Operations/PostProcess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ extension CacheLevel {
.eraseToAnyPublisher()
},
setClosure: set,
removeClosure: remove,
clearClosure: clear,
memoryClosure: onMemoryWarning
)
Expand Down
Loading