Skip to content

Commit e42cbc0

Browse files
committed
Implement store observations
1 parent 34db394 commit e42cbc0

File tree

4 files changed

+66
-0
lines changed

4 files changed

+66
-0
lines changed

Cache.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@
135135
D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A138C01EB29BFA00881A20 /* UIImage+Extensions.swift */; };
136136
D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A138C01EB29BFA00881A20 /* UIImage+Extensions.swift */; };
137137
D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A138C31EB29C2100881A20 /* NSImage+Extensions.swift */; };
138+
D5A9D1B721134547005DBD3F /* ObservationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1B621134547005DBD3F /* ObservationToken.swift */; };
139+
D5A9D1B821134547005DBD3F /* ObservationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1B621134547005DBD3F /* ObservationToken.swift */; };
140+
D5A9D1B921134547005DBD3F /* ObservationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1B621134547005DBD3F /* ObservationToken.swift */; };
141+
D5A9D1BB211345D4005DBD3F /* Dictionary+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BA211345D4005DBD3F /* Dictionary+Extensions.swift */; };
142+
D5A9D1BC211345D4005DBD3F /* Dictionary+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BA211345D4005DBD3F /* Dictionary+Extensions.swift */; };
143+
D5A9D1BD211345D4005DBD3F /* Dictionary+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BA211345D4005DBD3F /* Dictionary+Extensions.swift */; };
138144
/* End PBXBuildFile section */
139145

