Skip to content

Commit 9986637

Browse files
authored
Merge pull request #201 from hyperoslo/feature/storage-observations
Feature: storage observations
2 parents 34db394 + 364d2ec commit 9986637

File tree

10 files changed

+242
-4
lines changed

10 files changed

+242
-4
lines changed

Cache.xcodeproj/project.pbxproj

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,26 @@
128128
D2D4CC251FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */; };
129129
D2D4CC261FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */; };
130130
D2D4CC281FA342CA00E4A2D5 /* JSONWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC271FA342CA00E4A2D5 /* JSONWrapperTests.swift */; };
131+
D511464B2114775100197DCE /* StorageObservationRegistryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D511464A2114775100197DCE /* StorageObservationRegistryTests.swift */; };
132+
D511464D2114775100197DCE /* StorageObservationRegistryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D511464A2114775100197DCE /* StorageObservationRegistryTests.swift */; };
133+
D511464F21147B7C00197DCE /* ObservationTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D511464E21147B7C00197DCE /* ObservationTokenTests.swift */; };
134+
D511465121147B7C00197DCE /* ObservationTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D511464E21147B7C00197DCE /* ObservationTokenTests.swift */; };
131135
D5291D1D1C2837DB00B702C9 /* Cache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5DC59E01C20593E003BD79B /* Cache.framework */; };
132136
D5291D6A1C283B5400B702C9 /* Cache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5291D601C283B5300B702C9 /* Cache.framework */; };
133137
D5291D851C283C7C00B702C9 /* TestHelper+OSX.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5291D811C283C7000B702C9 /* TestHelper+OSX.swift */; };
134138
D5291DA31C2841D200B702C9 /* NSImage+ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5291DA21C2841D200B702C9 /* NSImage+ExtensionsTests.swift */; };
135139
D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A138C01EB29BFA00881A20 /* UIImage+Extensions.swift */; };
136140
D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A138C01EB29BFA00881A20 /* UIImage+Extensions.swift */; };
137141
D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A138C31EB29C2100881A20 /* NSImage+Extensions.swift */; };
142+
D5A9D1B721134547005DBD3F /* ObservationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1B621134547005DBD3F /* ObservationToken.swift */; };
143+
D5A9D1B821134547005DBD3F /* ObservationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1B621134547005DBD3F /* ObservationToken.swift */; };
144+
D5A9D1B921134547005DBD3F /* ObservationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1B621134547005DBD3F /* ObservationToken.swift */; };
145+
D5A9D1BF21134776005DBD3F /* StoreChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BE21134776005DBD3F /* StoreChange.swift */; };
146+
D5A9D1C021134776005DBD3F /* StoreChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BE21134776005DBD3F /* StoreChange.swift */; };
147+
D5A9D1C121134776005DBD3F /* StoreChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BE21134776005DBD3F /* StoreChange.swift */; };
148+
D5A9D1C321144B65005DBD3F /* StorageObservationRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1C221144B65005DBD3F /* StorageObservationRegistry.swift */; };
149+
D5A9D1C421144B65005DBD3F /* StorageObservationRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1C221144B65005DBD3F /* StorageObservationRegistry.swift */; };
150+
D5A9D1C521144B65005DBD3F /* StorageObservationRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1C221144B65005DBD3F /* StorageObservationRegistry.swift */; };
138151
/* End PBXBuildFile section */
139152

