diff --git a/TIFoundationUtils/CodableKeyValueStorage/Sources/CodableKeyVlaueStorage+MigratingBackingStore.swift b/TIFoundationUtils/CodableKeyValueStorage/Sources/CodableKeyVlaueStorage+MigratingBackingStore.swift index 1786f9de..4d21b8c2 100644 --- a/TIFoundationUtils/CodableKeyValueStorage/Sources/CodableKeyVlaueStorage+MigratingBackingStore.swift +++ b/TIFoundationUtils/CodableKeyValueStorage/Sources/CodableKeyVlaueStorage+MigratingBackingStore.swift @@ -32,16 +32,7 @@ public extension MigratingBackingStore where MigratingStorages: CodableKeyValueS where StoreContent == Value? { let getClosure: GetClosure = { container in - // Storage container can't handle migration when source storage has value and target doesn't. - // This happens because of Decodable restrictions for Value in method `codableObject(forKey:decoder)` - // and absence of encoder. - try? container.codableObject(forKey: key, decoder: decoder) - .flatMap { - let _ = container.set(encodableObject: $0, forKey: key, encoder: encoder) - - return .success($0) - } - .get() + Self.getValue(from: container, forKey: key, decoder: decoder, encoder: encoder) } let setClosure: SetClosure = { container, value in @@ -59,20 +50,7 @@ public extension MigratingBackingStore where MigratingStorages: CodableKeyValueS where StoreContent == Value? { let getClosure: GetClosure = { container in - // Storage container can't handle migration when source storage has value and target doesn't. - // This happens because of Decodable restrictions for Value in method `codableObject(forKey:decoder)` - // and absence of encoder. - try? container.codableObject(forKey: key, decoder: decoder) - .flatMap { - let _ = container.set(encodableObject: $0, forKey: key, encoder: encoder) - - return .success($0) - } - .flatMapError { _ in - container.set(encodableObject: wrappedValue, forKey: key, encoder: encoder) - .flatMap { _ in .success(wrappedValue) } - } - .get() + Self.getValue(from: container, forKey: key, wrappedValue: wrappedValue, decoder: decoder, encoder: encoder) } let setClosure: SetClosure = { container, value in @@ -81,4 +59,30 @@ public extension MigratingBackingStore where MigratingStorages: CodableKeyValueS self.init(storageContainer: storageContainer, getClosure: getClosure, setClosure: setClosure) } + + static func getValue(from container: MigratingStorages, + forKey key: StorageKey, + wrappedValue: Value? = nil, + decoder: CodableKeyValueDecoder, + encoder: CodableKeyValueEncoder) -> StoreContent where StoreContent == Value? { + + // Storage container can't handle migration when source storage has value and target doesn't. + // This happens because of Decodable restrictions for Value in method `codableObject(forKey:decoder)` + // and absence of encoder. + try? container.codableObject(forKey: key, decoder: decoder) + .flatMap { + let _ = container.set(encodableObject: $0, forKey: key, encoder: encoder) + + return .success($0) + } + .flatMapError { + guard let wrappedValue else { + return .failure($0) + } + + return container.set(encodableObject: wrappedValue, forKey: key, encoder: encoder) + .flatMap { _ in .success(wrappedValue) } + } + .get() + } } diff --git a/TIFoundationUtils/DataStorage/Sources/SingleValueStorage/Implementations/MigratableStorage/Container/BaseCodableMigratingStorageContainer.swift b/TIFoundationUtils/DataStorage/Sources/SingleValueStorage/Implementations/MigratableStorage/Container/BaseCodableMigratingStorageContainer.swift index 741ed211..d8e1b446 100644 --- a/TIFoundationUtils/DataStorage/Sources/SingleValueStorage/Implementations/MigratableStorage/Container/BaseCodableMigratingStorageContainer.swift +++ b/TIFoundationUtils/DataStorage/Sources/SingleValueStorage/Implementations/MigratableStorage/Container/BaseCodableMigratingStorageContainer.swift @@ -44,16 +44,14 @@ where SourceStorage: CodableKeyValueStorage, TargetStorage: CodableKeyValueStora targetStorage.codableObject(forKey: key, decoder: decoder) .flatMap { - if case let .failure(error) = removeSourceValue(forKey: key), !error.isValueNotFound { - errorLogger.log(error: error, file: #file, line: #line) + if case let .failure(error) = removeSourceValue(forKey: key) { + logErrorIfNeeded(error, file: #file, line: #line) } return .success($0) } .flatMapError { - if !$0.isValueNotFound { - errorLogger.log(error: $0, file: #file, line: #line) - } + logErrorIfNeeded($0, file: #file, line: #line) return sourceStorage.codableObject(forKey: key, decoder: decoder) } @@ -63,33 +61,46 @@ where SourceStorage: CodableKeyValueStorage, TargetStorage: CodableKeyValueStora forKey key: StorageKey, encoder: CodableKeyValueEncoder) -> Result { - if case let .failure(error) = removeSourceValue(forKey: key), !error.isValueNotFound { - errorLogger.log(error: error, file: #file, line: #line) + if case let .failure(error) = removeSourceValue(forKey: key) { + logErrorIfNeeded(error, file: #file, line: #line) } return targetStorage.set(encodableObject: encodableObject, forKey: key, encoder: encoder) } open func removeCodableValue(forKey key: StorageKey) -> Result { - targetStorage.removeCodableValue(forKey: key) - .flatMap { value in - sourceStorage.removeCodableValue(forKey: key) - .flatMapError { _ in .success(value) } - } - .flatMapError { - if !$0.isValueNotFound { - errorLogger.log(error: $0, file: #file, line: #line) - } + switch (targetStorage.removeCodableValue(forKey: key), sourceStorage.removeCodableValue(forKey: key)) { + case (.success, .success): + return .success(()) - return removeSourceValue(forKey: key) - } + case let (.success, .failure(error)): + logErrorIfNeeded(error, file: #file, line: #line) + + return .success(()) + + case let (.failure(error), .success): + return .failure(error) + + case let (.failure(targetError), .failure(sourceError)): + logErrorIfNeeded(sourceError, file: #file, line: #line) + + return .failure(targetError) + } } open func hasCodableValue(forKey key: StorageKey) -> Result { - targetStorage.hasCodableValue(forKey: key) - .flatMapError { _ in - sourceStorage.hasCodableValue(forKey: key) - } + switch (targetStorage.hasCodableValue(forKey: key), sourceStorage.hasCodableValue(forKey: key)) { + case let (.success(value), _): + return .success(value) + + case let (.failure, .success(value)): + return .success(value) + + case let (.failure(targetError), .failure(sourceError)): + logErrorIfNeeded(sourceError, file: #file, line: #line) + + return .failure(targetError) + } } // MARK: - Open methods @@ -98,4 +109,14 @@ where SourceStorage: CodableKeyValueStorage, TargetStorage: CodableKeyValueStora // override in subclasses .failure(.valueNotFound) } + + // MARK: - Private methods + + private func logErrorIfNeeded(_ error: StorageError, file: StaticString, line: Int) { + guard !error.isValueNotFound else { + return + } + + errorLogger.log(error: error, file: file, line: line) + } } diff --git a/TIFoundationUtils/DataStorage/Sources/SingleValueStorage/Implementations/MigratableStorage/MigratingSingleValueStorage.swift b/TIFoundationUtils/DataStorage/Sources/SingleValueStorage/Implementations/MigratableStorage/MigratingSingleValueStorage.swift index 09b80ca8..89b25f72 100644 --- a/TIFoundationUtils/DataStorage/Sources/SingleValueStorage/Implementations/MigratableStorage/MigratingSingleValueStorage.swift +++ b/TIFoundationUtils/DataStorage/Sources/SingleValueStorage/Implementations/MigratableStorage/MigratingSingleValueStorage.swift @@ -36,7 +36,7 @@ where SourceStorage.ErrorType == TargetStorage.ErrorType, public init(sourceStorage: SourceStorage, targetStorage: TargetStorage, - errorLogger: ErrorLogger = TIFoundationLogger(category: "BaseMigratingSingleValueStorage")) { + errorLogger: ErrorLogger = TIFoundationLogger(category: "MigratingSingleValueStorage")) { self.sourceStorage = sourceStorage self.targetStorage = targetStorage @@ -50,8 +50,8 @@ where SourceStorage.ErrorType == TargetStorage.ErrorType, } public func store(value: ValueType) -> Result { - if case let .failure(error) = sourceStorage.deleteValue(), !error.isValueNotFound { - errorLogger.log(error: error, file: #file, line: #line) + if case let .failure(error) = sourceStorage.deleteValue() { + logErrorIfNeeded(error, file: #file, line: #line) } return targetStorage.store(value: value) @@ -60,8 +60,8 @@ where SourceStorage.ErrorType == TargetStorage.ErrorType, public func getValue() -> Result { targetStorage.getValue() .flatMap { - if case let .failure(error) = sourceStorage.deleteValue(), !error.isValueNotFound { - errorLogger.log(error: error, file: #file, line: #line) + if case let .failure(error) = sourceStorage.deleteValue() { + logErrorIfNeeded(error, file: #file, line: #line) } return .success($0) @@ -85,29 +85,33 @@ where SourceStorage.ErrorType == TargetStorage.ErrorType, return .success(()) case let (.success, .failure(error)): - if error.isValueNotFound { - return .success(()) - } + logErrorIfNeeded(error, file: #file, line: #line) - errorLogger.log(error: error, file: #file, line: #line) - - return .failure(error) + return .success(()) case let (.failure(error), .success): - if error.isValueNotFound { - return .success(()) - } - - errorLogger.log(error: error, file: #file, line: #line) - return .failure(error) - case let (.failure, .failure(error)): - return .failure(error) + case let (.failure(targetError), .failure(sourceError)): + logErrorIfNeeded(sourceError, file: #file, line: #line) + + return .failure(targetError) } } + + // MARK: - Private methods + + private func logErrorIfNeeded(_ error: StorageError, file: StaticString, line: Int) { + guard !error.isValueNotFound else { + return + } + + errorLogger.log(error: error, file: file, line: line) + } } +// MARK: - Factory method + public extension SingleValueStorage where ErrorType == StorageError { func migrating(to targetStorage: S) -> MigratingSingleValueStorage diff --git a/TIKeychainUtils/TIKeychainUtils.app/Contents/MacOS/TIKeychainUtils.playground/Pages/SingleValueStorage.xcplaygroundpage/Contents.swift b/TIKeychainUtils/TIKeychainUtils.app/Contents/MacOS/TIKeychainUtils.playground/Pages/SingleValueStorage.xcplaygroundpage/Contents.swift index 2648322f..af651031 100644 --- a/TIKeychainUtils/TIKeychainUtils.app/Contents/MacOS/TIKeychainUtils.playground/Pages/SingleValueStorage.xcplaygroundpage/Contents.swift +++ b/TIKeychainUtils/TIKeychainUtils.app/Contents/MacOS/TIKeychainUtils.playground/Pages/SingleValueStorage.xcplaygroundpage/Contents.swift @@ -118,7 +118,7 @@ case let .failure(storageError): /*: ### MigratingStorage - При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `BaseMigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать: + При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `MigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать: - source storage: хранилище с которого мигрируем - target storage: хранилище на которое мигрируем diff --git a/Tests/TIFoundationUtilsTests/MigratingSingleValueStorageTests.swift b/Tests/TIFoundationUtilsTests/MigratingSingleValueStorageTests.swift index 70532748..445e65f3 100644 --- a/Tests/TIFoundationUtilsTests/MigratingSingleValueStorageTests.swift +++ b/Tests/TIFoundationUtilsTests/MigratingSingleValueStorageTests.swift @@ -185,7 +185,7 @@ final class MigratingSingleValueStorageTests: XCTestCase { let _ = sourceRefreshTokenStorage.store(value: oldToken) - XCTAssertNoThrow(try refreshToken.deleteValue().get()) + XCTAssertThrowsError(try refreshToken.deleteValue().get()) XCTAssertNil(MockStorageLogger.defaultLogger.getError()) XCTAssertThrowsError(try sourceRefreshTokenStorage.getValue().get()) XCTAssertThrowsError(try targetRefreshTokenStorage.getValue().get()) diff --git a/docs/tikeychainutils/singlevaluestorage.md b/docs/tikeychainutils/singlevaluestorage.md index 2a5aed06..07d0518f 100644 --- a/docs/tikeychainutils/singlevaluestorage.md +++ b/docs/tikeychainutils/singlevaluestorage.md @@ -116,7 +116,7 @@ case let .failure(storageError): ### MigratingStorage - При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `BaseMigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать: + При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `MigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать: - source storage: хранилище с которого мигрируем - target storage: хранилище на которое мигрируем