Skip to content

Commit

Permalink
Implement observations in HybridStorage
Browse files Browse the repository at this point in the history
  • Loading branch information
vadymmarkov committed Aug 2, 2018
1 parent e42cbc0 commit a73bec5
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 20 deletions.
8 changes: 8 additions & 0 deletions Cache.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@
D5A9D1BB211345D4005DBD3F /* Dictionary+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BA211345D4005DBD3F /* Dictionary+Extensions.swift */; };
D5A9D1BC211345D4005DBD3F /* Dictionary+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BA211345D4005DBD3F /* Dictionary+Extensions.swift */; };
D5A9D1BD211345D4005DBD3F /* Dictionary+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BA211345D4005DBD3F /* Dictionary+Extensions.swift */; };
D5A9D1BF21134776005DBD3F /* StoreChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BE21134776005DBD3F /* StoreChange.swift */; };
D5A9D1C021134776005DBD3F /* StoreChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BE21134776005DBD3F /* StoreChange.swift */; };
D5A9D1C121134776005DBD3F /* StoreChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9D1BE21134776005DBD3F /* StoreChange.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -230,6 +233,7 @@
D5A138C31EB29C2100881A20 /* NSImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSImage+Extensions.swift"; sourceTree = "<group>"; };
D5A9D1B621134547005DBD3F /* ObservationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservationToken.swift; sourceTree = "<group>"; };
D5A9D1BA211345D4005DBD3F /* Dictionary+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Extensions.swift"; sourceTree = "<group>"; };
D5A9D1BE21134776005DBD3F /* StoreChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreChange.swift; sourceTree = "<group>"; };
D5DC59E01C20593E003BD79B /* Cache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cache.framework; sourceTree = BUILT_PRODUCTS_DIR; };
EBAACA991FBC369300FA206E /* SimpleStorage.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SimpleStorage.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -358,6 +362,7 @@
D270147F20D10982003B45C7 /* Storage.swift */,
D270148320D10E76003B45C7 /* AsyncStorage.swift */,
D270148720D11040003B45C7 /* Storage+Transform.swift */,
D5A9D1BE21134776005DBD3F /* StoreChange.swift */,
);
path = Storage;
sourceTree = "<group>";
Expand Down Expand Up @@ -833,6 +838,7 @@
D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */,
D270148220D10982003B45C7 /* Storage.swift in Sources */,
D221E5C220D00DCC00BC940E /* Entry.swift in Sources */,
D5A9D1C121134776005DBD3F /* StoreChange.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -916,6 +922,7 @@
D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */,
D270148120D10982003B45C7 /* Storage.swift in Sources */,
D221E5C120D00DCC00BC940E /* Entry.swift in Sources */,
D5A9D1C021134776005DBD3F /* StoreChange.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -968,6 +975,7 @@
D2CF98611F694FFA00CE8F68 /* MemoryConfig.swift in Sources */,
D2CF98661F694FFA00CE8F68 /* ExpirationMode.swift in Sources */,
D221E5C020D00DCC00BC940E /* Entry.swift in Sources */,
D5A9D1BF21134776005DBD3F /* StoreChange.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
28 changes: 28 additions & 0 deletions Source/Shared/Storage/HybridStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ public class HybridStorage<T> {
public let memoryStorage: MemoryStorage<T>
public let diskStorage: DiskStorage<T>

private var observations = [UUID : (HybridStorage, StoreChange) -> Void]()

public init(memoryStorage: MemoryStorage<T>, diskStorage: DiskStorage<T>) {
self.memoryStorage = memoryStorage
self.diskStorage = diskStorage
Expand All @@ -26,21 +28,25 @@ extension HybridStorage: StorageAware {
public func removeObject(forKey key: String) throws {
memoryStorage.removeObject(forKey: key)
try diskStorage.removeObject(forKey: key)
notifyObservers(of: .singleDeletion)
}

public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
memoryStorage.setObject(object, forKey: key, expiry: expiry)
try diskStorage.setObject(object, forKey: key, expiry: expiry)
notifyObservers(of: .addition)
}

public func removeAll() throws {
memoryStorage.removeAll()
try diskStorage.removeAll()
notifyObservers(of: .allDeletion)
}

public func removeExpiredObjects() throws {
memoryStorage.removeExpiredObjects()
try diskStorage.removeExpiredObjects()
notifyObservers(of: .expiredDeletion)
}
}

Expand All @@ -54,3 +60,25 @@ public extension HybridStorage {
return storage
}
}

// MARK: - Observations

extension HybridStorage {
@discardableResult
func observeChanges(using closure: @escaping (HybridStorage, StoreChange) -> Void) -> ObservationToken {
let id = observations.insert(closure)

return ObservationToken { [weak self] in
self?.observations.removeValue(forKey: id)
}
}

private func notifyObservers(of change: StoreChange) {
observations.values.forEach { [weak self] closure in
guard let strongSelf = self else {
return
}
closure(strongSelf, change)
}
}
}
39 changes: 19 additions & 20 deletions Source/Shared/Storage/Storage.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import Foundation
import Dispatch

enum StoreChange {
case addition
case singleDeletion
case allDeletion
case expiredDeletion
}

/// Manage storage. Use memory storage if specified.
/// Synchronous by default. Use `async` for asynchronous operations.
public class Storage<T> {
Expand Down Expand Up @@ -41,7 +34,6 @@ public class Storage<T> {
storage: hybridStorage,
serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue")
)

let asyncStorage = AsyncStorage(
storage: hybridStorage,
serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue")
Expand All @@ -57,10 +49,29 @@ public class Storage<T> {
public required init(syncStorage: SyncStorage<T>, asyncStorage: AsyncStorage<T>) {
self.syncStorage = syncStorage
self.asyncStorage = asyncStorage
subscribeToChanges()
}

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

private func subscribeToChanges() {
subscribeToChanges(in: syncStorage.innerStorage)
if syncStorage !== asyncStorage {
subscribeToChanges(in: asyncStorage.innerStorage)
}
}

private func subscribeToChanges(in storage: HybridStorage<T>) {
storage.observeChanges { [weak self] _, change in
self?.observations.values.forEach { [weak self] closure in
guard let strongSelf = self else {
return
}
closure(strongSelf, change)
}
}
}
}

extension Storage: StorageAware {
Expand All @@ -74,26 +85,14 @@ extension Storage: StorageAware {

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

public func removeAll() throws {
try self.syncStorage.removeAll()
notifyObservers(of: .allDeletion)
}

public func removeExpiredObjects() throws {
try self.syncStorage.removeExpiredObjects()
notifyObservers(of: .expiredDeletion)
}

private func notifyObservers(of change: StoreChange) {
observations.values.forEach { [weak self] closure in
guard let strongSelf = self else {
return
}
closure(strongSelf, change)
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions Source/Shared/Storage/StoreChange.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public enum StoreChange {
case addition
case singleDeletion
case allDeletion
case expiredDeletion
}
4 changes: 4 additions & 0 deletions Tests/iOS/Tests/Storage/StorageTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,8 @@ final class StorageTests: XCTestCase {
let cachedObject = try! alienStorage.object(forKey: "person")
XCTAssertEqual(cachedObject.firstName, "John")
}

func testObserveAddition() {

}
}

0 comments on commit a73bec5

Please sign in to comment.