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

Commit

Permalink
Merge pull request #194 from spring-media/feature/remove-object-from-…
Browse files Browse the repository at this point in the history
…cache

Feature: Remove value from cache
  • Loading branch information
Ivan Lisovyi authored Jul 8, 2021
2 parents f289959 + 2af9441 commit 497a318
Show file tree
Hide file tree
Showing 26 changed files with 308 additions and 56 deletions.
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)
.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

0 comments on commit 497a318

Please sign in to comment.