HybridStorage.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import Foundation
  2. /// Use both memory and disk storage. Try on memory first.
  3. public final class HybridStorage<T> {
  4. public let memoryStorage: MemoryStorage<T>
  5. public let diskStorage: DiskStorage<T>
  6. private(set) var storageObservations = [UUID: (HybridStorage, StorageChange) -> Void]()
  7. private(set) var keyObservations = [String: (HybridStorage, KeyChange<T>) -> Void]()
  8. public init(memoryStorage: MemoryStorage<T>, diskStorage: DiskStorage<T>) {
  9. self.memoryStorage = memoryStorage
  10. self.diskStorage = diskStorage
  11. diskStorage.onRemove = { [weak self] path in
  12. self?.handleRemovedObject(at: path)
  13. }
  14. }
  15. private func handleRemovedObject(at path: String) {
  16. notifyObserver(about: .remove) { key in
  17. let fileName = diskStorage.makeFileName(for: key)
  18. return path.contains(fileName)
  19. }
  20. }
  21. }
  22. extension HybridStorage: StorageAware {
  23. public func entry(forKey key: String) throws -> Entry<T> {
  24. do {
  25. return try memoryStorage.entry(forKey: key)
  26. } catch {
  27. let entry = try diskStorage.entry(forKey: key)
  28. // set back to memoryStorage
  29. memoryStorage.setObject(entry.object, forKey: key, expiry: entry.expiry)
  30. return entry
  31. }
  32. }
  33. public func removeObject(forKey key: String) throws {
  34. memoryStorage.removeObject(forKey: key)
  35. try diskStorage.removeObject(forKey: key)
  36. notifyStorageObservers(about: .remove(key: key))
  37. }
  38. public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
  39. var keyChange: KeyChange<T>?
  40. if keyObservations[key] != nil {
  41. keyChange = .edit(before: try? self.object(forKey: key), after: object)
  42. }
  43. memoryStorage.setObject(object, forKey: key, expiry: expiry)
  44. try diskStorage.setObject(object, forKey: key, expiry: expiry)
  45. if let change = keyChange {
  46. notifyObserver(forKey: key, about: change)
  47. }
  48. notifyStorageObservers(about: .add(key: key))
  49. }
  50. public func removeAll() throws {
  51. memoryStorage.removeAll()
  52. try diskStorage.removeAll()
  53. notifyStorageObservers(about: .removeAll)
  54. notifyKeyObservers(about: .remove)
  55. }
  56. public func removeExpiredObjects() throws {
  57. memoryStorage.removeExpiredObjects()
  58. try diskStorage.removeExpiredObjects()
  59. notifyStorageObservers(about: .removeExpired)
  60. }
  61. }
  62. public extension HybridStorage {
  63. func transform<U>(transformer: Transformer<U>) -> HybridStorage<U> {
  64. let storage = HybridStorage<U>(
  65. memoryStorage: memoryStorage.transform(),
  66. diskStorage: diskStorage.transform(transformer: transformer)
  67. )
  68. return storage
  69. }
  70. }
  71. extension HybridStorage: StorageObservationRegistry {
  72. @discardableResult
  73. public func addStorageObserver<O: AnyObject>(
  74. _ observer: O,
  75. closure: @escaping (O, HybridStorage, StorageChange) -> Void
  76. ) -> ObservationToken {
  77. let id = UUID()
  78. storageObservations[id] = { [weak self, weak observer] storage, change in
  79. guard let observer = observer else {
  80. self?.storageObservations.removeValue(forKey: id)
  81. return
  82. }
  83. closure(observer, storage, change)
  84. }
  85. return ObservationToken { [weak self] in
  86. self?.storageObservations.removeValue(forKey: id)
  87. }
  88. }
  89. public func removeAllStorageObservers() {
  90. storageObservations.removeAll()
  91. }
  92. private func notifyStorageObservers(about change: StorageChange) {
  93. storageObservations.values.forEach { closure in
  94. closure(self, change)
  95. }
  96. }
  97. }
  98. extension HybridStorage: KeyObservationRegistry {
  99. @discardableResult
  100. public func addObserver<O: AnyObject>(
  101. _ observer: O,
  102. forKey key: String,
  103. closure: @escaping (O, HybridStorage, KeyChange<T>) -> Void
  104. ) -> ObservationToken {
  105. keyObservations[key] = { [weak self, weak observer] storage, change in
  106. guard let observer = observer else {
  107. self?.removeObserver(forKey: key)
  108. return
  109. }
  110. closure(observer, storage, change)
  111. }
  112. return ObservationToken { [weak self] in
  113. self?.keyObservations.removeValue(forKey: key)
  114. }
  115. }
  116. public func removeObserver(forKey key: String) {
  117. keyObservations.removeValue(forKey: key)
  118. }
  119. public func removeAllKeyObservers() {
  120. keyObservations.removeAll()
  121. }
  122. private func notifyObserver(forKey key: String, about change: KeyChange<T>) {
  123. keyObservations[key]?(self, change)
  124. }
  125. private func notifyObserver(about change: KeyChange<T>, whereKey closure: ((String) -> Bool)) {
  126. let observation = keyObservations.first { key, _ in closure(key) }?.value
  127. observation?(self, change)
  128. }
  129. private func notifyKeyObservers(about change: KeyChange<T>) {
  130. keyObservations.values.forEach { closure in
  131. closure(self, change)
  132. }
  133. }
  134. }