Skip to content

Commit 2a18a46

Browse files
committed
Add observation registry
1 parent a7e40e7 commit 2a18a46

File tree

4 files changed

+100
-39
lines changed

4 files changed

+100
-39
lines changed

Source/Shared/Storage/HybridStorage.swift

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
11
import Foundation
22

3+
protocol StorageChangeNotifier {
4+
func notifyObservers(about change: StorageChange)
5+
}
6+
7+
struct KeyChangeNotifier<T> {
8+
9+
10+
func notifyObserver(about change: KeyChange<T>, where closure: ((String) -> Bool)) {
11+
12+
}
13+
}
14+
15+
316
/// Use both memory and disk storage. Try on memory first.
417
public final class HybridStorage<T> {
518
public let memoryStorage: MemoryStorage<T>
619
public let diskStorage: DiskStorage<T>
7-
public let storageObservationRegistry = StorageObservationRegistry<HybridStorage>()
8-
public let keyObservationRegistry = KeyObservationRegistry<HybridStorage>()
20+
let storageObservationRegistry = ObservationRegistry<StorageChange>()
21+
let keyObservationRegistry = ObservationRegistry<KeyChange<T>>()
22+
23+
var onKeyChange: ((KeyChange<T>, ((String) -> Bool)) -> Void)?
924

1025
public init(memoryStorage: MemoryStorage<T>, diskStorage: DiskStorage<T>) {
1126
self.memoryStorage = memoryStorage
@@ -17,7 +32,7 @@ public final class HybridStorage<T> {
1732
}
1833

1934
private func handleRemovedObject(at path: String) {
20-
keyObservationRegistry.notifyObserver(about: .remove, in: self) { key in
35+
keyObservationRegistry.notifyObserver(about: .remove) { key in
2136
let fileName = diskStorage.makeFileName(for: key)
2237
return path.contains(fileName)
2338
}
@@ -40,7 +55,7 @@ extension HybridStorage: StorageAware {
4055
memoryStorage.removeObject(forKey: key)
4156
try diskStorage.removeObject(forKey: key)
4257

43-
storageObservationRegistry.notifyObservers(about: .remove(key: key), in: self)
58+
storageObservationRegistry.notifyAllObservers(about: .remove(key: key))
4459
}
4560

4661
public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
@@ -55,25 +70,25 @@ extension HybridStorage: StorageAware {
5570

5671

5772
if let change = keyChange {
58-
keyObservationRegistry.notifyObserver(forKey: key, about: change, in: self)
73+
keyObservationRegistry.notifyObserver(forKey: key, about: change)
5974
}
6075

61-
storageObservationRegistry.notifyObservers(about: .add(key: key), in: self)
76+
storageObservationRegistry.notifyAllObservers(about: .add(key: key))
6277
}
6378

6479
public func removeAll() throws {
6580
memoryStorage.removeAll()
6681
try diskStorage.removeAll()
6782

68-
storageObservationRegistry.notifyObservers(about: .removeAll, in: self)
69-
keyObservationRegistry.notifyAllObservers(about: .remove, in: self)
83+
storageObservationRegistry.notifyAllObservers(about: .removeAll)
84+
keyObservationRegistry.notifyAllObservers(about: .remove)
7085
}
7186

7287
public func removeExpiredObjects() throws {
7388
memoryStorage.removeExpiredObjects()
7489
try diskStorage.removeExpiredObjects()
7590

76-
storageObservationRegistry.notifyObservers(about: .removeExpired, in: self)
91+
storageObservationRegistry.notifyAllObservers(about: .removeExpired)
7792
}
7893
}
7994

Source/Shared/Storage/KeyObservationRegistry.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Foundation
33
public final class KeyObservationRegistry<Storage: StorageAware> {
44
public typealias Observation = (Storage, KeyChange<Storage.T>) -> Void
55
private(set) var observations = [String: Observation]()
6+
var onNewKey: ((String) -> Void)?
67

78
public var isEmpty: Bool {
89
return observations.isEmpty
@@ -11,6 +12,7 @@ public final class KeyObservationRegistry<Storage: StorageAware> {
1112
@discardableResult
1213
public func addObservation(_ observation: @escaping Observation, forKey key: String) -> ObservationToken {
1314
observations[key] = observation
15+
onNewKey?(key)
1416

1517
return ObservationToken { [weak self] in
1618
self?.observations.removeValue(forKey: key)
@@ -47,6 +49,13 @@ public final class KeyObservationRegistry<Storage: StorageAware> {
4749
}
4850
}
4951

52+
// MARK: - Notification
53+
54+
public struct KeyNotification<Storage: StorageAware> {
55+
public let change: KeyChange<Storage.T>
56+
public let storage: Storage
57+
}
58+
5059
// MARK: - KeyChange
5160

5261
public enum KeyChange<T> {
@@ -66,3 +75,47 @@ extension KeyChange: Equatable where T: Equatable {
6675
}
6776
}
6877
}
78+
79+
80+
81+
82+
83+
final class ObservationRegistry<Change> {
84+
typealias Observation = (Change) -> Void
85+
private(set) var observations = [String: Observation]()
86+
87+
var isEmpty: Bool {
88+
return observations.isEmpty
89+
}
90+
91+
@discardableResult
92+
func addObservation(forKey key: String = UUID().uuidString, _ observation: @escaping Observation) -> ObservationToken {
93+
observations[key] = observation
94+
return ObservationToken { [weak self] in
95+
self?.observations.removeValue(forKey: key)
96+
}
97+
}
98+
99+
func removeObservation(forKey key: String) {
100+
observations.removeValue(forKey: key)
101+
}
102+
103+
func removeAllObservations() {
104+
observations.removeAll()
105+
}
106+
107+
func notifyObserver(forKey key: String, about change: Change) {
108+
observations[key]?(change)
109+
}
110+
111+
func notifyObserver(about change: Change, where closure: ((String) -> Bool)) {
112+
let observation = observations.first { key, value in closure(key) }?.value
113+
observation?(change)
114+
}
115+
116+
func notifyAllObservers(about change: Change) {
117+
observations.values.forEach { closure in
118+
closure(change)
119+
}
120+
}
121+
}

Source/Shared/Storage/Storage.swift

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import Dispatch
55
/// Synchronous by default. Use `async` for asynchronous operations.
66
public final class Storage<T> {
77
/// Used for sync operations
8-
let syncStorage: SyncStorage<T>
9-
let asyncStorage: AsyncStorage<T>
8+
private let syncStorage: SyncStorage<T>
9+
private let asyncStorage: AsyncStorage<T>
10+
private let hybridStorage: HybridStorage<T>
1011

1112
public let storageObservationRegistry = StorageObservationRegistry<Storage>()
1213
public let keyObservationRegistry = KeyObservationRegistry<Storage>()
@@ -20,45 +21,41 @@ public final class Storage<T> {
2021
public convenience init(diskConfig: DiskConfig, memoryConfig: MemoryConfig, transformer: Transformer<T>) throws {
2122
let disk = try DiskStorage(config: diskConfig, transformer: transformer)
2223
let memory = MemoryStorage<T>(config: memoryConfig)
23-
2424
let hybridStorage = HybridStorage(memoryStorage: memory, diskStorage: disk)
25-
let syncStorage = SyncStorage(
26-
storage: hybridStorage,
27-
serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue")
28-
)
29-
let asyncStorage = AsyncStorage(
30-
storage: hybridStorage,
31-
serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue")
32-
)
33-
34-
self.init(syncStorage: syncStorage, asyncStorage: asyncStorage)
25+
self.init(hybridStorage: hybridStorage)
3526
}
3627

3728
/// Initialise with sync and async storages
3829
///
3930
/// - Parameter syncStorage: Synchronous storage
4031
/// - Paraeter: asyncStorage: Asynchronous storage
41-
public required init(syncStorage: SyncStorage<T>, asyncStorage: AsyncStorage<T>) {
42-
self.syncStorage = syncStorage
43-
self.asyncStorage = asyncStorage
44-
subscribeToChanges()
32+
public init(hybridStorage: HybridStorage<T>) {
33+
self.hybridStorage = hybridStorage
34+
self.syncStorage = SyncStorage(
35+
storage: hybridStorage,
36+
serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue")
37+
)
38+
self.asyncStorage = AsyncStorage(
39+
storage: hybridStorage,
40+
serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue")
41+
)
42+
subscribeToChanges(in: hybridStorage)
4543
}
4644

4745
/// Used for async operations
4846
public lazy var async = self.asyncStorage
4947

50-
private func subscribeToChanges() {
51-
subscribeToChanges(in: syncStorage.innerStorage)
52-
if syncStorage.innerStorage !== asyncStorage.innerStorage {
53-
subscribeToChanges(in: asyncStorage.innerStorage)
54-
}
55-
}
56-
5748
private func subscribeToChanges(in storage: HybridStorage<T>) {
5849
storage.storageObservationRegistry.addObservation { [weak self] _, change in
5950
guard let strongSelf = self else { return }
6051
strongSelf.storageObservationRegistry.notifyObservers(about: change, in: strongSelf)
6152
}
53+
keyObservationRegistry.onNewKey = { [weak self] key in
54+
guard let strongSelf = self else { return }
55+
storage.keyObservationRegistry.addObservation({ _, change in
56+
strongSelf.keyObservationRegistry.notifyObserver(forKey: key, about: change, in: strongSelf)
57+
}, forKey: key)
58+
}
6259
}
6360
}
6461

@@ -86,10 +83,6 @@ extension Storage: StorageAware {
8683

8784
public extension Storage {
8885
func transform<U>(transformer: Transformer<U>) -> Storage<U> {
89-
let storage = Storage<U>(
90-
syncStorage: syncStorage.transform(transformer: transformer),
91-
asyncStorage: asyncStorage.transform(transformer: transformer)
92-
)
93-
return storage
86+
return Storage<U>(hybridStorage: hybridStorage.transform(transformer: transformer))
9487
}
9588
}

Source/Shared/Storage/StorageObservationRegistry.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Foundation
22

33
public final class StorageObservationRegistry<Storage: StorageAware> {
44
public typealias Observation = (Storage, StorageChange) -> Void
5-
private(set) var observations = [UUID: Observation]()
5+
private(set) var observations = [UUID: Observation]()
66

77
public var isEmpty: Bool {
88
return observations.isEmpty

0 commit comments

Comments
 (0)