140153
/* Begin PBXContainerItemProxy section */
@@ -210,6 +223,8 @@
210223
D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDictionaryWrapper.swift; sourceTree = "<group>"; };
211224
D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONArrayWrapper.swift; sourceTree = "<group>"; };
212225
D2D4CC271FA342CA00E4A2D5 /* JSONWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONWrapperTests.swift; sourceTree = "<group>"; };
226+
D511464A2114775100197DCE /* StorageObservationRegistryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageObservationRegistryTests.swift; sourceTree = "<group>"; };
227+
D511464E21147B7C00197DCE /* ObservationTokenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservationTokenTests.swift; sourceTree = "<group>"; };
213228
D5291CDF1C28374800B702C9 /* TestHelper+iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TestHelper+iOS.swift"; sourceTree = "<group>"; };
214229
D5291D181C2837DB00B702C9 /* Cache-iOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Cache-iOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
215230
D5291D231C28380100B702C9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -222,6 +237,9 @@
222237
D5643E361C43F2CC00582E17 /* Storage.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Storage.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
223238
D5A138C01EB29BFA00881A20 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = "<group>"; };
224239
D5A138C31EB29C2100881A20 /* NSImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSImage+Extensions.swift"; sourceTree = "<group>"; };
240+
D5A9D1B621134547005DBD3F /* ObservationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservationToken.swift; sourceTree = "<group>"; };
241+
D5A9D1BE21134776005DBD3F /* StoreChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreChange.swift; sourceTree = "<group>"; };
242+
D5A9D1C221144B65005DBD3F /* StorageObservationRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageObservationRegistry.swift; sourceTree = "<group>"; };
225243
D5DC59E01C20593E003BD79B /* Cache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cache.framework; sourceTree = BUILT_PRODUCTS_DIR; };
226244
EBAACA991FBC369300FA206E /* SimpleStorage.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SimpleStorage.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
227245
/* End PBXFileReference section */
@@ -333,6 +351,7 @@
333351
D270148F20D1251E003B45C7 /* TypeWrapper.swift */,
334352
D270149320D125AC003B45C7 /* MemoryCapsule.swift */,
335353
D27014A420D129EB003B45C7 /* TransformerFactory.swift */,
354+
D5A9D1B621134547005DBD3F /* ObservationToken.swift */,
336355
);
337356
path = Library;
338357
sourceTree = "<group>";
@@ -348,6 +367,8 @@
348367
D270147F20D10982003B45C7 /* Storage.swift */,
349368
D270148320D10E76003B45C7 /* AsyncStorage.swift */,
350369
D270148720D11040003B45C7 /* Storage+Transform.swift */,
370+
D5A9D1BE21134776005DBD3F /* StoreChange.swift */,
371+
D5A9D1C221144B65005DBD3F /* StorageObservationRegistry.swift */,
351372
);
352373
path = Storage;
353374
sourceTree = "<group>";
@@ -380,6 +401,7 @@
380401
D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */,
381402
D2D4CC191FA3166900E4A2D5 /* MD5Tests.swift */,
382403
D2D4CC271FA342CA00E4A2D5 /* JSONWrapperTests.swift */,
404+
D511464E21147B7C00197DCE /* ObservationTokenTests.swift */,
383405
);
384406
path = Library;
385407
sourceTree = "<group>";
@@ -394,6 +416,7 @@
394416
D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */,
395417
D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */,
396418
D236F3191F6BEF73004EE01F /* StorageTests.swift */,
419+
D511464A2114775100197DCE /* StorageObservationRegistryTests.swift */,
397420
);
398421
path = Storage;
399422
sourceTree = "<group>";
@@ -794,6 +817,7 @@
794817
files = (
795818
D221E5C620D00DDB00BC940E /* DiskStorage.swift in Sources */,
796819
D21B669D1F6A724600125DE1 /* DiskConfig.swift in Sources */,
820+
D5A9D1C521144B65005DBD3F /* StorageObservationRegistry.swift in Sources */,
797821
D21B66871F6A723C00125DE1 /* ExpirationMode.swift in Sources */,
798822
D21B66881F6A723C00125DE1 /* Expiry.swift in Sources */,
799823
D270147620D101F3003B45C7 /* StorageAware.swift in Sources */,
@@ -812,6 +836,7 @@
812836
D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */,
813837
D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */,
814838
D21B668B1F6A723C00125DE1 /* StorageError.swift in Sources */,
839+
D5A9D1B921134547005DBD3F /* ObservationToken.swift in Sources */,
815840
D270148A20D11040003B45C7 /* Storage+Transform.swift in Sources */,
816841
D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */,
817842
D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */,
@@ -821,6 +846,7 @@
821846
D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */,
822847
D270148220D10982003B45C7 /* Storage.swift in Sources */,
823848
D221E5C220D00DCC00BC940E /* Entry.swift in Sources */,
849+
D5A9D1C121134776005DBD3F /* StoreChange.swift in Sources */,
824850
);
825851
runOnlyForDeploymentPostprocessing = 0;
826852
};
@@ -835,8 +861,10 @@
835861
D27014A020D12870003B45C7 /* MemoryStorageTests.swift in Sources */,
836862
D2CF98261F69427C00CE8F68 /* User.swift in Sources */,
837863
D27014AE20D12D83003B45C7 /* AsyncStorageTests.swift in Sources */,
864+
D511465121147B7C00197DCE /* ObservationTokenTests.swift in Sources */,
838865
D28A1D241F6FFEF60030DF81 /* ObjectConverterTests.swift in Sources */,
839866
D27014B120D12E38003B45C7 /* StorageSupportTests.swift in Sources */,
867+
D511464D2114775100197DCE /* StorageObservationRegistryTests.swift in Sources */,
840868
D27014AD20D12CC3003B45C7 /* SyncStorageTests.swift in Sources */,
841869
D27014AA20D12BA4003B45C7 /* HybridStorageTests.swift in Sources */,
842870
);
@@ -852,9 +880,11 @@
852880
D2CF987C1F69513800CE8F68 /* Date+ExtensionsTests.swift in Sources */,
853881
D28C9BAC1F67ECD400C180C1 /* TestHelper+iOS.swift in Sources */,
854882
D2CF98211F69427C00CE8F68 /* TestHelper.swift in Sources */,
883+
D511464F21147B7C00197DCE /* ObservationTokenTests.swift in Sources */,
855884
D2CF987F1F69513800CE8F68 /* ImageWrapperTests.swift in Sources */,
856885
D2D4CC1A1FA3166900E4A2D5 /* MD5Tests.swift in Sources */,
857886
D2D4CC281FA342CA00E4A2D5 /* JSONWrapperTests.swift in Sources */,
887+
D511464B2114775100197DCE /* StorageObservationRegistryTests.swift in Sources */,
858888
D27014B320D13E2C003B45C7 /* StorageTests.swift in Sources */,
859889
D28C9BAF1F67EF8300C180C1 /* UIImage+ExtensionsTests.swift in Sources */,
860890
D2CF987D1F69513800CE8F68 /* MemoryCapsuleTests.swift in Sources */,
@@ -875,6 +905,7 @@
875905
files = (
876906
D221E5C520D00DDB00BC940E /* DiskStorage.swift in Sources */,
877907
D21B669B1F6A724600125DE1 /* DiskConfig.swift in Sources */,
908+
D5A9D1C421144B65005DBD3F /* StorageObservationRegistry.swift in Sources */,
878909
D21B667E1F6A723C00125DE1 /* ExpirationMode.swift in Sources */,
879910
D21B667F1F6A723C00125DE1 /* Expiry.swift in Sources */,
880911
D270147520D101F3003B45C7 /* StorageAware.swift in Sources */,
@@ -893,6 +924,7 @@
893924
D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */,
894925
D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */,
895926
D21B66821F6A723C00125DE1 /* StorageError.swift in Sources */,
927+
D5A9D1B821134547005DBD3F /* ObservationToken.swift in Sources */,
896928
D270148920D11040003B45C7 /* Storage+Transform.swift in Sources */,
897929
D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */,
898930
D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */,
@@ -902,6 +934,7 @@
902934
D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */,
903935
D270148120D10982003B45C7 /* Storage.swift in Sources */,
904936
D221E5C120D00DCC00BC940E /* Entry.swift in Sources */,
937+
D5A9D1C021134776005DBD3F /* StoreChange.swift in Sources */,
905938
);
906939
runOnlyForDeploymentPostprocessing = 0;
907940
};
@@ -925,6 +958,7 @@
925958
files = (
926959
D221E5C420D00DDB00BC940E /* DiskStorage.swift in Sources */,
927960
D2CF98681F694FFA00CE8F68 /* ImageWrapper.swift in Sources */,
961+
D5A9D1C321144B65005DBD3F /* StorageObservationRegistry.swift in Sources */,
928962
D2CF98871F695B8F00CE8F68 /* Types.swift in Sources */,
929963
D2CF98621F694FFA00CE8F68 /* Date+Extensions.swift in Sources */,
930964
D2CF98641F694FFA00CE8F68 /* DataSerializer.swift in Sources */,
@@ -943,6 +977,7 @@
943977
D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */,
944978
D270148820D11040003B45C7 /* Storage+Transform.swift in Sources */,
945979
D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */,
980+
D5A9D1B721134547005DBD3F /* ObservationToken.swift in Sources */,
946981
D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */,
947982
D270147820D1046A003B45C7 /* HybridStorage.swift in Sources */,
948983
D270148420D10E76003B45C7 /* AsyncStorage.swift in Sources */,
@@ -952,6 +987,7 @@
952987
D2CF98611F694FFA00CE8F68 /* MemoryConfig.swift in Sources */,
953988
D2CF98661F694FFA00CE8F68 /* ExpirationMode.swift in Sources */,
954989
D221E5C020D00DCC00BC940E /* Entry.swift in Sources */,
990+
D5A9D1BF21134776005DBD3F /* StoreChange.swift in Sources */,
955991
);
956992
runOnlyForDeploymentPostprocessing = 0;
957993
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public final class ObservationToken {
2+
private let cancellationClosure: () -> Void
3+
4+
init(cancellationClosure: @escaping () -> Void) {
5+
self.cancellationClosure = cancellationClosure
6+
}
7+
8+
public func cancel() {
9+
cancellationClosure()
10+
}
11+
}

