diff --git a/CHANGELOG.md b/CHANGELOG.md index 72db96a1..e0f572ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +### 1.2.0 +- **Add**: `TIKeychainUtils` - Set of helpers for Keychain classes. + ### 1.1.1 - **Fix**: `StatefullButton` propagation diff --git a/LeadKit.podspec b/LeadKit.podspec index 0899b713..6a9a6f1f 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "1.1.2" + s.version = "1.2.0" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/TouchInstinct/LeadKit" s.license = "Apache License, Version 2.0" diff --git a/Package.resolved b/Package.resolved index 7c8c1236..65ac8579 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,6 +1,15 @@ { "object": { "pins": [ + { + "package": "KeychainAccess", + "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess.git", + "state": { + "branch": null, + "revision": "84e546727d66f1adc5439debad16270d0fdd04e7", + "version": "4.2.2" + } + }, { "package": "TableKit", "repositoryURL": "https://github.com/maxsokolov/TableKit.git", diff --git a/Package.swift b/Package.swift index 6c5640d1..ef8ed654 100644 --- a/Package.swift +++ b/Package.swift @@ -11,18 +11,21 @@ let package = Package( .library(name: "TIUIKitCore", targets: ["TIUIKitCore"]), .library(name: "TISwiftUtils", targets: ["TISwiftUtils"]), .library(name: "TIFoundationUtils", targets: ["TIFoundationUtils"]), + .library(name: "TIKeychainUtils", targets: ["TIKeychainUtils"]), .library(name: "TIUIElements", targets: ["TIUIElements"]), .library(name: "TITableKitUtils", targets: ["TITableKitUtils"]), .library(name: "OTPSwiftView", targets: ["OTPSwiftView"]) ], dependencies: [ - .package(url: "https://github.com/maxsokolov/TableKit.git", from: "2.11.0") + .package(url: "https://github.com/maxsokolov/TableKit.git", from: "2.11.0"), + .package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "4.2.2") ], targets: [ .target(name: "TITransitions", path: "TITransitions/Sources"), .target(name: "TIUIKitCore", path: "TIUIKitCore/Sources"), .target(name: "TISwiftUtils", path: "TISwiftUtils/Sources"), .target(name: "TIFoundationUtils", dependencies: ["TISwiftUtils"], path: "TIFoundationUtils/Sources"), + .target(name: "TIKeychainUtils", dependencies: ["TIFoundationUtils", "KeychainAccess"], path: "TIKeychainUtils/Sources"), .target(name: "TIUIElements", dependencies: ["TIUIKitCore", "TISwiftUtils"], path: "TIUIElements/Sources"), .target(name: "TITableKitUtils", dependencies: ["TIUIElements", "TableKit"], path: "TITableKitUtils/Sources"), .target(name: "OTPSwiftView", dependencies: ["TIUIElements"], path: "OTPSwiftView/Sources") diff --git a/README.md b/README.md index 53dba73b..1c080af5 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ This repository contains the following additional frameworks: - [OTPSwiftView](OTPSwiftView) - a fully customizable OTP view. - [TISwiftUtils](TISwiftUtils) - a bunch of useful helpers for development. - [TITableKitUtils](TITableKitUtils) - Set of helpers for TableKit classes. +- [TIFoundationUtils](TIFoundationUtils) - Set of helpers for Foundation framework classes. +- [TIKeychainUtils](TIKeychainUtils) - Set of helpers for Keychain classes. Useful docs: - [Semantic Commit Messages](docs/semantic-commit-messages.md) - commit message codestyle. @@ -42,4 +44,4 @@ source 'https://github.com/TouchInstinct/Podspecs.git' pod 'TISwiftUtils', 'x.y.z' pod 'TIFoundationUtils', 'x.y.z' # ... -``` \ No newline at end of file +``` diff --git a/TIFoundationUtils/Sources/CodableKeyValueStorage/StorageError.swift b/TIFoundationUtils/Sources/CodableKeyValueStorage/StorageError.swift index 0e64dc35..4e2617fd 100644 --- a/TIFoundationUtils/Sources/CodableKeyValueStorage/StorageError.swift +++ b/TIFoundationUtils/Sources/CodableKeyValueStorage/StorageError.swift @@ -20,7 +20,7 @@ // THE SOFTWARE. // -enum StorageError: Error { +public enum StorageError: Error { case valueNotFound case unableToExtractData(underlyingError: Error) case unableToDecode(underlyingError: Error) diff --git a/TIFoundationUtils/TIFoundationUtils.podspec b/TIFoundationUtils/TIFoundationUtils.podspec index c63f8958..bf875d6d 100644 --- a/TIFoundationUtils/TIFoundationUtils.podspec +++ b/TIFoundationUtils/TIFoundationUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIFoundationUtils' - s.version = '1.1.2' + s.version = '1.2.0' s.summary = 'Set of helpers for Foundation framework classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIKeychainUtils/README.md b/TIKeychainUtils/README.md new file mode 100644 index 00000000..b709c446 --- /dev/null +++ b/TIKeychainUtils/README.md @@ -0,0 +1,38 @@ +# TIKeychainUtils + +Set of helpers for Keychain classes. +Bunch of CodableBackingStore from TIFoundationUtils and KeychainAccess. +Implement + +#### @propertyWrapper example + +```swift +extension StorageKey { + static var userProfileKey: StorageKey { + .init(rawValue: "userProfileKey") + } +} + +extension Bundle { + var bundleId: String { + Bundle.main.bundleIdentifier ?? .empty + } +} + +extension Keychain { + static var defaultKeychain: Keychain { + .init(service: Bundle.main.bundleId) + } +} + +final class ViewModel { + @KeychainCodableBackingStore(key: .userProfileKey, codableKeyValueStorage: .defaultKeychain) + + private(set) var profile: UserProfile? + + func updateProfile(newProfile: UserProfile?) { + profile = newProfile + } +} +``` + diff --git a/TIKeychainUtils/Sources/KeychainCodableBackingStore/Keychain+CodableKeyValueStorage.swift b/TIKeychainUtils/Sources/KeychainCodableBackingStore/Keychain+CodableKeyValueStorage.swift new file mode 100644 index 00000000..9e66246c --- /dev/null +++ b/TIKeychainUtils/Sources/KeychainCodableBackingStore/Keychain+CodableKeyValueStorage.swift @@ -0,0 +1,68 @@ +// +// Copyright (c) 2020 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. +// + +import Foundation +import KeychainAccess +import TIFoundationUtils + +public typealias KeychainCodableBackingStore = CodableKeyValueBackingStore + +extension Keychain: CodableKeyValueStorage { + public func codableObject(forKey key: StorageKey, + decoder: CodableKeyValueDecoder) throws -> Value { + + let unwrappedStoredData: Data? + + do { + unwrappedStoredData = try getData(key.rawValue) + } catch { + throw StorageError.unableToExtractData(underlyingError: error) + } + + guard let storedData = unwrappedStoredData else { + throw StorageError.valueNotFound + } + + return try decoder.decodeDecodable(from: storedData, for: key) + } + + public func set(encodableObject: Value, + forKey key: StorageKey, + encoder: CodableKeyValueEncoder) throws { + + let objectData = try encoder.encodeEncodable(value: encodableObject, for: key) + + do { + try set(objectData, key: key.rawValue) + } catch { + throw StorageError.unableToWriteData(underlyingError: error) + } + } + + public func removeCodableValue(forKey key: StorageKey) throws { + do { + try remove(key.rawValue) + } catch { + throw StorageError.unableToWriteData(underlyingError: error) + } + } +} diff --git a/TIKeychainUtils/TIKeychainUtils.podspec b/TIKeychainUtils/TIKeychainUtils.podspec new file mode 100644 index 00000000..789ee1a4 --- /dev/null +++ b/TIKeychainUtils/TIKeychainUtils.podspec @@ -0,0 +1,17 @@ +Pod::Spec.new do |s| + s.name = 'TIKeychainUtils' + s.version = '1.2.0' + s.summary = 'Set of helpers for Keychain classes.' + s.homepage = 'https://github.com/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://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s } + + s.ios.deployment_target = '11.0' + s.swift_versions = ['5.3'] + + s.source_files = s.name + '/Sources/**/*' + + s.dependency 'TIFoundationUtils', s.version.to_s + s.dependency 'KeychainAccess' +end diff --git a/TISwiftUtils/TISwiftUtils.podspec b/TISwiftUtils/TISwiftUtils.podspec index b5a911ff..9e67d72c 100644 --- a/TISwiftUtils/TISwiftUtils.podspec +++ b/TISwiftUtils/TISwiftUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TISwiftUtils' - s.version = '1.1.2' + s.version = '1.2.0' s.summary = 'Bunch of useful helpers for Swift development.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITableKitUtils/TITableKitUtils.podspec b/TITableKitUtils/TITableKitUtils.podspec index eaffd093..8a954ccd 100644 --- a/TITableKitUtils/TITableKitUtils.podspec +++ b/TITableKitUtils/TITableKitUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITableKitUtils' - s.version = '1.1.2' + s.version = '1.2.0' s.summary = 'Set of helpers for TableKit classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITransitions/TITransitions.podspec b/TITransitions/TITransitions.podspec index bba696f5..51af6e08 100644 --- a/TITransitions/TITransitions.podspec +++ b/TITransitions/TITransitions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITransitions' - s.version = '1.1.2' + s.version = '1.2.0' s.summary = 'Set of custom transitions to present controller. ' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIElements/TIUIElements.podspec b/TIUIElements/TIUIElements.podspec index 832ef76c..0ee0b3db 100644 --- a/TIUIElements/TIUIElements.podspec +++ b/TIUIElements/TIUIElements.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIElements' - s.version = '1.1.2' + s.version = '1.2.0' s.summary = 'Bunch of useful protocols and views.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIKitCore/TIUIKitCore.podspec b/TIUIKitCore/TIUIKitCore.podspec index 7387c9fb..9d482c7b 100644 --- a/TIUIKitCore/TIUIKitCore.podspec +++ b/TIUIKitCore/TIUIKitCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIKitCore' - s.version = '1.1.2' + s.version = '1.2.0' s.summary = 'Core UI elements: protocols, views and helpers.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' }