From ecfb83bafa6dda01cafebdd6439b3fbc3a358baa Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Thu, 25 May 2023 11:30:43 +0300 Subject: [PATCH] feat: add TILogging module and TINetworking error logging --- CHANGELOG.md | 2 + Package.swift | 10 ++- Plugins/TISwiftLintPlugin/plugin.swift | 2 +- .../DefaultEncryptedTokenKeyStorage.swift | 4 +- .../DefaultEncryptedTokenStorage.swift | 4 +- .../SingleValueAuthKeychainStorage.swift | 4 +- .../Sources/AnySingleValueStorage.swift | 64 ++++++++++++++++++ ...AppInstallLifetimeSingleValueStorage.swift | 2 - .../BaseSingleValueDefaultsStorage.swift | 4 +- .../Sources/BaseSingleValueStorage.swift | 12 ++-- .../Sources/StringValueDefaultsStorage.swift | 2 +- .../BaseSingleValueKeychainStorage.swift | 4 +- .../StringValueKeychainStorage.swift | 4 +- .../Sources/DefaultOSLogErrorLogger.swift | 44 ++++++++++++ .../Sources/ErrorLogger.swift | 6 +- TILogging/TILogging.podspec | 15 +++++ .../Operations/CALayerDrawingOperation.swift | 2 +- .../DefaultFingerprintsProvider.swift | 67 ++++++++++++++----- .../DefaultFingerprintsSettingsStorage.swift | 21 ++---- .../FingerprintsProvider.swift | 2 +- ...Storage.swift => TINetworkingLogger.swift} | 12 ++-- project-scripts/ordered_modules_list.txt | 1 + 22 files changed, 221 insertions(+), 67 deletions(-) create mode 100644 TIFoundationUtils/DataStorage/Sources/AnySingleValueStorage.swift rename {TIKeychainUtils/Sources/AppInstallKeychainSingleValueStorage => TIFoundationUtils/DataStorage/Sources}/AppInstallLifetimeSingleValueStorage.swift (99%) create mode 100644 TILogging/Sources/DefaultOSLogErrorLogger.swift rename TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/FingerprintsSecureStorage.swift => TILogging/Sources/ErrorLogger.swift (91%) create mode 100644 TILogging/TILogging.podspec rename TINetworking/Sources/{Alamofire/FingerprintsTrustEvaluation/FingerprintsSettingsStorage.swift => TINetworkingLogger.swift} (81%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0960439f..91b67ce3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ ### 1.45.0 - **Added**: `SingleValueStorage` implementations + `AppInstallLifetimeSingleValueStorage` for automatically removing keychain items on app reinstall. +- **Added**: `TILogging` with error logging types - **Update**: `DefaultRecoverableJsonNetworkService` supports iOS 12. +- **Update**: `DefaultFingerprintsProvider` now uses `SingleValueStorage` ### 1.44.0 diff --git a/Package.swift b/Package.swift index f63ae3bf..3b206f7c 100644 --- a/Package.swift +++ b/Package.swift @@ -68,7 +68,12 @@ let package = Package( // MARK: - Utils .target(name: "TISwiftUtils", path: "TISwiftUtils/Sources"), - .target(name: "TIFoundationUtils", dependencies: ["TISwiftUtils"], path: "TIFoundationUtils", exclude: ["TIFoundationUtils.app"]), + + .target(name: "TIFoundationUtils", + dependencies: ["TISwiftUtils"], + path: "TIFoundationUtils", + exclude: ["TIFoundationUtils.app"], + plugins: [.plugin(name: "TISwiftLintPlugin")]), .target(name: "TIKeychainUtils", dependencies: ["TIFoundationUtils", "KeychainAccess"], @@ -79,11 +84,12 @@ let package = Package( .target(name: "TITableKitUtils", dependencies: ["TIUIElements", "TableKit"], path: "TITableKitUtils/Sources"), .target(name: "TIDeeplink", dependencies: ["TIFoundationUtils"], path: "TIDeeplink", exclude: ["TIDeeplink.app"]), .target(name: "TIDeveloperUtils", dependencies: ["TISwiftUtils", "TIUIKitCore", "TIUIElements"], path: "TIDeveloperUtils/Sources"), + .target(name: "TILogging", path: "TILogging/Sources", plugins: ["TISwiftLintPlugin"]), // MARK: - Networking .target(name: "TINetworking", - dependencies: ["TIFoundationUtils", "Alamofire"], + dependencies: ["TIFoundationUtils", "Alamofire", "TILogging"], path: "TINetworking/Sources", plugins: [.plugin(name: "TISwiftLintPlugin")]), diff --git a/Plugins/TISwiftLintPlugin/plugin.swift b/Plugins/TISwiftLintPlugin/plugin.swift index 5a2d4188..be711adf 100644 --- a/Plugins/TISwiftLintPlugin/plugin.swift +++ b/Plugins/TISwiftLintPlugin/plugin.swift @@ -30,7 +30,7 @@ struct SwiftLintPlugin: BuildToolPlugin { let swiftlintExecutablePath = try context.tool(named: "swiftlint").path return [ - .prebuildCommand(displayName: "SwiftLint linting...", + .prebuildCommand(displayName: "SwiftLint linting \(target.name)...", executable: swiftlintScriptPath, arguments: [ swiftlintExecutablePath, diff --git a/TIAuth/Sources/TokenStorage/DefaultEncryptedTokenKeyStorage.swift b/TIAuth/Sources/TokenStorage/DefaultEncryptedTokenKeyStorage.swift index 58a3d4dc..9b23e6bd 100644 --- a/TIAuth/Sources/TokenStorage/DefaultEncryptedTokenKeyStorage.swift +++ b/TIAuth/Sources/TokenStorage/DefaultEncryptedTokenKeyStorage.swift @@ -55,7 +55,7 @@ open class DefaultEncryptedTokenKeyStorage: SingleValueAuthKeychainStorage } } - let setValueClosure: SetValueClosure = { keychain, value, storageKey in + let storeValueClosure: StoreValueClosure = { keychain, value, storageKey in do { return .success(try keychain.set(value, key: storageKey.rawValue)) } catch { @@ -67,6 +67,6 @@ open class DefaultEncryptedTokenKeyStorage: SingleValueAuthKeychainStorage settingsStorage: settingsStorage, storageKey: encryptedTokenKeyStorageKey, getValueClosure: getValueClosure, - setValueClosure: setValueClosure) + storeValueClosure: storeValueClosure) } } diff --git a/TIAuth/Sources/TokenStorage/DefaultEncryptedTokenStorage.swift b/TIAuth/Sources/TokenStorage/DefaultEncryptedTokenStorage.swift index ab8ebbab..95a90e16 100644 --- a/TIAuth/Sources/TokenStorage/DefaultEncryptedTokenStorage.swift +++ b/TIAuth/Sources/TokenStorage/DefaultEncryptedTokenStorage.swift @@ -51,7 +51,7 @@ open class DefaultEncryptedTokenStorage: SingleValueAuthKeychainStorage: BaseSingleValueKeychainSto settingsStorage: AuthSettingsStorage = DefaultAuthSettingsStorage(), storageKey: StorageKey, getValueClosure: @escaping GetValueClosure, - setValueClosure: @escaping SetValueClosure) { + storeValueClosure: @escaping StoreValueClosure) { self.settingsStorage = settingsStorage super.init(keychain: keychain, storageKey: storageKey, getValueClosure: getValueClosure, - setValueClosure: setValueClosure) + storeValueClosure: storeValueClosure) } // MARK: - SingleValueStorage diff --git a/TIFoundationUtils/DataStorage/Sources/AnySingleValueStorage.swift b/TIFoundationUtils/DataStorage/Sources/AnySingleValueStorage.swift new file mode 100644 index 00000000..81a2a197 --- /dev/null +++ b/TIFoundationUtils/DataStorage/Sources/AnySingleValueStorage.swift @@ -0,0 +1,64 @@ +// +// Copyright (c) 2023 Touch Instinct +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the Software), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +public struct AnySingleValueStorage: SingleValueStorage { + public typealias HasValueClosure = () -> Bool + public typealias GetValueClosure = () -> Result + public typealias StoreValueClosure = (ValueType) -> Result + public typealias DeleteValueClosure = () -> Result + + private let hasValueClosure: HasValueClosure + private let deleteValueClosure: DeleteValueClosure + private let getValueClosure: GetValueClosure + private let storeValueClosure: StoreValueClosure + + public init(storage: Storage) + where Storage.ValueType == ValueType, Storage.ErrorType == ErrorType { + + self.hasValueClosure = storage.hasStoredValue + self.deleteValueClosure = storage.deleteValue + self.getValueClosure = storage.getValue + self.storeValueClosure = storage.store + } + + public func hasStoredValue() -> Bool { + hasValueClosure() + } + + public func store(value: ValueType) -> Result { + storeValueClosure(value) + } + + public func getValue() -> Result { + getValueClosure() + } + + public func deleteValue() -> Result { + deleteValueClosure() + } +} + +public extension SingleValueStorage { + func eraseToAnySingleValueStorate() -> AnySingleValueStorage { + AnySingleValueStorage(storage: self) + } +} diff --git a/TIKeychainUtils/Sources/AppInstallKeychainSingleValueStorage/AppInstallLifetimeSingleValueStorage.swift b/TIFoundationUtils/DataStorage/Sources/AppInstallLifetimeSingleValueStorage.swift similarity index 99% rename from TIKeychainUtils/Sources/AppInstallKeychainSingleValueStorage/AppInstallLifetimeSingleValueStorage.swift rename to TIFoundationUtils/DataStorage/Sources/AppInstallLifetimeSingleValueStorage.swift index eb8fca53..239ce00f 100644 --- a/TIKeychainUtils/Sources/AppInstallKeychainSingleValueStorage/AppInstallLifetimeSingleValueStorage.swift +++ b/TIFoundationUtils/DataStorage/Sources/AppInstallLifetimeSingleValueStorage.swift @@ -20,8 +20,6 @@ // THE SOFTWARE. // -import TIFoundationUtils - open class AppInstallLifetimeSingleValueStorage: SingleValueStorage where Storage.ErrorType == StorageError { diff --git a/TIFoundationUtils/DataStorage/Sources/BaseSingleValueDefaultsStorage.swift b/TIFoundationUtils/DataStorage/Sources/BaseSingleValueDefaultsStorage.swift index b1b14a95..08e1dccd 100644 --- a/TIFoundationUtils/DataStorage/Sources/BaseSingleValueDefaultsStorage.swift +++ b/TIFoundationUtils/DataStorage/Sources/BaseSingleValueDefaultsStorage.swift @@ -26,13 +26,13 @@ open class BaseSingleValueDefaultsStorage: BaseSingleValueStorage, getValueClosure: @escaping GetValueClosure, - setValueClosure: @escaping SetValueClosure) { + storeValueClosure: @escaping StoreValueClosure) { super.init(storage: defaults, storageKey: storageKey, hasValueClosure: { .success($0.object(forKey: $1.rawValue) != nil) }, deleteValueClosure: { .success($0.removeObject(forKey: $1.rawValue)) }, getValueClosure: getValueClosure, - setValueClosure: setValueClosure) + storeValueClosure: storeValueClosure) } } diff --git a/TIFoundationUtils/DataStorage/Sources/BaseSingleValueStorage.swift b/TIFoundationUtils/DataStorage/Sources/BaseSingleValueStorage.swift index eed87d9c..73a2119d 100644 --- a/TIFoundationUtils/DataStorage/Sources/BaseSingleValueStorage.swift +++ b/TIFoundationUtils/DataStorage/Sources/BaseSingleValueStorage.swift @@ -23,7 +23,7 @@ open class BaseSingleValueStorage: SingleValueStorage { public typealias HasValueClosure = (StorageType, StorageKey) -> Result public typealias GetValueClosure = (StorageType, StorageKey) -> Result - public typealias SetValueClosure = (StorageType, ValueType, StorageKey) -> Result + public typealias StoreValueClosure = (StorageType, ValueType, StorageKey) -> Result public typealias DeleteValueClosure = (StorageType, StorageKey) -> Result public let storage: StorageType @@ -31,21 +31,21 @@ open class BaseSingleValueStorage: SingleValueStorage { public let hasValueClosure: HasValueClosure public let deleteValueClosure: DeleteValueClosure public let getValueClosure: GetValueClosure - public let setValueClosure: SetValueClosure + public let storeValueClosure: StoreValueClosure public init(storage: StorageType, storageKey: StorageKey, hasValueClosure: @escaping HasValueClosure, deleteValueClosure: @escaping DeleteValueClosure, getValueClosure: @escaping GetValueClosure, - setValueClosure: @escaping SetValueClosure) { + storeValueClosure: @escaping StoreValueClosure) { self.storage = storage self.storageKey = storageKey self.hasValueClosure = hasValueClosure self.deleteValueClosure = deleteValueClosure self.getValueClosure = getValueClosure - self.setValueClosure = setValueClosure + self.storeValueClosure = storeValueClosure } // MARK: - SingleValueStorage @@ -55,14 +55,14 @@ open class BaseSingleValueStorage: SingleValueStorage { } open func store(value: ValueType) -> Result { - setValueClosure(storage, value, storageKey) + storeValueClosure(storage, value, storageKey) } open func getValue() -> Result { getValueClosure(storage, storageKey) } - public func deleteValue() -> Result { + open func deleteValue() -> Result { deleteValueClosure(storage, storageKey) } } diff --git a/TIFoundationUtils/DataStorage/Sources/StringValueDefaultsStorage.swift b/TIFoundationUtils/DataStorage/Sources/StringValueDefaultsStorage.swift index 46e4ac45..9bc2c4dc 100644 --- a/TIFoundationUtils/DataStorage/Sources/StringValueDefaultsStorage.swift +++ b/TIFoundationUtils/DataStorage/Sources/StringValueDefaultsStorage.swift @@ -35,6 +35,6 @@ public final class StringValueDefaultsStorage: BaseSingleValueDefaultsStorage: BaseSingleValueStorage, getValueClosure: @escaping GetValueClosure, - setValueClosure: @escaping SetValueClosure) { + storeValueClosure: @escaping StoreValueClosure) { let hasValueClosure: HasValueClosure = { keychain, storageKey in Result { try keychain.contains(storageKey.rawValue) } @@ -44,6 +44,6 @@ open class BaseSingleValueKeychainStorage: BaseSingleValueStorage] { get set } +import Foundation + +public protocol ErrorLogger { + func log(error: Error, file: StaticString, line: Int) } diff --git a/TILogging/TILogging.podspec b/TILogging/TILogging.podspec new file mode 100644 index 00000000..d47e273a --- /dev/null +++ b/TILogging/TILogging.podspec @@ -0,0 +1,15 @@ +Pod::Spec.new do |s| + s.name = 'TILogging' + s.version = '1.45.0' + s.summary = 'Logging for TI libraries.' + s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name + s.license = { :type => 'MIT', :file => 'LICENSE' } + s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' } + s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s } + + s.ios.deployment_target = '11.0' + s.swift_versions = ['5.7'] + + s.source_files = s.name + '/Sources/**/*' + +end diff --git a/TIMapUtils/Sources/Drawing/Operations/CALayerDrawingOperation.swift b/TIMapUtils/Sources/Drawing/Operations/CALayerDrawingOperation.swift index 49200b0f..3ea07ff9 100644 --- a/TIMapUtils/Sources/Drawing/Operations/CALayerDrawingOperation.swift +++ b/TIMapUtils/Sources/Drawing/Operations/CALayerDrawingOperation.swift @@ -45,6 +45,6 @@ public struct CALayerDrawingOperation: DrawingOperation { context.concatenate(offsetTransform) layer.render(in: context) - offsetTransform.concatenating(offsetTransform.inverted()) + context.concatenate(offsetTransform.inverted()) } } diff --git a/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/DefaultFingerprintsProvider.swift b/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/DefaultFingerprintsProvider.swift index f4a61d45..cf21bda6 100644 --- a/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/DefaultFingerprintsProvider.swift +++ b/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/DefaultFingerprintsProvider.swift @@ -20,33 +20,64 @@ // THE SOFTWARE. // +import TIFoundationUtils +import TILogging + open class DefaultFingerprintsProvider: FingerprintsProvider { - public var secureStorage: FingerprintsSecureStorage - public var settingsStorage: FingerprintsSettingsStorage + public typealias FingerprintsMapping = [String: Set] - public init(secureStorage: FingerprintsSecureStorage, - settingsStorage: FingerprintsSettingsStorage, - bundledFingerprints: [String: Set]) { + public var secureStorage: AnySingleValueStorage + public var bundledFingerprints: FingerprintsMapping + public var errorLogger: ErrorLogger - self.secureStorage = secureStorage - self.settingsStorage = settingsStorage + public init(secureStorage: Storage, + bundledFingerprints: FingerprintsMapping, + errorLogger: ErrorLogger = TINetworkingLogger(category: "Fingerprints")) + where Storage.ValueType == FingerprintsMapping, Storage.ErrorType == StorageError { - if settingsStorage.shouldResetFingerprints { - self.secureStorage.knownPins = bundledFingerprints - self.settingsStorage.shouldResetFingerprints = false - } else { - self.secureStorage.knownPins.merge(bundledFingerprints) { storedFingerprints, bundleFingerprints in - storedFingerprints.union(bundleFingerprints) + self.secureStorage = secureStorage.eraseToAnySingleValueStorate() + self.bundledFingerprints = bundledFingerprints + self.errorLogger = errorLogger + + let fingerprintsUpdateResult = secureStorage + .getValue() + .map { + $0.merging(bundledFingerprints) { storedFingerprints, bundleFingerprints in + storedFingerprints.union(bundleFingerprints) + } } + .flatMap { + secureStorage.store(value: $0) + } + + if case let .failure(storageError) = fingerprintsUpdateResult { + errorLogger.log(error: storageError, file: #file, line: #line) } } - public func fingerprints(forHost host: String) -> Set { - secureStorage.knownPins[host] ?? [] + open func fingerprints(forHost host: String) -> Set { + (try? secureStorage + .getValue() + .flatMapError { _ -> Result in + .success(bundledFingerprints) + } + .get())?[host] ?? [] } - public func add(fingerprints: [String], forHost host: String) { - let pinsForHost = (secureStorage.knownPins[host] ?? []).union(fingerprints) - secureStorage.knownPins.updateValue(pinsForHost, forKey: host) + open func add(fingerprints: Set, forHost host: String) { + let fingerprintsUpdateResult = secureStorage + .getValue() + .map { + $0.merging([host: fingerprints]) { storedFingerprints, addedFingerprints in + storedFingerprints.union(addedFingerprints) + } + } + .flatMap { + secureStorage.store(value: $0) + } + + if case let .failure(storageError) = fingerprintsUpdateResult { + errorLogger.log(error: storageError, file: #file, line: #line) + } } } diff --git a/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/DefaultFingerprintsSettingsStorage.swift b/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/DefaultFingerprintsSettingsStorage.swift index 50d833ba..72f61591 100644 --- a/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/DefaultFingerprintsSettingsStorage.swift +++ b/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/DefaultFingerprintsSettingsStorage.swift @@ -23,29 +23,16 @@ import TIFoundationUtils import Foundation -open class DefaultFingerprintsSettingsStorage: FingerprintsSettingsStorage { +open class FingerprintsReinstallChecker: AppReinstallChecker { public enum Defaults { public static var shouldResetFingerprintsKey: StorageKey { .init(rawValue: "shouldResetFingerprints") } } - private let reinstallChecker: AppReinstallChecker + public override init(defaultsStorage: UserDefaults = .standard, + storageKey: StorageKey = Defaults.shouldResetFingerprintsKey) { - // MARK: - PinCodeSettingsStorage - - open var shouldResetFingerprints: Bool { - get { - reinstallChecker.isAppFirstRun - } - set { - reinstallChecker.isAppFirstRun = newValue - } - } - - public init(defaultsStorage: UserDefaults = .standard, - storageKey: StorageKey = Defaults.shouldResetFingerprintsKey) { - - self.reinstallChecker = AppReinstallChecker(defaultsStorage: defaultsStorage, storageKey: storageKey) + super.init(defaultsStorage: defaultsStorage, storageKey: storageKey) } } diff --git a/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/FingerprintsProvider.swift b/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/FingerprintsProvider.swift index 871f46de..906025cd 100644 --- a/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/FingerprintsProvider.swift +++ b/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/FingerprintsProvider.swift @@ -22,5 +22,5 @@ public protocol FingerprintsProvider { func fingerprints(forHost host: String) -> Set - func add(fingerprints: [String], forHost host: String) + func add(fingerprints: Set, forHost host: String) } diff --git a/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/FingerprintsSettingsStorage.swift b/TINetworking/Sources/TINetworkingLogger.swift similarity index 81% rename from TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/FingerprintsSettingsStorage.swift rename to TINetworking/Sources/TINetworkingLogger.swift index daf4909a..96366b48 100644 --- a/TINetworking/Sources/Alamofire/FingerprintsTrustEvaluation/FingerprintsSettingsStorage.swift +++ b/TINetworking/Sources/TINetworkingLogger.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2022 Touch Instinct +// Copyright (c) 2023 Touch Instinct // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the Software), to deal @@ -20,7 +20,11 @@ // THE SOFTWARE. // -public protocol FingerprintsSettingsStorage { - /// Should be true by default (on app first run) - var shouldResetFingerprints: Bool { get set } +import TILogging +import os + +public final class TINetworkingLogger: DefaultOSLogErrorLogger { + public init(category: String) { + super.init(log: OSLog(subsystem: "TINetworking", category: category)) + } } diff --git a/project-scripts/ordered_modules_list.txt b/project-scripts/ordered_modules_list.txt index e426fddf..49d3f59c 100644 --- a/project-scripts/ordered_modules_list.txt +++ b/project-scripts/ordered_modules_list.txt @@ -1,3 +1,4 @@ +TILogging TISwiftUtils TIPagination TIFoundationUtils