Source/Shared/Storage/HybridStorage.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import Foundation
22

33
/// Use both memory and disk storage. Try on memory first.
4-
public class HybridStorage<T> {
4+
public final class HybridStorage<T> {
55
public let memoryStorage: MemoryStorage<T>
66
public let diskStorage: DiskStorage<T>
7+
public let storageObservationRegistry = StorageObservationRegistry<HybridStorage>()
78

89
public init(memoryStorage: MemoryStorage<T>, diskStorage: DiskStorage<T>) {
910
self.memoryStorage = memoryStorage
@@ -26,21 +27,25 @@ extension HybridStorage: StorageAware {
2627
public func removeObject(forKey key: String) throws {
2728
memoryStorage.removeObject(forKey: key)
2829
try diskStorage.removeObject(forKey: key)
30+
storageObservationRegistry.notifyObservers(about: .remove(key: key), in: self)
2931
}
3032

3133
public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
3234
memoryStorage.setObject(object, forKey: key, expiry: expiry)
3335
try diskStorage.setObject(object, forKey: key, expiry: expiry)
36+
storageObservationRegistry.notifyObservers(about: .add(key: key), in: self)
3437
}
3538

3639
public func removeAll() throws {
3740
memoryStorage.removeAll()
3841
try diskStorage.removeAll()
42+
storageObservationRegistry.notifyObservers(about: .removeAll, in: self)
3943
}
4044

4145
public func removeExpiredObjects() throws {
4246
memoryStorage.removeExpiredObjects()
4347
try diskStorage.removeExpiredObjects()
48+
storageObservationRegistry.notifyObservers(about: .removeExpired, in: self)
4449
}
4550
}
4651

Source/Shared/Storage/Storage.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import Dispatch
33

44
/// Manage storage. Use memory storage if specified.
55
/// Synchronous by default. Use `async` for asynchronous operations.
6-
public class Storage<T> {
6+
public final class Storage<T> {
77
/// Used for sync operations
88
let syncStorage: SyncStorage<T>
99
let asyncStorage: AsyncStorage<T>
1010

11+
public let storageObservationRegistry = StorageObservationRegistry<Storage>()
12+
1113
/// Initialize storage with configuration options.
1214
///
1315
/// - Parameters:
@@ -23,7 +25,6 @@ public class Storage<T> {
2325
storage: hybridStorage,
2426
serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue")
2527
)
26-
2728
let asyncStorage = AsyncStorage(
2829
storage: hybridStorage,
2930
serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue")
@@ -39,10 +40,25 @@ public class Storage<T> {
3940
public required init(syncStorage: SyncStorage<T>, asyncStorage: AsyncStorage<T>) {
4041
self.syncStorage = syncStorage
4142
self.asyncStorage = asyncStorage
43+
subscribeToChanges()
4244
}
4345

4446
/// Used for async operations
4547
public lazy var async = self.asyncStorage
48+
49+
private func subscribeToChanges() {
50+
subscribeToChanges(in: syncStorage.innerStorage)
51+
if syncStorage.innerStorage !== asyncStorage.innerStorage {
52+
subscribeToChanges(in: asyncStorage.innerStorage)
53+
}
54+
}
55+
56+
private func subscribeToChanges(in storage: HybridStorage<T>) {
57+
storage.storageObservationRegistry.addObservation { [weak self] _, change in
58+
guard let strongSelf = self else { return }
59+
strongSelf.storageObservationRegistry.notifyObservers(about: change, in: strongSelf)
60+
}
61+
}
4662
}
4763

4864
extension Storage: StorageAware {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import Foundation
2+
3+
public final class StorageObservationRegistry<T: StorageAware> {
4+
public typealias Observation = (T, StorageChange) -> Void
5+
private(set) var observations = [UUID: Observation]()
6+
7+
@discardableResult
8+
public func addObservation(_ observation: @escaping Observation) -> ObservationToken {
9+
let id = UUID()
10+
observations[id] = observation
11+
12+
return ObservationToken { [weak self] in
13+
self?.observations.removeValue(forKey: id)
14+
}
15+
}
16+
17+
public func removeObservation(token: ObservationToken) {
18+
token.cancel()
19+
}
20+
21+
public func removeAllObservations() {
22+
observations.removeAll()
23+
}
24+
25+
func notifyObservers(about change: StorageChange, in storage: T) {
26+
observations.values.forEach { closure in
27+
closure(storage, change)
28+
}
29+
}
30+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
public enum StorageChange: Equatable {
2+
case add(key: String)
3+
case remove(key: String)
4+
case removeAll
5+
case removeExpired
6+
}
7+
8+
public func == (lhs: StorageChange, rhs: StorageChange) -> Bool {
9+
switch (lhs, rhs) {
10+
case (.add(let key1), .add(let key2)), (.remove(let key1), .remove(let key2)):
11+
return key1 == key2
12+
case (.removeAll, .removeAll), (.removeExpired, .removeExpired):
13+
return true
14+
default:
15+
return false
16+
}
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import XCTest
2+
@testable import Cache
3+
4+
final class ObservationTokenTests: XCTestCase {
5+
func testCancel() {
6+
var cancelled = false
7+
8+
let token = ObservationToken {
9+
cancelled = true
10+
}
11+
12+
token.cancel()
13+
XCTAssertTrue(cancelled)
14+
}
15+
}

Tests/iOS/Tests/Storage/HybridStorageTests.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,28 @@ final class HybridStorageTests: XCTestCase {
156156
XCTAssertNotNil(try? storage.object(forKey: key2))
157157
}
158158
}
159+
160+
func testAddObservations() throws {
161+
var changes = [StorageChange]()
162+
163+
storage.storageObservationRegistry.addObservation { storage, change in
164+
changes.append(change)
165+
}
166+
167+
try storage.setObject(testObject, forKey: "user1")
168+
try storage.setObject(testObject, forKey: "user2")
169+
try storage.removeObject(forKey: "user1")
170+
try storage.removeExpiredObjects()
171+
try storage.removeAll()
172+
173+
let expectedChanges: [StorageChange] = [
174+
.add(key: "user1"),
175+
.add(key: "user2"),
176+
.remove(key: "user1"),
177+
.removeExpired,
178+
.removeAll
179+
]
180+
181+
XCTAssertEqual(changes, expectedChanges)
182+
}
159183
}

0 commit comments

Comments
 (0)