140146
/* Begin PBXContainerItemProxy section */
@@ -222,6 +228,8 @@
222228
D5643E361C43F2CC00582E17 /* Storage.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Storage.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
223229
D5A138C01EB29BFA00881A20 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = "<group>"; };
224230
D5A138C31EB29C2100881A20 /* NSImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSImage+Extensions.swift"; sourceTree = "<group>"; };
231+
D5A9D1B621134547005DBD3F /* ObservationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservationToken.swift; sourceTree = "<group>"; };
232+
D5A9D1BA211345D4005DBD3F /* Dictionary+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Extensions.swift"; sourceTree = "<group>"; };
225233
D5DC59E01C20593E003BD79B /* Cache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cache.framework; sourceTree = BUILT_PRODUCTS_DIR; };
226234
EBAACA991FBC369300FA206E /* SimpleStorage.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SimpleStorage.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
227235
/* End PBXFileReference section */
@@ -310,6 +318,7 @@
310318
children = (
311319
D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */,
312320
D2CF98501F694FFA00CE8F68 /* Date+Extensions.swift */,
321+
D5A9D1BA211345D4005DBD3F /* Dictionary+Extensions.swift */,
313322
);
314323
path = Extensions;
315324
sourceTree = "<group>";
@@ -333,6 +342,7 @@
333342
D270148F20D1251E003B45C7 /* TypeWrapper.swift */,
334343
D270149320D125AC003B45C7 /* MemoryCapsule.swift */,
335344
D27014A420D129EB003B45C7 /* TransformerFactory.swift */,
345+
D5A9D1B621134547005DBD3F /* ObservationToken.swift */,
336346
);
337347
path = Library;
338348
sourceTree = "<group>";
@@ -806,12 +816,14 @@
806816
D270147220D1018A003B45C7 /* Transformer.swift in Sources */,
807817
D21B668C1F6A723C00125DE1 /* Types.swift in Sources */,
808818
D2D4CC261FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */,
819+
D5A9D1BD211345D4005DBD3F /* Dictionary+Extensions.swift in Sources */,
809820
D27014A720D129EB003B45C7 /* TransformerFactory.swift in Sources */,
810821
D270147E20D107DA003B45C7 /* SyncStorage.swift in Sources */,
811822
D292DAFF1F6A970B0060F614 /* Result.swift in Sources */,
812823
D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */,
813824
D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */,
814825
D21B668B1F6A723C00125DE1 /* StorageError.swift in Sources */,
826+
D5A9D1B921134547005DBD3F /* ObservationToken.swift in Sources */,
815827
D270148A20D11040003B45C7 /* Storage+Transform.swift in Sources */,
816828
D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */,
817829
D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */,
@@ -887,12 +899,14 @@
887899
D270147120D1018A003B45C7 /* Transformer.swift in Sources */,
888900
D21B66831F6A723C00125DE1 /* Types.swift in Sources */,
889901
D2D4CC251FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */,
902+
D5A9D1BC211345D4005DBD3F /* Dictionary+Extensions.swift in Sources */,
890903
D27014A620D129EB003B45C7 /* TransformerFactory.swift in Sources */,
891904
D270147D20D107DA003B45C7 /* SyncStorage.swift in Sources */,
892905
D292DAFE1F6A970B0060F614 /* Result.swift in Sources */,
893906
D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */,
894907
D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */,
895908
D21B66821F6A723C00125DE1 /* StorageError.swift in Sources */,
909+
D5A9D1B821134547005DBD3F /* ObservationToken.swift in Sources */,
896910
D270148920D11040003B45C7 /* Storage+Transform.swift in Sources */,
897911
D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */,
898912
D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */,
@@ -937,12 +951,14 @@
937951
D2CF98601F694FFA00CE8F68 /* DiskConfig.swift in Sources */,
938952
D270147020D1018A003B45C7 /* Transformer.swift in Sources */,
939953
D292DAFD1F6A970B0060F614 /* Result.swift in Sources */,
954+
D5A9D1BB211345D4005DBD3F /* Dictionary+Extensions.swift in Sources */,
940955
D27014A520D129EB003B45C7 /* TransformerFactory.swift in Sources */,
941956
D2D4CC241FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */,
942957
D270147C20D107DA003B45C7 /* SyncStorage.swift in Sources */,
943958
D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */,
944959
D270148820D11040003B45C7 /* Storage+Transform.swift in Sources */,
945960
D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */,
961+
D5A9D1B721134547005DBD3F /* ObservationToken.swift in Sources */,
946962
D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */,
947963
D270147820D1046A003B45C7 /* HybridStorage.swift in Sources */,
948964
D270148420D10E76003B45C7 /* AsyncStorage.swift in Sources */,
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Foundation
2+
3+
extension Dictionary where Key == UUID {
4+
mutating func insert(_ value: Value) -> UUID {
5+
let id = UUID()
6+
self[id] = value
7+
return id
8+
}
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
final class ObservationToken {
2+
private let cancellationClosure: () -> Void
3+
4+
init(cancellationClosure: @escaping () -> Void) {
5+
self.cancellationClosure = cancellationClosure
6+
}
7+
8+
func cancel() {
9+
cancellationClosure()
10+
}
11+
}

Source/Shared/Storage/Storage.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
11
import Foundation
22
import Dispatch
33

4+
enum StoreChange {
5+
case addition
6+
case singleDeletion
7+
case allDeletion
8+
case expiredDeletion
9+
}
10+
411
/// Manage storage. Use memory storage if specified.
512
/// Synchronous by default. Use `async` for asynchronous operations.
613
public class Storage<T> {
714
/// Used for sync operations
815
let syncStorage: SyncStorage<T>
916
let asyncStorage: AsyncStorage<T>
1017

18+
private var observations = [UUID : (Storage, StoreChange) -> Void]()
19+
20+
@discardableResult
21+
func observeChanges(using closure: @escaping (Storage, StoreChange) -> Void) -> ObservationToken {
22+
let id = observations.insert(closure)
23+
24+
return ObservationToken { [weak self] in
25+
self?.observations.removeValue(forKey: id)
26+
}
27+
}
28+
1129
/// Initialize storage with configuration options.
1230
///
1331
/// - Parameters:
@@ -56,14 +74,26 @@ extension Storage: StorageAware {
5674

5775
public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
5876
try self.syncStorage.setObject(object, forKey: key, expiry: expiry)
77+
notifyObservers(of: .addition)
5978
}
6079

6180
public func removeAll() throws {
6281
try self.syncStorage.removeAll()
82+
notifyObservers(of: .allDeletion)
6383
}
6484

6585
public func removeExpiredObjects() throws {
6686
try self.syncStorage.removeExpiredObjects()
87+
notifyObservers(of: .expiredDeletion)
88+
}
89+
90+
private func notifyObservers(of change: StoreChange) {
91+
observations.values.forEach { [weak self] closure in
92+
guard let strongSelf = self else {
93+
return
94+
}
95+
closure(strongSelf, change)
96+
}
6797
}
6898
}
6999

0 commit comments

Comments
 (0)