Compare commits

..

No commits in common. "master" and "0.3.4" have entirely different histories.

43 changed files with 1188 additions and 560 deletions

3
.gitignore vendored
View File

@ -63,6 +63,3 @@ fastlane/report.xml
fastlane/Preview.html fastlane/Preview.html
fastlane/screenshots fastlane/screenshots
fastlane/test_output fastlane/test_output
# Touch Instinct custom
Downloads/

View File

@ -1 +1 @@
5.0 4.0

View File

@ -1,56 +1,5 @@
# Changelog # Changelog
### 0.4.0
- **Update**: update libraries to build with Xcode 13.1
### 0.3.15
- **Update**: update LeadKit dependency
### 0.3.14
- **Update**: remove Carthage binary dependencies, update build scripts.
### 0.3.13
- **[Breaking change]**: `LabelTableViewCell` moved to `LeadKit`.
- **[Breaking change]**: Removed `PinLayout` dependency.
### 0.3.12
- **Update**: Swift 5 support
### 0.3.11
- **Add**: Carthage support.
- **[Breaking change]**: Remove `BaseDateFormatter`. Use `NumberFormattingService` from LeadKit instead.
### 0.3.10
- **[Breaking change]**: Remove `isTouchIdSupported` and `isFaceIdSupported`.
- **Add** BiometryType for BiometricsService: `touchID`, `faceID`, `none`.
### 0.3.9
- **Add**: `allowableReuseDuration` variable to `BiometricsService` which is a wrapper for touchIDAuthenticationAllowableReuseDuration.
- **Add**: individual `isTouchIdSupported` variable to `BiometricsService` to indicate if TouchId can be used.
- **Add**: `clear` function to `BiometricsService` so the context can be refreshed.
- **Update**: Documentations.
### 0.3.8
- **Fixed**: `SwiftValidator` fork moved to TouchInstinct repo, the used one is removed. Version number is downgraded to `4.0.2` to avoid collision when the original pod will update.
- **Fixed**: Extensions target building. Removed all PassCode logic, SeparatorTableViewCell and PinLayoutTableViewCell from extensions target.
### 0.3.7
- **Fixed**: `PassCodeError.tooManyAttempts` logic in `.create` and `.change` `PassCodeOperationType`
### 0.3.6
- **Update**: PassCodeError, now emmit `tooManyAttempts` in any operation (*create* / *change* / *enter*) type.
- **Update**: Rename `PassCodeControllerType` to `PassCodeOperationType`.
- **Update**: `showBiometricsRequestIfNeeded` method become public.
### 0.3.5
- **Update**: Migrate to Swift 4.2 & Xcode 10. Update dependencies.
### 0.3.4 ### 0.3.4
@ -142,3 +91,4 @@
## 0.1.0 ## 0.1.0
- **Add**: support for Swift 3.2 / 4 - **Add**: support for Swift 3.2 / 4

View File

@ -1,5 +0,0 @@
github "krzyzanowskim/CryptoSwift"
github "TouchInstinct/LeadKit"
github "petropavel13/SwiftValidator"
github "kishikawakatsumi/KeychainAccess"
github "layoutBox/PinLayout"

View File

@ -1,12 +0,0 @@
github "Alamofire/Alamofire" "4.9.1"
github "ReactiveX/RxSwift" "4.5.0"
github "RxSwiftCommunity/RxAlamofire" "4.5.0"
github "SnapKit/SnapKit" "4.2.0"
github "TouchInstinct/LeadKit" "0.9.29"
github "TouchInstinct/TableKit" "2.10008.1"
github "krzyzanowskim/CryptoSwift" "1.3.0"
github "malcommac/SwiftDate" "6.1.0"
github "petropavel13/SwiftValidator" "be856202cd70e6549ebc8c9d07ec47bbff784b07"
github "pronebird/UIScrollView-InfiniteScroll" "1.1.0"
github "kishikawakatsumi/KeychainAccess" "v3.2.0"
github "layoutBox/PinLayout" "1.8.6"

View File

@ -1,21 +1,43 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "LeadKitAdditions" s.name = "LeadKitAdditions"
s.version = "0.4.0" s.version = "0.3.4"
s.summary = "iOS framework with a bunch of tools for rapid development" s.summary = "iOS framework with a bunch of tools for rapid development"
s.homepage = "https://github.com/TouchInstinct/LeadKitAdditions" s.homepage = "https://github.com/TouchInstinct/LeadKitAdditions"
s.license = "Apache License, Version 2.0" s.license = "Apache License, Version 2.0"
s.author = "Touch Instinct" s.author = "Touch Instinct"
s.platform = :ios, "10.0" s.platform = :ios, "9.0"
s.source = { :git => "https://github.com/TouchInstinct/LeadKitAdditions.git", :tag => s.version } s.source = { :git => "https://github.com/TouchInstinct/LeadKitAdditions.git", :tag => s.version }
s.subspec 'Core' do |ss| s.subspec 'Core' do |ss|
ss.ios.deployment_target = '10.0' ss.ios.deployment_target = '9.0'
ss.source_files = "Sources/**/*.swift" ss.source_files = "Sources/**/*.swift"
ss.dependency "LeadKit", '~> 1.7.0' ss.exclude_files = [
ss.dependency "KeychainAccess", '~> 4.2.0' "Sources/Services/Network/DefaultNetworkService+ActivityIndicator+Extension.swift",
ss.dependency "CryptoSwift", '~> 1.4.0' ]
ss.dependency "SwiftValidator", '4.0.2'
ss.dependency "LeadKit", '~> 0.8.0'
ss.dependency "KeychainAccess", '3.1.0'
ss.dependency "CryptoSwift", '~> 0.9.0'
ss.dependency "SwiftValidator", '5.0.0'
ss.dependency "PinLayout", '~> 1.6'
end
s.subspec 'Core-iOS-Extension' do |ss|
ss.platform = :ios, '9.0'
ss.source_files = "Sources/**/*.swift"
ss.exclude_files = [
"Sources/Classes/Cells/LabelTableViewCell/*.swift",
"Sources/Services/Network/DefaultNetworkService+ActivityIndicator.swift",
"Sources/Extensions/Cells/**/*.swift"
]
ss.dependency "LeadKit/Core-iOS-Extension", '~> 0.8.0'
ss.dependency "KeychainAccess", '3.1.0'
ss.dependency "CryptoSwift", '~> 0.9.0'
ss.dependency "SwiftValidator", '5.0.0'
ss.dependency "PinLayout", '~> 1.6'
end end
s.default_subspec = 'Core' s.default_subspec = 'Core'

View File

@ -7,70 +7,91 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
14E3AD49502B3D81264560DB /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3EDEF7F6C22BB04CBB754C14 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */; }; 248389A288C0A6D7914F0546 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED4A1B793EAA73C9E95969F /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */; };
3622F5D620E15DD1009DED94 /* PinLayoutTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CF05AF206E99DF009A2AB9 /* PinLayoutTableViewCell.swift */; };
3622F5D720E15DD5009DED94 /* SeparatorTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C2A41520724BBA000A5682 /* SeparatorTableViewCell.swift */; };
6760AF1A207268EC00C2BB7E /* PinLayoutCell+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6760AF19207268EC00C2BB7E /* PinLayoutCell+DefaultImplementation.swift */; };
67779CBC206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67779CBB206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift */; }; 67779CBC206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67779CBB206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift */; };
67779CBD206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67779CBB206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift */; };
678D26AA206935B900B05B93 /* BiometricsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D26A9206935B900B05B93 /* BiometricsService.swift */; }; 678D26AA206935B900B05B93 /* BiometricsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D26A9206935B900B05B93 /* BiometricsService.swift */; };
67B4E6EB206941CE00E233EA /* BiometricsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D26A9206935B900B05B93 /* BiometricsService.swift */; };
67B4E6F3206945D200E233EA /* BaseTextFieldViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F1206945D200E233EA /* BaseTextFieldViewModel.swift */; };
67B4E6F6206945DD00E233EA /* OnlineValidationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F5206945DC00E233EA /* OnlineValidationResult.swift */; }; 67B4E6F6206945DD00E233EA /* OnlineValidationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F5206945DC00E233EA /* OnlineValidationResult.swift */; };
67B4E6F7206945DD00E233EA /* OnlineValidationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F5206945DC00E233EA /* OnlineValidationResult.swift */; };
67B4E6F9206945F900E233EA /* OnlineValidationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F8206945F900E233EA /* OnlineValidationState.swift */; }; 67B4E6F9206945F900E233EA /* OnlineValidationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F8206945F900E233EA /* OnlineValidationState.swift */; };
67B4E6FA206945F900E233EA /* OnlineValidationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F8206945F900E233EA /* OnlineValidationState.swift */; };
67B4E6FB20694A4200E233EA /* BaseTextFieldViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F1206945D200E233EA /* BaseTextFieldViewModel.swift */; }; 67B4E6FB20694A4200E233EA /* BaseTextFieldViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F1206945D200E233EA /* BaseTextFieldViewModel.swift */; };
67D6041821627600002DAF5B /* LegacyConfigurableController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D6041721627600002DAF5B /* LegacyConfigurableController.swift */; }; 67C2A41620724BBA000A5682 /* SeparatorTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C2A41520724BBA000A5682 /* SeparatorTableViewCell.swift */; };
786CBB2F220DD7A70017587F /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB22220DD7A70017587F /* RxSwift.framework */; }; 67C2A41820724EA0000A5682 /* LabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C2A41720724EA0000A5682 /* LabelTableViewCell.swift */; };
786CBB30220DD7A70017587F /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB23220DD7A70017587F /* Alamofire.framework */; }; 67C2A41B20724F40000A5682 /* LabelCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C2A41A20724F40000A5682 /* LabelCellViewModel.swift */; };
786CBB31220DD7A70017587F /* TableKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB24220DD7A70017587F /* TableKit.framework */; }; 67C2A41D20725359000A5682 /* LabelTableViewCell+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C2A41C20725359000A5682 /* LabelTableViewCell+Extensions.swift */; };
786CBB32220DD7A70017587F /* LeadKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB25220DD7A70017587F /* LeadKit.framework */; }; 67CF05AA206E9880009A2AB9 /* PinLayoutCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CF05A9206E9880009A2AB9 /* PinLayoutCell.swift */; };
786CBB33220DD7A70017587F /* SwiftValidator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB26220DD7A70017587F /* SwiftValidator.framework */; }; 67CF05B0206E99DF009A2AB9 /* PinLayoutTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CF05AF206E99DF009A2AB9 /* PinLayoutTableViewCell.swift */; };
786CBB34220DD7A70017587F /* UIScrollView_InfiniteScroll.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB27220DD7A70017587F /* UIScrollView_InfiniteScroll.framework */; };
786CBB36220DD7A70017587F /* RxAlamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB29220DD7A70017587F /* RxAlamofire.framework */; };
786CBB37220DD7A70017587F /* SwiftDate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB2A220DD7A70017587F /* SwiftDate.framework */; };
786CBB38220DD7A70017587F /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB2B220DD7A70017587F /* RxCocoa.framework */; };
786CBB39220DD7A70017587F /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB2C220DD7A70017587F /* CryptoSwift.framework */; };
786CBB3A220DD7A70017587F /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB2D220DD7A70017587F /* KeychainAccess.framework */; };
A6CFB8D91F5024A500A42CC2 /* Error+NetworkingExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6CFB8D81F5024A500A42CC2 /* Error+NetworkingExtensions.swift */; }; A6CFB8D91F5024A500A42CC2 /* Error+NetworkingExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6CFB8D81F5024A500A42CC2 /* Error+NetworkingExtensions.swift */; };
B326804BA6CC8B8BB136A46A /* Pods_LeadKitAdditions_LeadKitAdditions_iOS_Extensions.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CFD5627139CAB27705F75C07 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS_Extensions.framework */; };
CAE698E81E968820000394B0 /* LeadKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = CAE698E61E968820000394B0 /* LeadKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; CAE698E81E968820000394B0 /* LeadKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = CAE698E61E968820000394B0 /* LeadKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
ED0C34051F2906EC00FAE9FD /* BaseDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D41F2906EC00FAE9FD /* BaseDateFormatter.swift */; };
ED0C34061F2906EC00FAE9FD /* BaseDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D41F2906EC00FAE9FD /* BaseDateFormatter.swift */; };
ED0C34071F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.swift */; }; ED0C34071F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.swift */; };
ED0C34081F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.swift */; };
ED0C34091F2906EC00FAE9FD /* PassCodeConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D91F2906EC00FAE9FD /* PassCodeConfiguration.swift */; }; ED0C34091F2906EC00FAE9FD /* PassCodeConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D91F2906EC00FAE9FD /* PassCodeConfiguration.swift */; };
ED0C340A1F2906EC00FAE9FD /* PassCodeConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D91F2906EC00FAE9FD /* PassCodeConfiguration.swift */; };
ED0C340B1F2906EC00FAE9FD /* PassCodeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DA1F2906EC00FAE9FD /* PassCodeError.swift */; }; ED0C340B1F2906EC00FAE9FD /* PassCodeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DA1F2906EC00FAE9FD /* PassCodeError.swift */; };
ED0C340C1F2906EC00FAE9FD /* PassCodeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DA1F2906EC00FAE9FD /* PassCodeError.swift */; };
ED0C340D1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DB1F2906EC00FAE9FD /* PassCodeHolder.swift */; }; ED0C340D1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DB1F2906EC00FAE9FD /* PassCodeHolder.swift */; };
ED0C340E1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DB1F2906EC00FAE9FD /* PassCodeHolder.swift */; };
ED0C340F1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DC1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift */; }; ED0C340F1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DC1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift */; };
ED0C34101F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DC1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift */; };
ED0C34111F2906EC00FAE9FD /* PassCodeValidationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DD1F2906EC00FAE9FD /* PassCodeValidationResult.swift */; }; ED0C34111F2906EC00FAE9FD /* PassCodeValidationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DD1F2906EC00FAE9FD /* PassCodeValidationResult.swift */; };
ED0C34121F2906EC00FAE9FD /* PassCodeValidationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DD1F2906EC00FAE9FD /* PassCodeValidationResult.swift */; };
ED0C34131F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DF1F2906EC00FAE9FD /* BasePassCodeViewController.swift */; }; ED0C34131F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DF1F2906EC00FAE9FD /* BasePassCodeViewController.swift */; };
ED0C34141F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DF1F2906EC00FAE9FD /* BasePassCodeViewController.swift */; };
ED0C34151F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */; }; ED0C34151F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */; };
ED0C34161F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */; };
ED0C341F1F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */; }; ED0C341F1F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */; };
ED0C34201F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */; };
ED0C34211F2906EC00FAE9FD /* UserDefaults+UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */; }; ED0C34211F2906EC00FAE9FD /* UserDefaults+UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */; };
ED0C34221F2906EC00FAE9FD /* UserDefaults+UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */; };
ED0C342D1F2906EC00FAE9FD /* BasePassCodeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F11F2906EC00FAE9FD /* BasePassCodeService.swift */; }; ED0C342D1F2906EC00FAE9FD /* BasePassCodeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F11F2906EC00FAE9FD /* BasePassCodeService.swift */; };
ED0C342E1F2906EC00FAE9FD /* BasePassCodeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F11F2906EC00FAE9FD /* BasePassCodeService.swift */; };
ED0C342F1F2906EC00FAE9FD /* BaseUserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F21F2906EC00FAE9FD /* BaseUserService.swift */; }; ED0C342F1F2906EC00FAE9FD /* BaseUserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F21F2906EC00FAE9FD /* BaseUserService.swift */; };
ED0C34301F2906EC00FAE9FD /* BaseUserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F21F2906EC00FAE9FD /* BaseUserService.swift */; };
ED0C343F1F2906EC00FAE9FD /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FC1F2906EC00FAE9FD /* ValidationError.swift */; }; ED0C343F1F2906EC00FAE9FD /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FC1F2906EC00FAE9FD /* ValidationError.swift */; };
ED0C34401F2906EC00FAE9FD /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FC1F2906EC00FAE9FD /* ValidationError.swift */; };
ED0C34411F2906EC00FAE9FD /* ValidationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FD1F2906EC00FAE9FD /* ValidationItem.swift */; }; ED0C34411F2906EC00FAE9FD /* ValidationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FD1F2906EC00FAE9FD /* ValidationItem.swift */; };
ED0C34421F2906EC00FAE9FD /* ValidationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FD1F2906EC00FAE9FD /* ValidationItem.swift */; };
ED0C34431F2906EC00FAE9FD /* ValidationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FE1F2906EC00FAE9FD /* ValidationService.swift */; }; ED0C34431F2906EC00FAE9FD /* ValidationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FE1F2906EC00FAE9FD /* ValidationService.swift */; };
ED0C34441F2906EC00FAE9FD /* ValidationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FE1F2906EC00FAE9FD /* ValidationService.swift */; };
EF5A43B1206E7A67003CED07 /* PassCodeDelayedDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF5A43B0206E7A67003CED07 /* PassCodeDelayedDescription.swift */; }; EF5A43B1206E7A67003CED07 /* PassCodeDelayedDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF5A43B0206E7A67003CED07 /* PassCodeDelayedDescription.swift */; };
EF5A43B2206E7A67003CED07 /* PassCodeDelayedDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF5A43B0206E7A67003CED07 /* PassCodeDelayedDescription.swift */; };
EFBD55921EBB9A980062AA63 /* LeadKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = CAE698E61E968820000394B0 /* LeadKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1AC235099AA10D83D608A253 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig"; path = "Target Support Files/Pods-LeadKitAdditions-LeadKitAdditions iOS/Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig"; sourceTree = "<group>"; }; 01605ECA03749D49C27FA3DD /* Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.release.xcconfig"; path = "Pods/Target Support Files/Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions/Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.release.xcconfig"; sourceTree = "<group>"; };
3EDEF7F6C22BB04CBB754C14 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0ED4A1B793EAA73C9E95969F /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
49738551AC648B0AFA74E57F /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LeadKitAdditions-LeadKitAdditions iOS/Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig"; sourceTree = "<group>"; };
67528BCE206E3CC6009F2525 /* iOS.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = iOS.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 67528BCE206E3CC6009F2525 /* iOS.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = iOS.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
6760AF19207268EC00C2BB7E /* PinLayoutCell+DefaultImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PinLayoutCell+DefaultImplementation.swift"; sourceTree = "<group>"; };
67779CBB206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTextFieldViewEvents+Extensions.swift"; sourceTree = "<group>"; }; 67779CBB206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTextFieldViewEvents+Extensions.swift"; sourceTree = "<group>"; };
678D26A9206935B900B05B93 /* BiometricsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BiometricsService.swift; sourceTree = "<group>"; }; 678D26A9206935B900B05B93 /* BiometricsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BiometricsService.swift; sourceTree = "<group>"; };
67B4E6F1206945D200E233EA /* BaseTextFieldViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTextFieldViewModel.swift; sourceTree = "<group>"; }; 67B4E6F1206945D200E233EA /* BaseTextFieldViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTextFieldViewModel.swift; sourceTree = "<group>"; };
67B4E6F5206945DC00E233EA /* OnlineValidationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnlineValidationResult.swift; sourceTree = "<group>"; }; 67B4E6F5206945DC00E233EA /* OnlineValidationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnlineValidationResult.swift; sourceTree = "<group>"; };
67B4E6F8206945F900E233EA /* OnlineValidationState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnlineValidationState.swift; sourceTree = "<group>"; }; 67B4E6F8206945F900E233EA /* OnlineValidationState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnlineValidationState.swift; sourceTree = "<group>"; };
67D6041721627600002DAF5B /* LegacyConfigurableController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyConfigurableController.swift; sourceTree = "<group>"; }; 67C2A41520724BBA000A5682 /* SeparatorTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorTableViewCell.swift; sourceTree = "<group>"; };
6FEE03727D0B955F00DE8429 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig"; path = "Target Support Files/Pods-LeadKitAdditions-LeadKitAdditions iOS/Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig"; sourceTree = "<group>"; }; 67C2A41720724EA0000A5682 /* LabelTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelTableViewCell.swift; sourceTree = "<group>"; };
786CBB22220DD7A70017587F /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = "<group>"; }; 67C2A41A20724F40000A5682 /* LabelCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelCellViewModel.swift; sourceTree = "<group>"; };
786CBB23220DD7A70017587F /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = "<group>"; }; 67C2A41C20725359000A5682 /* LabelTableViewCell+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LabelTableViewCell+Extensions.swift"; sourceTree = "<group>"; };
786CBB24220DD7A70017587F /* TableKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TableKit.framework; path = Carthage/Build/iOS/TableKit.framework; sourceTree = "<group>"; }; 67CF05A9206E9880009A2AB9 /* PinLayoutCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinLayoutCell.swift; sourceTree = "<group>"; };
786CBB25220DD7A70017587F /* LeadKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LeadKit.framework; path = Carthage/Build/iOS/LeadKit.framework; sourceTree = "<group>"; }; 67CF05AF206E99DF009A2AB9 /* PinLayoutTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinLayoutTableViewCell.swift; sourceTree = "<group>"; };
786CBB26220DD7A70017587F /* SwiftValidator.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftValidator.framework; path = Carthage/Build/iOS/SwiftValidator.framework; sourceTree = "<group>"; }; 7B7F57C5E5275C4D8DC71992 /* Pods_LeadKitAdditions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKitAdditions.framework; sourceTree = BUILT_PRODUCTS_DIR; };
786CBB27220DD7A70017587F /* UIScrollView_InfiniteScroll.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIScrollView_InfiniteScroll.framework; path = Carthage/Build/iOS/UIScrollView_InfiniteScroll.framework; sourceTree = "<group>"; }; 9D549FA5A7579702358E07DF /* Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions/Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.debug.xcconfig"; sourceTree = "<group>"; };
786CBB29220DD7A70017587F /* RxAlamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxAlamofire.framework; path = Carthage/Build/iOS/RxAlamofire.framework; sourceTree = "<group>"; };
786CBB2A220DD7A70017587F /* SwiftDate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftDate.framework; path = Carthage/Build/iOS/SwiftDate.framework; sourceTree = "<group>"; };
786CBB2B220DD7A70017587F /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = "<group>"; };
786CBB2C220DD7A70017587F /* CryptoSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CryptoSwift.framework; path = Carthage/Build/iOS/CryptoSwift.framework; sourceTree = "<group>"; };
786CBB2D220DD7A70017587F /* KeychainAccess.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainAccess.framework; path = Carthage/Build/iOS/KeychainAccess.framework; sourceTree = "<group>"; };
786CBB2E220DD7A70017587F /* PinLayout.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PinLayout.framework; path = Carthage/Build/iOS/PinLayout.framework; sourceTree = "<group>"; };
A6CFB8D81F5024A500A42CC2 /* Error+NetworkingExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Error+NetworkingExtensions.swift"; sourceTree = "<group>"; }; A6CFB8D81F5024A500A42CC2 /* Error+NetworkingExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Error+NetworkingExtensions.swift"; sourceTree = "<group>"; };
CAE698E31E968820000394B0 /* LeadKitAdditions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LeadKitAdditions.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CAE698E31E968820000394B0 /* LeadKitAdditions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LeadKitAdditions.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CAE698E61E968820000394B0 /* LeadKitAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LeadKitAdditions.h; sourceTree = "<group>"; }; CAE698E61E968820000394B0 /* LeadKitAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LeadKitAdditions.h; sourceTree = "<group>"; };
CFD5627139CAB27705F75C07 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS_Extensions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKitAdditions_LeadKitAdditions_iOS_Extensions.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D0AB283D10B2175EFDBF7924 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-LeadKitAdditions-LeadKitAdditions iOS/Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig"; sourceTree = "<group>"; };
ED0C33D41F2906EC00FAE9FD /* BaseDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDateFormatter.swift; sourceTree = "<group>"; };
ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingBarButton.swift; sourceTree = "<group>"; }; ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingBarButton.swift; sourceTree = "<group>"; };
ED0C33D91F2906EC00FAE9FD /* PassCodeConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeConfiguration.swift; sourceTree = "<group>"; }; ED0C33D91F2906EC00FAE9FD /* PassCodeConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeConfiguration.swift; sourceTree = "<group>"; };
ED0C33DA1F2906EC00FAE9FD /* PassCodeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeError.swift; sourceTree = "<group>"; }; ED0C33DA1F2906EC00FAE9FD /* PassCodeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeError.swift; sourceTree = "<group>"; };
@ -87,6 +108,7 @@
ED0C33FD1F2906EC00FAE9FD /* ValidationItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationItem.swift; sourceTree = "<group>"; }; ED0C33FD1F2906EC00FAE9FD /* ValidationItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationItem.swift; sourceTree = "<group>"; };
ED0C33FE1F2906EC00FAE9FD /* ValidationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationService.swift; sourceTree = "<group>"; }; ED0C33FE1F2906EC00FAE9FD /* ValidationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationService.swift; sourceTree = "<group>"; };
EF5A43B0206E7A67003CED07 /* PassCodeDelayedDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassCodeDelayedDescription.swift; sourceTree = "<group>"; }; EF5A43B0206E7A67003CED07 /* PassCodeDelayedDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassCodeDelayedDescription.swift; sourceTree = "<group>"; };
EFBD55701EBB87100062AA63 /* LeadKitAdditions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LeadKitAdditions.framework; sourceTree = BUILT_PRODUCTS_DIR; };
EFBD55781EBB893F0062AA63 /* Info-iOS-Extensions.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS-Extensions.plist"; sourceTree = "<group>"; }; EFBD55781EBB893F0062AA63 /* Info-iOS-Extensions.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS-Extensions.plist"; sourceTree = "<group>"; };
EFBD55791EBB893F0062AA63 /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = "<group>"; }; EFBD55791EBB893F0062AA63 /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -96,24 +118,31 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
786CBB2F220DD7A70017587F /* RxSwift.framework in Frameworks */, 248389A288C0A6D7914F0546 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework in Frameworks */,
786CBB30220DD7A70017587F /* Alamofire.framework in Frameworks */, );
786CBB31220DD7A70017587F /* TableKit.framework in Frameworks */, runOnlyForDeploymentPostprocessing = 0;
786CBB32220DD7A70017587F /* LeadKit.framework in Frameworks */, };
786CBB33220DD7A70017587F /* SwiftValidator.framework in Frameworks */, EFBD556C1EBB87100062AA63 /* Frameworks */ = {
786CBB34220DD7A70017587F /* UIScrollView_InfiniteScroll.framework in Frameworks */, isa = PBXFrameworksBuildPhase;
786CBB36220DD7A70017587F /* RxAlamofire.framework in Frameworks */, buildActionMask = 2147483647;
786CBB37220DD7A70017587F /* SwiftDate.framework in Frameworks */, files = (
786CBB38220DD7A70017587F /* RxCocoa.framework in Frameworks */, B326804BA6CC8B8BB136A46A /* Pods_LeadKitAdditions_LeadKitAdditions_iOS_Extensions.framework in Frameworks */,
786CBB39220DD7A70017587F /* CryptoSwift.framework in Frameworks */,
786CBB3A220DD7A70017587F /* KeychainAccess.framework in Frameworks */,
14E3AD49502B3D81264560DB /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
67528BCB206E3C12009F2525 /* Cells */ = {
isa = PBXGroup;
children = (
67C2A41920724F32000A5682 /* LabelTableViewCell */,
67CF05AF206E99DF009A2AB9 /* PinLayoutTableViewCell.swift */,
67C2A41520724BBA000A5682 /* SeparatorTableViewCell.swift */,
);
path = Cells;
sourceTree = "<group>";
};
67B4E6F0206945D200E233EA /* BaseTextFieldViewModel */ = { 67B4E6F0206945D200E233EA /* BaseTextFieldViewModel */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -130,22 +159,38 @@
path = ValidationService; path = ValidationService;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
67C2A41920724F32000A5682 /* LabelTableViewCell */ = {
isa = PBXGroup;
children = (
67C2A41720724EA0000A5682 /* LabelTableViewCell.swift */,
67C2A41A20724F40000A5682 /* LabelCellViewModel.swift */,
);
path = LabelTableViewCell;
sourceTree = "<group>";
};
67CF05A8206E986A009A2AB9 /* Cells */ = {
isa = PBXGroup;
children = (
67CF05A9206E9880009A2AB9 /* PinLayoutCell.swift */,
);
path = Cells;
sourceTree = "<group>";
};
67CF05AC206E9931009A2AB9 /* Cells */ = {
isa = PBXGroup;
children = (
67C2A41C20725359000A5682 /* LabelTableViewCell+Extensions.swift */,
6760AF19207268EC00C2BB7E /* PinLayoutCell+DefaultImplementation.swift */,
);
path = Cells;
sourceTree = "<group>";
};
A3117951840B8B7D2E7A8A80 /* Frameworks */ = { A3117951840B8B7D2E7A8A80 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
786CBB23220DD7A70017587F /* Alamofire.framework */, 7B7F57C5E5275C4D8DC71992 /* Pods_LeadKitAdditions.framework */,
786CBB2C220DD7A70017587F /* CryptoSwift.framework */, 0ED4A1B793EAA73C9E95969F /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */,
786CBB2D220DD7A70017587F /* KeychainAccess.framework */, CFD5627139CAB27705F75C07 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS_Extensions.framework */,
786CBB25220DD7A70017587F /* LeadKit.framework */,
786CBB2E220DD7A70017587F /* PinLayout.framework */,
786CBB29220DD7A70017587F /* RxAlamofire.framework */,
786CBB2B220DD7A70017587F /* RxCocoa.framework */,
786CBB22220DD7A70017587F /* RxSwift.framework */,
786CBB2A220DD7A70017587F /* SwiftDate.framework */,
786CBB26220DD7A70017587F /* SwiftValidator.framework */,
786CBB24220DD7A70017587F /* TableKit.framework */,
786CBB27220DD7A70017587F /* UIScrollView_InfiniteScroll.framework */,
3EDEF7F6C22BB04CBB754C14 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@ -154,10 +199,10 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A3117951840B8B7D2E7A8A80 /* Frameworks */, A3117951840B8B7D2E7A8A80 /* Frameworks */,
F8A65FEC7C0EB4B93746E50F /* Pods */,
CAE698E41E968820000394B0 /* Products */, CAE698E41E968820000394B0 /* Products */,
CAE698E51E968820000394B0 /* Sources */, CAE698E51E968820000394B0 /* Sources */,
67528BCE206E3CC6009F2525 /* iOS.playground */, 67528BCE206E3CC6009F2525 /* iOS.playground */,
FBC34E01DAB566B85ED77BAD /* Pods */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -165,6 +210,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CAE698E31E968820000394B0 /* LeadKitAdditions.framework */, CAE698E31E968820000394B0 /* LeadKitAdditions.framework */,
EFBD55701EBB87100062AA63 /* LeadKitAdditions.framework */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@ -187,7 +233,9 @@
ED0C33D21F2906EC00FAE9FD /* Classes */ = { ED0C33D21F2906EC00FAE9FD /* Classes */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
67528BCB206E3C12009F2525 /* Cells */,
67B4E6F0206945D200E233EA /* BaseTextFieldViewModel */, 67B4E6F0206945D200E233EA /* BaseTextFieldViewModel */,
ED0C33D41F2906EC00FAE9FD /* BaseDateFormatter.swift */,
ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.swift */, ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.swift */,
); );
path = Classes; path = Classes;
@ -243,6 +291,7 @@
ED0C33E61F2906EC00FAE9FD /* Extensions */ = { ED0C33E61F2906EC00FAE9FD /* Extensions */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
67CF05AC206E9931009A2AB9 /* Cells */,
ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */, ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */,
ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */, ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */,
A6CFB8D81F5024A500A42CC2 /* Error+NetworkingExtensions.swift */, A6CFB8D81F5024A500A42CC2 /* Error+NetworkingExtensions.swift */,
@ -254,8 +303,8 @@
ED0C33EA1F2906EC00FAE9FD /* Protocols */ = { ED0C33EA1F2906EC00FAE9FD /* Protocols */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
67CF05A8206E986A009A2AB9 /* Cells */,
67B4E6F4206945DC00E233EA /* ValidationService */, 67B4E6F4206945DC00E233EA /* ValidationService */,
67D6041721627600002DAF5B /* LegacyConfigurableController.swift */,
); );
path = Protocols; path = Protocols;
sourceTree = "<group>"; sourceTree = "<group>";
@ -282,13 +331,15 @@
path = ValidationService; path = ValidationService;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
FBC34E01DAB566B85ED77BAD /* Pods */ = { F8A65FEC7C0EB4B93746E50F /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
6FEE03727D0B955F00DE8429 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */, 49738551AC648B0AFA74E57F /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */,
1AC235099AA10D83D608A253 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig */, D0AB283D10B2175EFDBF7924 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig */,
9D549FA5A7579702358E07DF /* Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.debug.xcconfig */,
01605ECA03749D49C27FA3DD /* Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.release.xcconfig */,
); );
path = Pods; name = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
/* End PBXGroup section */ /* End PBXGroup section */
@ -302,6 +353,14 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
EFBD556D1EBB87100062AA63 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
EFBD55921EBB9A980062AA63 /* LeadKitAdditions.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */ /* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -309,11 +368,12 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = CAE698EB1E968820000394B0 /* Build configuration list for PBXNativeTarget "LeadKitAdditions iOS" */; buildConfigurationList = CAE698EB1E968820000394B0 /* Build configuration list for PBXNativeTarget "LeadKitAdditions iOS" */;
buildPhases = ( buildPhases = (
DA96E76177E2633117FCBE55 /* [CP] Check Pods Manifest.lock */, E8E82E34792B38EF225575D7 /* [CP] Check Pods Manifest.lock */,
CAE698DE1E968820000394B0 /* Sources */, CAE698DE1E968820000394B0 /* Sources */,
CAE698DF1E968820000394B0 /* Frameworks */, CAE698DF1E968820000394B0 /* Frameworks */,
CAE698E01E968820000394B0 /* Headers */, CAE698E01E968820000394B0 /* Headers */,
CAE698E11E968820000394B0 /* Resources */, CAE698E11E968820000394B0 /* Resources */,
94F6E1BA5AD68C6E2F10062B /* [CP] Copy Pods Resources */,
CAE6990A1E969A7A000394B0 /* Swiftlint */, CAE6990A1E969A7A000394B0 /* Swiftlint */,
); );
buildRules = ( buildRules = (
@ -325,29 +385,53 @@
productReference = CAE698E31E968820000394B0 /* LeadKitAdditions.framework */; productReference = CAE698E31E968820000394B0 /* LeadKitAdditions.framework */;
productType = "com.apple.product-type.framework"; productType = "com.apple.product-type.framework";
}; };
EFBD556F1EBB87100062AA63 /* LeadKitAdditions iOS Extensions */ = {
isa = PBXNativeTarget;
buildConfigurationList = EFBD55751EBB87100062AA63 /* Build configuration list for PBXNativeTarget "LeadKitAdditions iOS Extensions" */;
buildPhases = (
1C200FD9213D2207669C4304 /* [CP] Check Pods Manifest.lock */,
EFBD556B1EBB87100062AA63 /* Sources */,
EFBD556C1EBB87100062AA63 /* Frameworks */,
EFBD556D1EBB87100062AA63 /* Headers */,
EFBD556E1EBB87100062AA63 /* Resources */,
808FF5474C0E1574D405EFAF /* [CP] Copy Pods Resources */,
ED69E7E91F2AD0E000C74895 /* Swiftlint */,
);
buildRules = (
);
dependencies = (
);
name = "LeadKitAdditions iOS Extensions";
productName = "LeadKitAdditions-iOS-Extensions";
productReference = EFBD55701EBB87100062AA63 /* LeadKitAdditions.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
/* Begin PBXProject section */ /* Begin PBXProject section */
CAE698DA1E968820000394B0 /* Project object */ = { CAE698DA1E968820000394B0 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 1020; LastUpgradeCheck = 0930;
ORGANIZATIONNAME = TouchInstinct; ORGANIZATIONNAME = TouchInstinct;
TargetAttributes = { TargetAttributes = {
CAE698E21E968820000394B0 = { CAE698E21E968820000394B0 = {
CreatedOnToolsVersion = 8.3; CreatedOnToolsVersion = 8.3;
LastSwiftMigration = 1020; LastSwiftMigration = 0900;
ProvisioningStyle = Manual;
};
EFBD556F1EBB87100062AA63 = {
CreatedOnToolsVersion = 8.3.1;
ProvisioningStyle = Manual; ProvisioningStyle = Manual;
}; };
}; };
}; };
buildConfigurationList = CAE698DD1E968820000394B0 /* Build configuration list for PBXProject "LeadKitAdditions" */; buildConfigurationList = CAE698DD1E968820000394B0 /* Build configuration list for PBXProject "LeadKitAdditions" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 3.2";
developmentRegion = en; developmentRegion = English;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
en, en,
Base,
); );
mainGroup = CAE698D91E968820000394B0; mainGroup = CAE698D91E968820000394B0;
productRefGroup = CAE698E41E968820000394B0 /* Products */; productRefGroup = CAE698E41E968820000394B0 /* Products */;
@ -355,6 +439,7 @@
projectRoot = ""; projectRoot = "";
targets = ( targets = (
CAE698E21E968820000394B0 /* LeadKitAdditions iOS */, CAE698E21E968820000394B0 /* LeadKitAdditions iOS */,
EFBD556F1EBB87100062AA63 /* LeadKitAdditions iOS Extensions */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@ -367,9 +452,64 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
EFBD556E1EBB87100062AA63 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
1C200FD9213D2207669C4304 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
808FF5474C0E1574D405EFAF /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions/Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions-resources.sh\"\n";
showEnvVarsInLog = 0;
};
94F6E1BA5AD68C6E2F10062B /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LeadKitAdditions-LeadKitAdditions iOS/Pods-LeadKitAdditions-LeadKitAdditions iOS-resources.sh\"\n";
showEnvVarsInLog = 0;
};
CAE6990A1E969A7A000394B0 /* Swiftlint */ = { CAE6990A1E969A7A000394B0 /* Swiftlint */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -382,22 +522,18 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = ". run_swiftlint.sh\n"; shellScript = ". build-scripts/xcode/build_phases/swiftlint.sh Sources";
}; };
DA96E76177E2633117FCBE55 /* [CP] Check Pods Manifest.lock */ = { E8E82E34792B38EF225575D7 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputFileListPaths = (
);
inputPaths = ( inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock", "${PODS_ROOT}/Manifest.lock",
); );
name = "[CP] Check Pods Manifest.lock"; name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = ( outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-LeadKitAdditions-LeadKitAdditions iOS-checkManifestLockResult.txt", "$(DERIVED_FILE_DIR)/Pods-LeadKitAdditions-LeadKitAdditions iOS-checkManifestLockResult.txt",
); );
@ -406,6 +542,20 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
ED69E7E91F2AD0E000C74895 /* Swiftlint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = Swiftlint;
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = ". build-scripts/xcode/build_phases/swiftlint.sh Sources";
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@ -413,32 +563,70 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
67D6041821627600002DAF5B /* LegacyConfigurableController.swift in Sources */,
67B4E6FB20694A4200E233EA /* BaseTextFieldViewModel.swift in Sources */, 67B4E6FB20694A4200E233EA /* BaseTextFieldViewModel.swift in Sources */,
ED0C340F1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */, ED0C340F1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */,
ED0C343F1F2906EC00FAE9FD /* ValidationError.swift in Sources */, ED0C343F1F2906EC00FAE9FD /* ValidationError.swift in Sources */,
ED0C342F1F2906EC00FAE9FD /* BaseUserService.swift in Sources */, ED0C342F1F2906EC00FAE9FD /* BaseUserService.swift in Sources */,
678D26AA206935B900B05B93 /* BiometricsService.swift in Sources */, 678D26AA206935B900B05B93 /* BiometricsService.swift in Sources */,
67C2A41D20725359000A5682 /* LabelTableViewCell+Extensions.swift in Sources */,
ED0C34411F2906EC00FAE9FD /* ValidationItem.swift in Sources */, ED0C34411F2906EC00FAE9FD /* ValidationItem.swift in Sources */,
ED0C341F1F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */, ED0C341F1F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */,
67C2A41820724EA0000A5682 /* LabelTableViewCell.swift in Sources */,
ED0C34091F2906EC00FAE9FD /* PassCodeConfiguration.swift in Sources */, ED0C34091F2906EC00FAE9FD /* PassCodeConfiguration.swift in Sources */,
EF5A43B1206E7A67003CED07 /* PassCodeDelayedDescription.swift in Sources */, EF5A43B1206E7A67003CED07 /* PassCodeDelayedDescription.swift in Sources */,
67CF05B0206E99DF009A2AB9 /* PinLayoutTableViewCell.swift in Sources */,
ED0C34131F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */, ED0C34131F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */,
67C2A41620724BBA000A5682 /* SeparatorTableViewCell.swift in Sources */,
ED0C342D1F2906EC00FAE9FD /* BasePassCodeService.swift in Sources */, ED0C342D1F2906EC00FAE9FD /* BasePassCodeService.swift in Sources */,
67B4E6F9206945F900E233EA /* OnlineValidationState.swift in Sources */, 67B4E6F9206945F900E233EA /* OnlineValidationState.swift in Sources */,
67779CBC206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift in Sources */, 67779CBC206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift in Sources */,
ED0C340D1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */, ED0C340D1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */,
67CF05AA206E9880009A2AB9 /* PinLayoutCell.swift in Sources */,
A6CFB8D91F5024A500A42CC2 /* Error+NetworkingExtensions.swift in Sources */, A6CFB8D91F5024A500A42CC2 /* Error+NetworkingExtensions.swift in Sources */,
ED0C34071F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */, ED0C34071F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */,
6760AF1A207268EC00C2BB7E /* PinLayoutCell+DefaultImplementation.swift in Sources */,
ED0C34211F2906EC00FAE9FD /* UserDefaults+UserService.swift in Sources */, ED0C34211F2906EC00FAE9FD /* UserDefaults+UserService.swift in Sources */,
ED0C34111F2906EC00FAE9FD /* PassCodeValidationResult.swift in Sources */, ED0C34111F2906EC00FAE9FD /* PassCodeValidationResult.swift in Sources */,
ED0C34051F2906EC00FAE9FD /* BaseDateFormatter.swift in Sources */,
ED0C34431F2906EC00FAE9FD /* ValidationService.swift in Sources */, ED0C34431F2906EC00FAE9FD /* ValidationService.swift in Sources */,
ED0C340B1F2906EC00FAE9FD /* PassCodeError.swift in Sources */, ED0C340B1F2906EC00FAE9FD /* PassCodeError.swift in Sources */,
67B4E6F6206945DD00E233EA /* OnlineValidationResult.swift in Sources */, 67B4E6F6206945DD00E233EA /* OnlineValidationResult.swift in Sources */,
67C2A41B20724F40000A5682 /* LabelCellViewModel.swift in Sources */,
ED0C34151F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */, ED0C34151F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
EFBD556B1EBB87100062AA63 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
67779CBD206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift in Sources */,
ED0C34101F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */,
ED0C34401F2906EC00FAE9FD /* ValidationError.swift in Sources */,
3622F5D720E15DD5009DED94 /* SeparatorTableViewCell.swift in Sources */,
ED0C34301F2906EC00FAE9FD /* BaseUserService.swift in Sources */,
ED0C34421F2906EC00FAE9FD /* ValidationItem.swift in Sources */,
ED0C34201F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */,
ED0C340A1F2906EC00FAE9FD /* PassCodeConfiguration.swift in Sources */,
67B4E6FA206945F900E233EA /* OnlineValidationState.swift in Sources */,
67B4E6EB206941CE00E233EA /* BiometricsService.swift in Sources */,
ED0C34141F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */,
ED0C342E1F2906EC00FAE9FD /* BasePassCodeService.swift in Sources */,
67B4E6F7206945DD00E233EA /* OnlineValidationResult.swift in Sources */,
ED0C340E1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */,
67B4E6F3206945D200E233EA /* BaseTextFieldViewModel.swift in Sources */,
ED0C34081F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */,
ED0C34221F2906EC00FAE9FD /* UserDefaults+UserService.swift in Sources */,
ED0C34121F2906EC00FAE9FD /* PassCodeValidationResult.swift in Sources */,
ED0C34061F2906EC00FAE9FD /* BaseDateFormatter.swift in Sources */,
3622F5D620E15DD1009DED94 /* PinLayoutTableViewCell.swift in Sources */,
ED0C34441F2906EC00FAE9FD /* ValidationService.swift in Sources */,
EF5A43B2206E7A67003CED07 /* PassCodeDelayedDescription.swift in Sources */,
ED0C340C1F2906EC00FAE9FD /* PassCodeError.swift in Sources */,
ED0C34161F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
@ -446,7 +634,6 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@ -509,7 +696,6 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@ -563,9 +749,9 @@
}; };
CAE698EC1E968820000394B0 /* Debug */ = { CAE698EC1E968820000394B0 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 6FEE03727D0B955F00DE8429 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */; baseConfigurationReference = 49738551AC648B0AFA74E57F /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */;
buildSettings = { buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES; APPLICATION_EXTENSION_API_ONLY = NO;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
@ -575,22 +761,22 @@
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info-iOS.plist"; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info-iOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "touchinstinct.LeadKitAdditions-iOS"; PRODUCT_BUNDLE_IDENTIFIER = "touchinstinct.LeadKitAdditions-iOS";
PRODUCT_NAME = LeadKitAdditions; PRODUCT_NAME = LeadKitAdditions;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 4.0;
}; };
name = Debug; name = Debug;
}; };
CAE698ED1E968820000394B0 /* Release */ = { CAE698ED1E968820000394B0 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 1AC235099AA10D83D608A253 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig */; baseConfigurationReference = D0AB283D10B2175EFDBF7924 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig */;
buildSettings = { buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES; APPLICATION_EXTENSION_API_ONLY = NO;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
@ -600,13 +786,61 @@
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info-iOS.plist"; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info-iOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "touchinstinct.LeadKitAdditions-iOS"; PRODUCT_BUNDLE_IDENTIFIER = "touchinstinct.LeadKitAdditions-iOS";
PRODUCT_NAME = LeadKitAdditions; PRODUCT_NAME = LeadKitAdditions;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 4.0;
};
name = Release;
};
EFBD55761EBB87100062AA63 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9D549FA5A7579702358E07DF /* Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.debug.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info-iOS-Extensions.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "touchinstinct.LeadKitAdditions-iOS-Extensions";
PRODUCT_NAME = LeadKitAdditions;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 4.0;
};
name = Debug;
};
EFBD55771EBB87100062AA63 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 01605ECA03749D49C27FA3DD /* Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.release.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info-iOS-Extensions.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "touchinstinct.LeadKitAdditions-iOS-Extensions";
PRODUCT_NAME = LeadKitAdditions;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_VERSION = 4.0;
}; };
name = Release; name = Release;
}; };
@ -631,6 +865,15 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
EFBD55751EBB87100062AA63 /* Build configuration list for PBXNativeTarget "LeadKitAdditions iOS Extensions" */ = {
isa = XCConfigurationList;
buildConfigurations = (
EFBD55761EBB87100062AA63 /* Debug */,
EFBD55771EBB87100062AA63 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };
rootObject = CAE698DA1E968820000394B0 /* Project object */; rootObject = CAE698DA1E968820000394B0 /* Project object */;

View File

@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CAE698E21E968820000394B0"
BuildableName = "LeadKitAdditions.framework"
BlueprintName = "LeadKitAdditions iOS"
ReferencedContainer = "container:LeadKitAdditions.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CAE698E21E968820000394B0"
BuildableName = "LeadKitAdditions.framework"
BlueprintName = "LeadKitAdditions iOS"
ReferencedContainer = "container:LeadKitAdditions.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CAE698E21E968820000394B0"
BuildableName = "LeadKitAdditions.framework"
BlueprintName = "LeadKitAdditions iOS"
ReferencedContainer = "container:LeadKitAdditions.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

25
Podfile
View File

@ -1,20 +1,29 @@
source 'https://cdn.cocoapods.org/' source "https://github.com/CocoaPods/Specs.git"
source "https://github.com/TouchInstinct/Podspecs.git" source "https://github.com/TouchInstinct/Podspecs.git"
abstract_target 'LeadKitAdditions' do abstract_target 'LeadKitAdditions' do
pod "KeychainAccess", '~> 4.2.0' pod "KeychainAccess", '3.1.0'
pod "CryptoSwift", "~> 1.4.0" pod "CryptoSwift", "~> 0.9.0"
pod "SwiftValidator", '4.0.2' pod "SwiftValidator", '5.0.0'
pod "SwiftLint", '~> 0.45.0' pod "SwiftLint", '~> 0.25'
pod "PinLayout", '~> 1.6' pod "PinLayout", '~> 1.6'
inhibit_all_warnings! inhibit_all_warnings!
use_frameworks!
target 'LeadKitAdditions iOS' do target 'LeadKitAdditions iOS' do
platform :ios, '10.0' platform :ios, '9.0'
pod 'LeadKit', '~> 1.7.0' use_frameworks!
pod 'LeadKit', '~> 0.7.0'
end
target 'LeadKitAdditions iOS Extensions' do
platform :ios, '9.0'
use_frameworks!
pod "LeadKit/Core-iOS-Extension", '~> 0.7.0'
end end
end end

View File

@ -1,81 +1,64 @@
PODS: PODS:
- Alamofire (5.5.0) - Alamofire (4.7.2)
- CryptoSwift (1.4.2) - CryptoSwift (0.9.0)
- KeychainAccess (4.2.2) - KeychainAccess (3.1.0)
- LeadKit (1.7.0): - LeadKit (0.7.12):
- LeadKit/Core (= 1.7.0) - LeadKit/Core (= 0.7.12)
- LeadKit/Core (1.7.0): - LeadKit/Core (0.7.12):
- RxAlamofire (~> 6.1) - ObjectMapper (~> 3.1)
- RxCocoa (~> 6.2) - RxAlamofire (~> 4.1)
- RxSwift (~> 6.2) - RxCocoa (~> 4.1)
- SnapKit (~> 5.0.1) - RxSwift (~> 4.1)
- SwiftDate (~> 6) - SwiftDate (~> 4.5)
- TableKit (~> 2.11) - TableKit (~> 2.6)
- UIScrollView-InfiniteScroll (~> 1.1.0) - UIScrollView-InfiniteScroll (~> 1.0.0)
- PinLayout (1.8.6) - LeadKit/Core-iOS-Extension (0.7.12):
- RxAlamofire (6.1.1): - ObjectMapper (~> 3.1)
- RxAlamofire/Core (= 6.1.1) - RxAlamofire (~> 4.1)
- RxAlamofire/Core (6.1.1): - RxCocoa (~> 4.1)
- Alamofire (~> 5.4) - RxSwift (~> 4.1)
- RxSwift (~> 6.0) - SwiftDate (~> 4.5)
- RxCocoa (6.2.0): - ObjectMapper (3.1.0)
- RxRelay (= 6.2.0) - PinLayout (1.6.0)
- RxSwift (= 6.2.0) - RxAlamofire (4.2.0):
- RxRelay (6.2.0): - RxAlamofire/Core (= 4.2.0)
- RxSwift (= 6.2.0) - RxAlamofire/Core (4.2.0):
- RxSwift (6.2.0) - Alamofire (~> 4.5)
- SnapKit (5.0.1) - RxSwift (~> 4)
- SwiftDate (6.3.1) - RxCocoa (4.1.2):
- SwiftLint (0.45.1) - RxSwift (~> 4.0)
- SwiftValidator (4.0.2) - RxSwift (4.1.2)
- TableKit (2.11.0) - SwiftDate (4.5.1)
- UIScrollView-InfiniteScroll (1.1.0) - SwiftLint (0.25.1)
- SwiftValidator (5.0.0)
- TableKit (2.6.0)
- UIScrollView-InfiniteScroll (1.0.2)
DEPENDENCIES: DEPENDENCIES:
- CryptoSwift (~> 1.4.0) - CryptoSwift (~> 0.9.0)
- KeychainAccess (~> 4.2.0) - KeychainAccess (= 3.1.0)
- LeadKit (~> 1.7.0) - LeadKit (~> 0.7.0)
- LeadKit/Core-iOS-Extension (~> 0.7.0)
- PinLayout (~> 1.6) - PinLayout (~> 1.6)
- SwiftLint (~> 0.45.0) - SwiftLint (~> 0.25)
- SwiftValidator (= 4.0.2) - SwiftValidator (= 5.0.0)
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
- PinLayout
https://github.com/TouchInstinct/Podspecs.git:
- LeadKit
- SwiftValidator
trunk:
- Alamofire
- CryptoSwift
- KeychainAccess
- RxAlamofire
- RxCocoa
- RxRelay
- RxSwift
- SnapKit
- SwiftDate
- SwiftLint
- TableKit
- UIScrollView-InfiniteScroll
SPEC CHECKSUMS: SPEC CHECKSUMS:
Alamofire: 1c4fb5369c3fe93d2857c780d8bbe09f06f97e7c Alamofire: e4fa87002c137ba2d8d634d2c51fabcda0d5c223
CryptoSwift: a532e74ed010f8c95f611d00b8bbae42e9fe7c17 CryptoSwift: bca8c5b653dcc2d9734409242a070ff53bafac86
KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51 KeychainAccess: 94c5540b32eabf7bc32bfb976a268e8ea05fd6da
LeadKit: e5765a6edac2813042304e940fd7a43b37be80ca LeadKit: e6628cfa16f5bab6fc001d4fcba9608f60fb6fd3
PinLayout: fe2a2432d6982588e208572005c941aeeae417ab ObjectMapper: 20505058f54e5c3ca69e1d6de9897d152a5369a6
RxAlamofire: beb75a1c452d0de225651db4903f5d29d034a620 PinLayout: a2bbe9057d49a1e326b13dc4fe8c14751f8c8844
RxCocoa: 4baf94bb35f2c0ab31bc0cb9f1900155f646ba42 RxAlamofire: 87a9c588541210cc3e4a1f843ccc3ecf3eb98b31
RxRelay: e72dbfd157807478401ef1982e1c61c945c94b2f RxCocoa: d88ba0f1f6abf040011a9eb4b539324fc426843a
RxSwift: d356ab7bee873611322f134c5f9ef379fa183d8f RxSwift: e49536837d9901277638493ea537394d4b55f570
SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb SwiftDate: 7b56d42a221f582047287deb256b23fc5ed49a60
SwiftDate: 72d28954e8e1c6c1c0f917ccc8005e4f83c7d4b2 SwiftLint: ce933681be10c3266e82576dad676fa815a602e9
SwiftLint: 06ac37e4d38c7068e0935bb30cda95f093bec761 SwiftValidator: 46cdd2061962df3ee8bab3c536dea9b34191d459
SwiftValidator: 8517a67bceebd7286cb487da259db7dd44c5fc74 TableKit: 61880e4c13ac0ba396a308fcb1ae48f6dec8b458
TableKit: 8a1a267d841a2da638ea09bdb0db33aedb459fa8 UIScrollView-InfiniteScroll: c132d6d5851daff229ab4a1060ccf70a05a051c9
UIScrollView-InfiniteScroll: 3ef456bcbe759c19f510a383cff96e6647c98c98
PODFILE CHECKSUM: 1d0f91b670998d56b8cf9bc24d5b265a89e8409b PODFILE CHECKSUM: 18dadbe1461045569b8c35d705ed56bcac3325c6
COCOAPODS: 1.10.1 COCOAPODS: 1.4.0

View File

@ -0,0 +1,107 @@
//
// Copyright (c) 2018 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
/// Base date formatter class, contains most frequently used formats, including RFC3339
open class BaseDateFormatter {
private static let apiDateTimeFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
private static let apiDateWithoutTimeFormat = "yyyy-MM-dd'T'Z"
private static let hourAndMinuteDateTimeFormat = "HH:mm"
private static let dayAndMonthDateTimeFormat = "dd MMM"
private static let dayMonthYearDateTimeFormat = "dd.MM.yyyy"
private static let apiFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = apiDateTimeFormat
return dateFormatter
}()
private static let apiDateWithoutTimeFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = apiDateWithoutTimeFormat
return dateFormatter
}()
private static let hourAndMinuteFormatter: DateFormatter = {
let dateFormater = DateFormatter()
dateFormater.dateFormat = hourAndMinuteDateTimeFormat
return dateFormater
}()
private static let dayAndMonthFormatter: DateFormatter = {
let dateFormater = DateFormatter()
dateFormater.dateFormat = dayAndMonthDateTimeFormat
return dateFormater
}()
private static let dayMonthYearFormatter: DateFormatter = {
let dateFormater = DateFormatter()
dateFormater.dateFormat = dayMonthYearDateTimeFormat
return dateFormater
}()
// MARK: - Public interface
/// DateFormatter's locale can be overriden
open class var usedLocale: Locale {
return .current
}
/// Parse date from string with format: yyyy-MM-dd'T'HH:mm:ssZ
public static func backendDate(fromStrDate strDate: String) -> Date? {
apiFormatter.locale = usedLocale
return apiFormatter.date(from: strDate)
}
/// Serialize date into string with format: yyyy-MM-dd'T'HH:mm:ssZ
public static func backendStrDate(withDate date: Date) -> String {
apiFormatter.locale = usedLocale
return apiFormatter.string(from: date)
}
/// Serialize date into string with format: yyyy-MM-dd'T'Z
public static func backendDateWithoutTime(withDate date: Date) -> String {
apiDateWithoutTimeFormatter.locale = usedLocale
return apiDateWithoutTimeFormatter.string(from: date)
}
/// Serialize date into string with format: HH:mm
public static func hourAndMinuteStrDate(withDate date: Date) -> String {
hourAndMinuteFormatter.locale = usedLocale
return hourAndMinuteFormatter.string(from: date)
}
/// Serialize date into string with format: dd MMM
public static func dayAndMonthStrDate(withDate date: Date) -> String {
hourAndMinuteFormatter.locale = usedLocale
return dayAndMonthFormatter.string(from: date)
}
/// Serialize date into string with format: dd.MM.yyyy
public static func dayMonthYearStrDate(withDate date: Date) -> String {
hourAndMinuteFormatter.locale = usedLocale
return dayMonthYearFormatter.string(from: date)
}
}

View File

@ -36,6 +36,7 @@ open class BaseTextFieldViewEvents: TextFieldViewEvents {
public init(textChangedDriver: Driver<String?>) { public init(textChangedDriver: Driver<String?>) {
self.textChangedDriver = textChangedDriver self.textChangedDriver = textChangedDriver
} }
} }
/// Base implementation of text field view model events. /// Base implementation of text field view model events.
@ -59,6 +60,7 @@ open class BaseTextFieldViewModelEvents: TextFieldViewModelEvents {
self.changeValidationStateDriver = changeValidationStateDriver self.changeValidationStateDriver = changeValidationStateDriver
self.changeOnlineValidationStateDriver = changeOnlineValidationStateDriver self.changeOnlineValidationStateDriver = changeOnlineValidationStateDriver
} }
} }
public extension BaseTextFieldViewModelEvents { public extension BaseTextFieldViewModelEvents {
@ -99,13 +101,14 @@ public extension BaseTextFieldViewModelEvents {
using offlineRules: [Rule] = [], using offlineRules: [Rule] = [],
validationClosure: @escaping OnlineValidationClosure) -> Driver<OnlineValidationState> { validationClosure: @escaping OnlineValidationClosure) -> Driver<OnlineValidationState> {
textDriver.flatMap { string -> Driver<OnlineValidationState> in return textDriver.flatMap { string -> Driver<OnlineValidationState> in
guard let nonEmptyString = string, !nonEmptyString.isEmpty else { guard let nonEmptyString = string, !nonEmptyString.isEmpty else {
return .just(.initial) return .just(.initial)
} }
let passedRules = offlineRules let passedRules = offlineRules
.allSatisfy { $0.validate(nonEmptyString) } .map { $0.validate(nonEmptyString) }
.reduce(true) { $0 && $1 }
guard passedRules else { guard passedRules else {
return .just(.initial) return .just(.initial)
@ -136,7 +139,7 @@ public extension BaseTextFieldViewModelEvents {
rules: [Rule] = [RequiredRule()], rules: [Rule] = [RequiredRule()],
validationService: ValidationService, validationService: ValidationService,
onlineValidationClosure: OnlineValidationClosure? = nil, onlineValidationClosure: OnlineValidationClosure? = nil,
onlineValidationThrottle: RxTimeInterval = .milliseconds(500)) { onlineValidationThrottle: RxTimeInterval = 0.5) {
let dataModelFieldDriver = binding.fieldDriver let dataModelFieldDriver = binding.fieldDriver
@ -161,4 +164,5 @@ public extension BaseTextFieldViewModelEvents {
changeValidationStateDriver: offlineValidationDriver, changeValidationStateDriver: offlineValidationDriver,
changeOnlineValidationStateDriver: onlineValidationDriver) changeOnlineValidationStateDriver: onlineValidationDriver)
} }
} }

View File

@ -0,0 +1,56 @@
//
// Copyright (c) 2018 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 UIKit.UIGeometry
import LeadKit
/// Default view model for LabelTableViewCell.
open class LabelCellViewModel {
public let viewText: ViewText
public let contentBackground: ViewBackground
public let contentInsets: UIEdgeInsets
public let labelInsets: UIEdgeInsets
public let separatorType: CellSeparatorType
/// Memberwise initializer.
///
/// - Parameters:
/// - viewText: View text to configure label.
/// - contentBackground: View background to configure background.
/// - contentInsets: Content insets to use for layout whole content.
/// - labelInsets: Label insets to use for layout label.
/// - separatorType: Separator type to use for separators.
public init(viewText: ViewText,
contentBackground: ViewBackground = .color(.clear),
contentInsets: UIEdgeInsets = .zero,
labelInsets: UIEdgeInsets = .zero,
separatorType: CellSeparatorType = .none) {
self.viewText = viewText
self.contentBackground = contentBackground
self.contentInsets = contentInsets
self.labelInsets = labelInsets
self.separatorType = separatorType
}
}

View File

@ -0,0 +1,141 @@
//
// Copyright (c) 2018 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 UIKit
import LeadKit
import PinLayout
/// Pin layout cell with separators, background and label.
open class LabelTableViewCell: SeparatorTableViewCell {
private let label = UILabel(frame: .zero)
private let backgroundImageView = UIImageView(frame: .zero)
private let contentContainerView = UIView(frame: .zero)
private var viewModel: LabelCellViewModel?
// MARK: - PinLayoutTableViewCell
override open func addViews() {
super.addViews()
contentContainerView.addSubview(backgroundImageView)
contentContainerView.addSubview(label)
contentView.addSubview(contentContainerView)
}
override open func configureAppearance() {
super.configureAppearance()
selectionStyle = .none
backgroundColor = .clear
contentView.backgroundColor = .clear
configureAppearance(of: label, backgroundImageView: backgroundImageView)
}
override open func layout() {
let topSeparatorHeight = viewModel?.separatorType.topConfiguration?.totalHeight ?? 0
let bottomSeparatorHeight = viewModel?.separatorType.bottomConfiguration?.totalHeight ?? 0
contentContainerView.pin
.top(topSeparatorHeight + contentInsets.top)
.horizontally(contentInsets)
.bottom(contentInsets.bottom + bottomSeparatorHeight)
.layout()
label.pin
.top(labelInsets)
.horizontally(labelInsets)
.sizeToFit(.width)
.layout()
backgroundImageView.pin
.all()
.layout()
// bottom separator positioning after content size (height) calculation
super.layout()
}
private var labelInsets: UIEdgeInsets {
return viewModel?.labelInsets ?? .zero
}
private var contentInsets: UIEdgeInsets {
return viewModel?.contentInsets ?? .zero
}
override open var contentHeight: CGFloat {
let selfContentHeight = contentInsets.top +
labelInsets.top +
label.frame.height +
labelInsets.bottom +
contentInsets.bottom
return selfContentHeight + super.contentHeight
}
// MARK: - Subclass methods to override
/// Callback for label and background image view appearance configuration.
///
/// - Parameters:
/// - label: Internal UILabel instance to configure.
/// - backgroundImageView: Internal UIImageView instance to configure.
open func configureAppearance(of label: UILabel, backgroundImageView: UIImageView) {
label.numberOfLines = 0
}
// MARK: - Configuration methods
/// Convenient method for configuration cell with LabelCellViewModel.
///
/// - Parameter viewModel: LabelCellViewModel instance.
public func configureLabelCell(with viewModel: LabelCellViewModel) {
self.viewModel = viewModel
configureSeparator(with: viewModel.separatorType)
configureLabelText(with: viewModel.viewText)
configureContentBackground(with: viewModel.contentBackground)
setNeedsLayout()
}
/// Method for background configuration.
///
/// - Parameter contentBackground: Content background to use as background.
public func configureContentBackground(with contentBackground: ViewBackground) {
contentBackground.configure(backgroundView: contentContainerView,
backgroundImageView: backgroundImageView)
}
/// Method for text configuration.
///
/// - Parameter viewText: View text to use as background.
public func configureLabelText(with viewText: ViewText) {
label.configure(with: viewText)
}
}

View File

@ -0,0 +1,104 @@
//
// Copyright (c) 2018 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 UIKit
import PinLayout
/// Cell that uses PinLayout. Contains methods that should be overriden in subclasses.
open class PinLayoutTableViewCell: UITableViewCell, PinLayoutCell {
public override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
initializeCell()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeCell()
}
open override func sizeThatFits(_ size: CGSize) -> CGSize {
return layout(with: size)
}
open override func layoutSubviews() {
super.layoutSubviews()
layout()
}
// MARK: - PinLayoutCell
open func initializeCell() {
addViews()
configureAppearance()
configureLayout()
}
open func layout(with containerSize: CGSize) -> CGSize {
// 1) Set the contentView's width to the specified size parameter
contentView.pin.width(containerSize.width).layout()
// 2) Layout the contentView's controls
layout()
// 3) Returns a size that contains all controls
return CGSize(width: contentView.frame.width,
height: contentHeight)
}
open func addViews() {
// override in subclass
// move from _UISnapshotWindow superview in Playground
addSubview(contentView)
}
open func configureAppearance() {
// override in subclass
}
open func configureLayout() {
// override in subclass
}
open func layout() {
// override in subclass
}
open var contentHeight: CGFloat {
return contentView.subviewsMaxY
}
}
private extension UIView {
var subviewsMaxY: CGFloat {
return subviews
.map { $0.frame.maxY }
.max() ?? frame.maxY
}
}

View File

@ -0,0 +1,104 @@
//
// Copyright (c) 2018 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 UIKit
import LeadKit
import PinLayout
/// Pin layout cell with top and bottom separators.
open class SeparatorTableViewCell: PinLayoutTableViewCell {
private let topSeparator = UIView(frame: .zero)
private let bottomSeparator = UIView(frame: .zero)
private var separatorType: CellSeparatorType = .none
/// Configure separator with viewModel.
/// - parameter separatorType: type of separators.
public func configureSeparator(with separatorType: CellSeparatorType) {
self.separatorType = separatorType
separatorType.configure(topSeparatorView: topSeparator,
bottomSeparatorView: bottomSeparator)
setNeedsLayout()
}
/// Move separator upward in hierarchy
public func bringSeparatorsToFront() {
contentView.bringSubview(toFront: topSeparator)
contentView.bringSubview(toFront: bottomSeparator)
}
/// Move separator backward in hierarchy
public func sendSeparatorsToBack() {
contentView.sendSubview(toBack: topSeparator)
contentView.sendSubview(toBack: bottomSeparator)
}
// MARK: - PinLayoutTableViewCell
override open func addViews() {
super.addViews()
contentView.addSubview(topSeparator)
contentView.addSubview(bottomSeparator)
}
override open func layout() {
super.layout()
if let topConfiguration = separatorType.topConfiguration {
topSeparator.pin
.top(topConfiguration.insets)
.horizontally(topConfiguration.insets)
.height(topConfiguration.height)
.layout()
}
if let bottomConfiguration = separatorType.bottomConfiguration {
let topInset = contentHeight - (bottomConfiguration.height + bottomConfiguration.insets.bottom)
bottomSeparator.pin
.top(topInset)
.horizontally(bottomConfiguration.insets)
.height(bottomConfiguration.height)
.layout()
}
}
override open var contentHeight: CGFloat {
let topSeparatorHeight = separatorType.topConfiguration?.totalHeight ?? 0
let bottomSeparatorHeight = separatorType.bottomConfiguration?.totalHeight ?? 0
return topSeparatorHeight + bottomSeparatorHeight
}
// MARK: - UITableViewCell
override open func prepareForReuse() {
super.prepareForReuse()
configureSeparator(with: .none)
}
}

View File

@ -42,7 +42,6 @@ public class LoadingBarButton {
switch side { switch side {
case .left: case .left:
return navigationItem?.leftBarButtonItem return navigationItem?.leftBarButtonItem
case .right: case .right:
return navigationItem?.rightBarButtonItem return navigationItem?.rightBarButtonItem
} }
@ -51,7 +50,6 @@ public class LoadingBarButton {
switch side { switch side {
case .left: case .left:
navigationItem?.leftBarButtonItem = newValue navigationItem?.leftBarButtonItem = newValue
case .right: case .right:
navigationItem?.rightBarButtonItem = newValue navigationItem?.rightBarButtonItem = newValue
} }
@ -73,13 +71,14 @@ public class LoadingBarButton {
fileprivate func setState(waiting: Bool = false) { fileprivate func setState(waiting: Bool = false) {
if waiting { if waiting {
let activityIndicatorItem = UIBarButtonItem.activityIndicator let activityIndicatorItem = UIBarButtonItem.activityIndicator
barButtonItem = activityIndicatorItem.barButton barButtonItem = activityIndicatorItem.barButton
activityIndicatorItem.activityIndicator.startAnimating() activityIndicatorItem.activityIndicator.startAnimating()
} else { } else {
barButtonItem = initialBarButton barButtonItem = initialBarButton
} }
} }
} }
public extension Observable { public extension Observable {
@ -93,12 +92,13 @@ public extension Observable {
- Returns: - Returns:
- observable, that handles LoadingBarButton behaviour - observable, that handles LoadingBarButton behaviour
*/ */
func changeLoadingUI(using barButton: LoadingBarButton) -> Observable<Observable.Element> { func changeLoadingUI(using barButton: LoadingBarButton) -> Observable<Observable.E> {
observe(on: MainScheduler.instance) return observeOn(MainScheduler.instance)
.do(onSubscribe: { .do(onSubscribe: {
barButton.setState(waiting: true) barButton.setState(waiting: true)
}, onDispose: { }, onDispose: {
barButton.setState(waiting: false) barButton.setState(waiting: false)
}) })
} }
} }

View File

@ -40,6 +40,6 @@ public struct PassCodeConfiguration {
/// Returns configuration with default values /// Returns configuration with default values
public static var defaultConfiguration: PassCodeConfiguration { public static var defaultConfiguration: PassCodeConfiguration {
PassCodeConfiguration() return PassCodeConfiguration()
} }
} }

View File

@ -32,4 +32,5 @@ public struct PassCodeDelayedDescription {
self.description = description self.description = description
self.delay = delay self.delay = delay
} }
} }

View File

@ -21,15 +21,13 @@
// //
/// Describes error, which may occur during pass code entering /// Describes error, which may occur during pass code entering
/// - codesNotMatch: Different codes
/// - wrongCode: Value is remaining attemps
/// - tooManyAttempts: Attempts limit reached
public enum PassCodeError: Error { public enum PassCodeError: Error {
/// Different codes
case codesNotMatch case codesNotMatch
case wrongCode(Int)
/// Value is remaining attemps case tooManyAttempts
case wrongCode(attemptsRemaining: Int)
/// Attempts limit reached (for create, change or enter)
case tooManyAttempts(type: PassCodeOperationType)
} }
public extension PassCodeError { public extension PassCodeError {
@ -37,7 +35,6 @@ public extension PassCodeError {
switch self { switch self {
case .tooManyAttempts: case .tooManyAttempts:
return true return true
default: default:
return false return false
} }

View File

@ -26,22 +26,22 @@ import RxCocoa
public extension PassCodeHolderProtocol { public extension PassCodeHolderProtocol {
var passCodeHolderCreate: PassCodeHolderCreate? { var passCodeHolderCreate: PassCodeHolderCreate? {
self as? PassCodeHolderCreate return self as? PassCodeHolderCreate
} }
var passCodeHolderEnter: PassCodeHolderEnter? { var passCodeHolderEnter: PassCodeHolderEnter? {
self as? PassCodeHolderEnter return self as? PassCodeHolderEnter
} }
var passCodeHolderChange: PassCodeHolderChange? { var passCodeHolderChange: PassCodeHolderChange? {
self as? PassCodeHolderChange return self as? PassCodeHolderChange
} }
} }
/// Holds information about pass codes during pass code creation process /// Holds information about pass codes during pass code creation process
public class PassCodeHolderCreate: PassCodeHolderProtocol { public class PassCodeHolderCreate: PassCodeHolderProtocol {
public let type: PassCodeOperationType = .create public let type: PassCodeControllerType = .create
private var firstPassCode: String? private var firstPassCode: String?
private var secondPassCode: String? private var secondPassCode: String?
@ -55,7 +55,7 @@ public class PassCodeHolderCreate: PassCodeHolderProtocol {
} }
public var shouldValidate: Bool { public var shouldValidate: Bool {
firstPassCode != nil && secondPassCode != nil return firstPassCode != nil && secondPassCode != nil
} }
public var passCode: String? { public var passCode: String? {
@ -70,10 +70,8 @@ public class PassCodeHolderCreate: PassCodeHolderProtocol {
switch enterStep { switch enterStep {
case .enter: case .enter:
firstPassCode = passCode firstPassCode = passCode
case .repeatEnter: case .repeatEnter:
secondPassCode = passCode secondPassCode = passCode
default: default:
break break
} }
@ -88,19 +86,20 @@ public class PassCodeHolderCreate: PassCodeHolderProtocol {
} }
public func reset() { public func reset() {
firstPassCode = nil firstPassCode = nil
secondPassCode = nil secondPassCode = nil
} }
} }
/// Holds information about pass code during pass code entering process /// Holds information about pass code during pass code entering process
public class PassCodeHolderEnter: PassCodeHolderProtocol { public class PassCodeHolderEnter: PassCodeHolderProtocol {
public let type: PassCodeOperationType = .enter public let type: PassCodeControllerType = .enter
public let enterStep: PassCodeControllerState = .enter public let enterStep: PassCodeControllerState = .enter
public var shouldValidate: Bool { public var shouldValidate: Bool {
passCode != nil return passCode != nil
} }
public var passCode: String? public var passCode: String?
@ -120,12 +119,13 @@ public class PassCodeHolderEnter: PassCodeHolderProtocol {
public func reset() { public func reset() {
passCode = nil passCode = nil
} }
} }
/// Holds information about pass codes during pass code changing process /// Holds information about pass codes during pass code changing process
public class PassCodeHolderChange: PassCodeHolderProtocol { public class PassCodeHolderChange: PassCodeHolderProtocol {
public let type: PassCodeOperationType = .change public let type: PassCodeControllerType = .change
private var oldPassCode: String? private var oldPassCode: String?
private var newFirstPassCode: String? private var newFirstPassCode: String?
@ -153,15 +153,12 @@ public class PassCodeHolderChange: PassCodeHolderProtocol {
public var passCode: String? { public var passCode: String? {
switch (oldPassCode, newFirstPassCode, newSecondPassCode) { switch (oldPassCode, newFirstPassCode, newSecondPassCode) {
case let (oldPassCode?, nil, nil): case (let oldPassCode?, nil, nil):
return oldPassCode return oldPassCode
case (_, _?, nil): case (_, _?, nil):
return nil return nil
case (_, let newFirstPassCode?, let newSecondPassCode?) where newFirstPassCode == newSecondPassCode:
case let (_, newFirstPassCode?, newSecondPassCode?) where newFirstPassCode == newSecondPassCode:
return newFirstPassCode return newFirstPassCode
default: default:
return nil return nil
} }
@ -193,4 +190,5 @@ public class PassCodeHolderChange: PassCodeHolderProtocol {
newSecondPassCode = nil newSecondPassCode = nil
} }
} }
} }

View File

@ -25,7 +25,7 @@
public protocol PassCodeHolderProtocol { public protocol PassCodeHolderProtocol {
/// Type of operation with pass code /// Type of operation with pass code
var type: PassCodeOperationType { get } var type: PassCodeControllerType { get }
/// Operation step /// Operation step
var enterStep: PassCodeControllerState { get } var enterStep: PassCodeControllerState { get }
@ -41,6 +41,7 @@ public protocol PassCodeHolderProtocol {
/// Returns passCode or error if pass code is invalid /// Returns passCode or error if pass code is invalid
func validate() -> PassCodeValidationResult func validate() -> PassCodeValidationResult
} }
public class PassCodeHolderBuilder { public class PassCodeHolderBuilder {
@ -53,16 +54,15 @@ public class PassCodeHolderBuilder {
- parameter type: type of pass code controller - parameter type: type of pass code controller
- returns: pass code information holder, specific by type - returns: pass code information holder, specific by type
*/ */
public static func build(with type: PassCodeOperationType) -> PassCodeHolderProtocol { public static func build(with type: PassCodeControllerType) -> PassCodeHolderProtocol {
switch type { switch type {
case .create: case .create:
return PassCodeHolderCreate() return PassCodeHolderCreate()
case .enter: case .enter:
return PassCodeHolderEnter() return PassCodeHolderEnter()
case .change: case .change:
return PassCodeHolderChange() return PassCodeHolderChange()
} }
} }
} }

View File

@ -25,6 +25,7 @@ public enum PassCodeValidationResult {
case valid(String) case valid(String)
case invalid(PassCodeError?) case invalid(PassCodeError?)
} }
public extension PassCodeValidationResult { public extension PassCodeValidationResult {
@ -32,7 +33,6 @@ public extension PassCodeValidationResult {
switch self { switch self {
case .valid: case .valid:
return true return true
case .invalid: case .invalid:
return false return false
} }
@ -42,7 +42,6 @@ public extension PassCodeValidationResult {
switch self { switch self {
case let .valid(passCode): case let .valid(passCode):
return passCode return passCode
case .invalid: case .invalid:
return nil return nil
} }
@ -52,7 +51,6 @@ public extension PassCodeValidationResult {
switch self { switch self {
case let .invalid(error): case let .invalid(error):
return error return error
case .valid: case .valid:
return nil return nil
} }

View File

@ -32,7 +32,7 @@ public enum PinImageType {
} }
/// Pass code operation type /// Pass code operation type
public enum PassCodeOperationType { public enum PassCodeControllerType {
case create case create
case enter case enter
case change case change
@ -47,9 +47,9 @@ public enum PassCodeControllerState {
} }
/// Base view controller that operates with pass code /// Base view controller that operates with pass code
open class BasePassCodeViewController: UIViewController, LegacyConfigurableController { open class BasePassCodeViewController: UIViewController, ConfigurableController {
public var viewModel: BasePassCodeViewModel! // swiftlint:disable:this implicitly_unwrapped_optional public var viewModel: BasePassCodeViewModel!
// MARK: - IBOutlets // MARK: - IBOutlets
@ -100,7 +100,7 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
return return
} }
NotificationCenter.default.rx.notification(UIApplication.willResignActiveNotification) NotificationCenter.default.rx.notification(.UIApplicationWillResignActive)
.subscribe(onNext: { [weak self] _ in .subscribe(onNext: { [weak self] _ in
self?.resetUI() self?.resetUI()
}) })
@ -150,6 +150,16 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
} }
} }
private func showBiometricsRequestIfNeeded() {
guard viewModel.isBiometricsEnabled && viewModel.controllerType == .enter else {
return
}
viewModel.authenticateUsingBiometrics(with: biometricsAuthorizationHint,
fallback: biometricsFallbackButtonTitle,
cancel: biometricsCancelButtonTitle)
}
private func resetUI() { private func resetUI() {
resetDotsUI() resetDotsUI()
viewModel.reset() viewModel.reset()
@ -201,8 +211,7 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
.sorted { $0.delay < $1.delay } .sorted { $0.delay < $1.delay }
.map { [weak self] delayedDescription in .map { [weak self] delayedDescription in
Observable<Int> Observable<Int>
.interval(.seconds(Int(delayedDescription.delay)), .interval(delayedDescription.delay, scheduler: MainScheduler.instance)
scheduler: MainScheduler.instance)
.take(1) .take(1)
.do(onNext: { _ in .do(onNext: { _ in
self?.errorLabel?.attributedText = delayedDescription.description self?.errorLabel?.attributedText = delayedDescription.description
@ -242,17 +251,6 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
fakeTextField.resignFirstResponder() fakeTextField.resignFirstResponder()
} }
/// Show biometrics system UI if applicable
public func showBiometricsRequestIfNeeded() {
guard viewModel.isBiometricsEnabled && viewModel.operationType == .enter else {
return
}
viewModel.authenticateUsingBiometrics(with: biometricsAuthorizationHint,
fallback: biometricsFallbackButtonTitle,
cancel: biometricsCancelButtonTitle)
}
// MARK: - ConfigurableController // MARK: - ConfigurableController
open func bindViews() { open func bindViews() {
@ -261,7 +259,7 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
self?.setStates(for: text ?? "") self?.setStates(for: text ?? "")
self?.hideError() self?.hideError()
}) })
.delay(.milliseconds(100)) // time to draw dots .delay(0.1) // time to draw dots
.drive(onNext: { [weak self] text in .drive(onNext: { [weak self] text in
self?.viewModel.setPassCodeText(text) self?.viewModel.setPassCodeText(text)
}) })
@ -290,11 +288,12 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
open func addViews() {} open func addViews() {}
open func configureAppearance() {} open func setAppearance() {}
open func configureBarButtons() {} open func configureBarButtons() {}
open func localize() {} open func localize() {}
} }
// MARK: - UITextFieldDelegate // MARK: - UITextFieldDelegate
@ -308,4 +307,5 @@ extension BasePassCodeViewController: UITextFieldDelegate {
let invalid = CharacterSet(charactersIn: "0123456789").inverted let invalid = CharacterSet(charactersIn: "0123456789").inverted
return string.rangeOfCharacter(from: invalid, options: [], range: string.startIndex..<string.endIndex) == nil return string.rangeOfCharacter(from: invalid, options: [], range: string.startIndex..<string.endIndex) == nil
} }
} }

View File

@ -20,6 +20,7 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
import LeadKit
import RxSwift import RxSwift
import RxCocoa import RxCocoa
@ -30,9 +31,9 @@ public enum PassCodeAuthType {
} }
/// Base view model for passCodeViewController /// Base view model for passCodeViewController
open class BasePassCodeViewModel { open class BasePassCodeViewModel: BaseViewModel {
public let operationType: PassCodeOperationType public let controllerType: PassCodeControllerType
public let disposeBag = DisposeBag() public let disposeBag = DisposeBag()
@ -42,24 +43,24 @@ open class BasePassCodeViewModel {
/// Contains configuration for pass code operations /// Contains configuration for pass code operations
public let passCodeConfiguration: PassCodeConfiguration public let passCodeConfiguration: PassCodeConfiguration
private let validationResultHolder = BehaviorRelay<PassCodeValidationResult?>(value: nil) private let validationResultHolder = Variable<PassCodeValidationResult?>(nil)
public var validationResultDriver: Driver<PassCodeValidationResult?> { public var validationResultDriver: Driver<PassCodeValidationResult?> {
validationResultHolder.asDriver() return validationResultHolder.asDriver()
} }
private let passCodeControllerStateVariable = BehaviorRelay<PassCodeControllerState>(value: .enter) private let passCodeControllerStateVariable = Variable<PassCodeControllerState>(.enter)
public var passCodeControllerStateDriver: Driver<PassCodeControllerState> { public var passCodeControllerStateDriver: Driver<PassCodeControllerState> {
passCodeControllerStateVariable.asDriver() return passCodeControllerStateVariable.asDriver()
} }
private let passCodeText = BehaviorRelay<String?>(value: nil) private let passCodeText = Variable<String?>(nil)
private var attemptsNumber = 0 private var attemptsNumber = 0
private lazy var passCodeHolder: PassCodeHolderProtocol = PassCodeHolderBuilder.build(with: self.operationType) private lazy var passCodeHolder: PassCodeHolderProtocol = PassCodeHolderBuilder.build(with: self.controllerType)
public init(operationType: PassCodeOperationType, passCodeConfiguration: PassCodeConfiguration) { public init(controllerType: PassCodeControllerType, passCodeConfiguration: PassCodeConfiguration) {
self.operationType = operationType self.controllerType = controllerType
self.passCodeConfiguration = passCodeConfiguration self.passCodeConfiguration = passCodeConfiguration
bindViewModel() bindViewModel()
@ -84,17 +85,17 @@ open class BasePassCodeViewModel {
// MARK: - Public // MARK: - Public
public var passCodeTextValue: String? { public var passCodeTextValue: String? {
passCodeText.value return passCodeText.value
} }
public func setPassCodeText(_ value: String?) { public func setPassCodeText(_ value: String?) {
passCodeText.accept(value) passCodeText.value = value
} }
public func reset() { public func reset() {
passCodeText.accept(nil) passCodeText.value = nil
validationResultHolder.accept(nil) validationResultHolder.value = nil
passCodeControllerStateVariable.accept(operationType == .change ? .oldEnter : .enter) passCodeControllerStateVariable.value = controllerType == .change ? .oldEnter : .enter
attemptsNumber = 0 attemptsNumber = 0
passCodeHolder.reset() passCodeHolder.reset()
} }
@ -135,7 +136,7 @@ open class BasePassCodeViewModel {
/// Posibility to use biometrics for authentication /// Posibility to use biometrics for authentication
open var isBiometricsEnabled: Bool { open var isBiometricsEnabled: Bool {
false return false
} }
/// Notify about activation for biometrics. Remember to save user choice /// Notify about activation for biometrics. Remember to save user choice
@ -148,11 +149,12 @@ open class BasePassCodeViewModel {
/// Notify after system alert with biometrics /// Notify after system alert with biometrics
open func biometricsAuthEnds() {} open func biometricsAuthEnds() {}
} }
private extension BasePassCodeViewModel { private extension BasePassCodeViewModel {
var validationResultBinder: Binder<PassCodeValidationResult?> { var validationResultBinder: Binder<PassCodeValidationResult?> {
Binder(self) { model, validationResult in return Binder(self) { model, validationResult in
let isValid = validationResult?.isValid ?? false let isValid = validationResult?.isValid ?? false
let passCode = validationResult?.passCode let passCode = validationResult?.passCode
@ -160,13 +162,13 @@ private extension BasePassCodeViewModel {
if isValid, model.passCodeHolder.enterStep == .repeatEnter, let passCode = passCode { if isValid, model.passCodeHolder.enterStep == .repeatEnter, let passCode = passCode {
model.authSucceed(.passCode(passCode)) model.authSucceed(.passCode(passCode))
} else { } else {
model.passCodeControllerStateVariable.accept(model.passCodeHolder.enterStep) model.passCodeControllerStateVariable.value = model.passCodeHolder.enterStep
} }
} else { } else {
if isValid, let passCode = passCode { if isValid, let passCode = passCode {
model.authSucceed(.passCode(passCode)) model.authSucceed(.passCode(passCode))
} else { } else {
model.passCodeControllerStateVariable.accept(model.passCodeHolder.enterStep) model.passCodeControllerStateVariable.value = model.passCodeHolder.enterStep
} }
} }
} }
@ -180,12 +182,12 @@ extension BasePassCodeViewModel {
validateIfNeeded() validateIfNeeded()
if shouldUpdateControllerState { if shouldUpdateControllerState {
passCodeControllerStateVariable.accept(passCodeHolder.enterStep) passCodeControllerStateVariable.value = passCodeHolder.enterStep
} }
} }
private var shouldUpdateControllerState: Bool { private var shouldUpdateControllerState: Bool {
!passCodeHolder.shouldValidate || return !passCodeHolder.shouldValidate ||
!(validationResultHolder.value?.isValid ?? true) || !(validationResultHolder.value?.isValid ?? true) ||
validationResultHolder.value?.error?.isTooManyAttempts ?? false validationResultHolder.value?.error?.isTooManyAttempts ?? false
} }
@ -195,41 +197,28 @@ extension BasePassCodeViewModel {
return return
} }
switch passCodeHolder.type {
case .create where passCodeHolder.enterStep == .repeatEnter:
attemptsNumber += 1
case .change where passCodeHolder.enterStep == .repeatEnter:
attemptsNumber += 1
case .enter:
attemptsNumber += 1
default:
break
}
var validationResult = passCodeHolder.validate() var validationResult = passCodeHolder.validate()
// if entered (in .enter mode) code is invalid -> .wrongCode let passCodeValidationForPassCodeChange = passCodeHolder.type == .change && passCodeHolder.enterStep == .newEnter
if passCodeHolder.type == .enter,
let passCode = validationResult.passCode,
!isEnteredPassCodeValid(passCode) {
let remainingAttemptsCount = passCodeConfiguration.maxAttemptsNumber - attemptsNumber if passCodeHolder.type == .enter || passCodeValidationForPassCodeChange {
validationResult = .invalid(.wrongCode(attemptsRemaining: remainingAttemptsCount)) attemptsNumber += 1
}
// if entered code (in any mode) is mismatched too many times -> .tooManyAttempts if let passCode = validationResult.passCode, !isEnteredPassCodeValid(passCode) {
if (!validationResult.isValid && attemptsNumber == passCodeConfiguration.maxAttemptsNumber) || validationResult = .invalid(.wrongCode(passCodeConfiguration.maxAttemptsNumber - attemptsNumber))
attemptsNumber > passCodeConfiguration.maxAttemptsNumber { }
validationResult = .invalid(.tooManyAttempts(type: operationType))
if (!validationResult.isValid && attemptsNumber == Int(passCodeConfiguration.maxAttemptsNumber)) ||
attemptsNumber > Int(passCodeConfiguration.maxAttemptsNumber) {
validationResult = .invalid(.tooManyAttempts)
}
} }
if !validationResult.isValid { if !validationResult.isValid {
passCodeHolder.reset() passCodeHolder.reset()
} }
validationResultHolder.accept(validationResult) validationResultHolder.value = validationResult
} }
} }

View File

@ -35,4 +35,5 @@ public extension BaseTextFieldViewEvents {
self.init(textChangedDriver: textChangedDriver) self.init(textChangedDriver: textChangedDriver)
} }
} }

View File

@ -0,0 +1,31 @@
//
// Copyright (c) 2018 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 TableKit
extension LabelTableViewCell: ConfigurableCell {
public func configure(with viewModel: LabelCellViewModel) {
configureLabelCell(with: viewModel)
}
}

View File

@ -20,32 +20,14 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
import LeadKit import Foundation
public protocol LegacyConfigurableController: InitializableView { public extension PinLayoutCell {
associatedtype ViewModelT func initializeCell() {
var viewModel: ViewModelT! { get } // swiftlint:disable:this implicitly_unwrapped_optional
func configureBarButtons()
func initialLoadView()
}
public extension LegacyConfigurableController where Self: UIViewController {
func initializeView() {
assertionFailure("Use \(String(describing: initialLoadView)) for UIViewController instead!")
}
/// Method that should be called in viewDidLoad method of UIViewController.
func initialLoadView() {
addViews() addViews()
configureLayout()
configureAppearance() configureAppearance()
configureBarButtons() configureLayout()
localize()
bindViews()
} }
} }

View File

@ -15,6 +15,7 @@ public extension Error {
/// Returns: true if server json response is not valid /// Returns: true if server json response is not valid
var isResponseSerializationError: Bool { var isResponseSerializationError: Bool {
(self as? AFError)?.isResponseSerializationError ?? false return (self as? AFError)?.isResponseSerializationError ?? false
} }
} }

View File

@ -26,8 +26,9 @@ public extension UIBarButtonItem {
/// Creates activity indicator view and bar button item (based on activity indicator) /// Creates activity indicator view and bar button item (based on activity indicator)
static var activityIndicator: (barButton: UIBarButtonItem, activityIndicator: UIActivityIndicatorView) { static var activityIndicator: (barButton: UIBarButtonItem, activityIndicator: UIActivityIndicatorView) {
let indicatorView = UIActivityIndicatorView(style: .white) let indicatorView = UIActivityIndicatorView(activityIndicatorStyle: .white)
let indicatorBar = UIBarButtonItem(customView: indicatorView) let indicatorBar = UIBarButtonItem(customView: indicatorView)
return (indicatorBar, indicatorView) return (indicatorBar, indicatorView)
} }
} }

View File

@ -32,7 +32,7 @@ public extension UserDefaults {
/// Default place to store session id /// Default place to store session id
var sessionId: String? { var sessionId: String? {
get { get {
string(forKey: Keys.sessionId) return string(forKey: Keys.sessionId)
} }
set { set {
set(newValue, forKey: Keys.sessionId) set(newValue, forKey: Keys.sessionId)
@ -42,10 +42,11 @@ public extension UserDefaults {
/// Default place to store userLogin /// Default place to store userLogin
var userLogin: String? { var userLogin: String? {
get { get {
string(forKey: Keys.userLogin) return string(forKey: Keys.userLogin)
} }
set { set {
set(newValue, forKey: Keys.userLogin) set(newValue, forKey: Keys.userLogin)
} }
} }
} }

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.3.13</string> <string>0.0.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>

View File

@ -0,0 +1,60 @@
//
// Copyright (c) 2018 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 CoreGraphics.CGBase
/// Protocol with methods for configuration and layout cell.
public protocol PinLayoutCell {
/// Method is called when cell initialized from nib or from code.
func initializeCell()
/// Method for adding subviews to content view.
///
/// - Returns: Nothing.
func addViews()
/// Method for cofiguring appearance of views.
///
/// - Returns: Nothing.
func configureAppearance()
/// Method for cofiguring layout and layout properties.
///
/// - Returns: Nothing.
func configureLayout()
/// Method is called during layout calls.
///
/// - Returns: Nothing.
func layout()
/// Method for calculating best-fitting cell size.
///
/// - Parameter containerSize: The size for which the view should calculate its best-fitting size.
/// - Returns: Best-fitting size.
func layout(with containerSize: CGSize) -> CGSize
/// Current content height.
var contentHeight: CGFloat { get }
}

View File

@ -30,4 +30,5 @@ public protocol OnlineValidateable {
/// May contain an error if validation did failed. /// May contain an error if validation did failed.
var error: Error? { get } var error: Error? { get }
} }

View File

@ -39,7 +39,7 @@ open class BasePassCodeService {
/// Override to set specific keychain service name /// Override to set specific keychain service name
open class var keychainServiceString: String { open class var keychainServiceString: String {
Bundle.main.bundleIdentifier ?? "" return Bundle.main.bundleIdentifier ?? ""
} }
public init() { public init() {
@ -55,21 +55,22 @@ open class BasePassCodeService {
private lazy var keychain = Keychain(service: BasePassCodeService.keychainServiceString).synchronizable(false) private lazy var keychain = Keychain(service: BasePassCodeService.keychainServiceString).synchronizable(false)
private var passCodeHash: String? { private var passCodeHash: String? {
keychain[Keys.passCodeHash] return keychain[Keys.passCodeHash]
} }
} }
public extension BasePassCodeService { public extension BasePassCodeService {
/// Indicates is pass code already saved on this device /// Indicates is pass code already saved on this device
var isPassCodeSaved: Bool { var isPassCodeSaved: Bool {
keychain[Keys.passCodeHash] != nil return keychain[Keys.passCodeHash] != nil
} }
/// Possibility to authenticate via biometrics. TouchID or FaceID /// Possibility to authenticate via biometrics. TouchID or FaceID
var isBiometricsAuthorizationEnabled: Bool { var isBiometricsAuthorizationEnabled: Bool {
get { get {
keychain[Keys.isBiometricsEnabled] == Values.biometricsEnabled return keychain[Keys.isBiometricsEnabled] == Values.biometricsEnabled
} }
set { set {
keychain[Keys.isBiometricsEnabled] = newValue ? Values.biometricsEnabled : nil keychain[Keys.isBiometricsEnabled] = newValue ? Values.biometricsEnabled : nil
@ -87,7 +88,7 @@ public extension BasePassCodeService {
/// Check if pass code is correct /// Check if pass code is correct
func check(passCode: String) -> Bool { func check(passCode: String) -> Bool {
passCode.sha256() == passCodeHash return passCode.sha256() == passCodeHash
} }
/// Reset pass code settings /// Reset pass code settings
@ -95,4 +96,5 @@ public extension BasePassCodeService {
save(passCode: nil) save(passCode: nil)
isBiometricsAuthorizationEnabled = false isBiometricsAuthorizationEnabled = false
} }
} }

View File

@ -21,6 +21,7 @@
// //
import RxSwift import RxSwift
import LeadKit
/// Represents service that store basic user information /// Represents service that store basic user information
open class BaseUserService { open class BaseUserService {
@ -50,7 +51,7 @@ open class BaseUserService {
/// Indicates if user is logged in /// Indicates if user is logged in
open var isLoggedIn: Bool { open var isLoggedIn: Bool {
UserDefaults.standard.sessionId != nil return UserDefaults.standard.sessionId != nil
} }
/// Reset user information /// Reset user information
@ -58,4 +59,5 @@ open class BaseUserService {
UserDefaults.standard.sessionId = nil UserDefaults.standard.sessionId = nil
UserDefaults.standard.userLogin = nil UserDefaults.standard.userLogin = nil
} }
} }

View File

@ -27,52 +27,15 @@ public typealias BiometricsAuthHandler = (Bool, Error?) -> Void
/// Service that provide access to authentication via biometric /// Service that provide access to authentication via biometric
public final class BiometricsService { public final class BiometricsService {
public enum BiometryType {
case faceID
case touchID
case none
}
private lazy var laContext = LAContext() private lazy var laContext = LAContext()
/// If the user unlocks the device using biometrics within the specified time interval, /// Returns true if user can authenticate via faceID
/// then authentication for the receiver succeeds automatically, without prompting the user for biometrics. public var isFaceIdSupported: Bool {
/// Works only after device unlock event, no other apps authentications counts.
public var allowableReuseDuration: TimeInterval? {
didSet {
guard let duration = allowableReuseDuration else {
return
}
if #available(iOS 9.0, *) {
laContext.touchIDAuthenticationAllowableReuseDuration = duration
}
}
}
/// Returns BiometryType supporting by device: TouchID, FaceID or none
public var biometryType: BiometryType {
let canEvaluate = canAuthenticateWithBiometrics
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
switch laContext.biometryType { return canAuthenticateWithBiometrics && laContext.biometryType == .faceID
case .touchID: } else {
return .touchID return false
case .faceID:
return .faceID
case .none:
return .none
@unknown default:
return .none
}
} }
return canEvaluate ? .touchID : .none
} }
/// Returns current domain state /// Returns current domain state
@ -82,22 +45,19 @@ public final class BiometricsService {
return laContext.evaluatedPolicyDomainState return laContext.evaluatedPolicyDomainState
} }
/// Indicates is it possible to authenticate on this device via any biometric. /// Indicates is it possible to authenticate on this device via touch id
/// Returns false if not enrolled or lockedout.
public var canAuthenticateWithBiometrics: Bool { public var canAuthenticateWithBiometrics: Bool {
laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) return laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
} }
/** /**
Initiates system biometrics authentication process. Initiates system biometrics authentication process
Once evaluated, will return success until the context is deallocated.
Call "clear" to use a new context instance.
- parameters: - parameters:
- description: prompt on the system alert - description: prompt on the system alert
- fallback: alternative action button title on system alert - fallback: alternative action button title on system alert
- cancel: cancel button title on the system alert - cancel: cancel button title on the system alert
- authHandler: callback, with parameter, indicates if user authenticate successfuly - authHandler: callback, with parameter, indicates if user authenticate successfuly
*/ */
public func authenticateWithBiometrics(with description: String, public func authenticateWithBiometrics(with description: String,
fallback fallbackTitle: String?, fallback fallbackTitle: String?,
@ -113,10 +73,6 @@ public final class BiometricsService {
} }
} }
/// Replace old instance of the context with the new one
public func clear() {
laContext = LAContext()
}
public init() {} public init() {}
} }

View File

@ -32,4 +32,5 @@ public enum OnlineValidationState {
case processing case processing
case valid case valid
case invalid(error: Error?) case invalid(error: Error?)
} }

View File

@ -11,4 +11,5 @@ public struct ValidationError: Error {
self.errorMessage = errorMessage self.errorMessage = errorMessage
self.errorHint = errorHint self.errorHint = errorHint
} }
} }

View File

@ -15,7 +15,6 @@ public extension ValidationItemState {
switch self { switch self {
case .initial: case .initial:
return true return true
default: default:
return false return false
} }
@ -25,26 +24,26 @@ public extension ValidationItemState {
switch self { switch self {
case .valid: case .valid:
return true return true
default: default:
return false return false
} }
} }
} }
public final class ValidationItem { public final class ValidationItem {
private let disposeBag = DisposeBag() private let disposeBag = DisposeBag()
private let validationStateHolder = BehaviorRelay<ValidationItemState>(value: .initial) private let validationStateHolder = Variable<ValidationItemState>(.initial)
public var validationState: ValidationItemState { public var validationState: ValidationItemState {
validationStateHolder.value return validationStateHolder.value
} }
public var validationStateObservable: Observable<ValidationItemState> { public var validationStateObservable: Observable<ValidationItemState> {
validationStateHolder.asObservable() return validationStateHolder.asObservable()
} }
private let text = BehaviorRelay<String?>(value: nil) private let text = Variable<String?>(nil)
private(set) var rules: [Rule] = [] private(set) var rules: [Rule] = []
@ -59,8 +58,7 @@ public final class ValidationItem {
.drive(text) .drive(text)
.disposed(by: disposeBag) .disposed(by: disposeBag)
textDriver.asObservable() textDriver.asObservable().withLatestFrom(validationStateHolder.asObservable()) { (text: $0, validationState: $1) }
.withLatestFrom(validationStateHolder.asObservable()) { (text: $0, validationState: $1) }
.filter { !$0.validationState.isInitial } .filter { !$0.validationState.isInitial }
.map { $0.text } .map { $0.text }
.subscribe(onNext: { [weak self] text in .subscribe(onNext: { [weak self] text in
@ -90,13 +88,13 @@ public final class ValidationItem {
.correction where !isManual, .correction where !isManual,
.valid where !isManual: .valid where !isManual:
validationStateHolder.accept(.correction(validationError)) validationStateHolder.value = .correction(validationError)
default: default:
validationStateHolder.accept(.error(validationError)) validationStateHolder.value = .error(validationError)
} }
} else { } else {
validationStateHolder.accept(.valid) validationStateHolder.value = .valid
} }
} }
} }

View File

@ -17,8 +17,9 @@ public enum ValidationServiceState {
public extension ValidationServiceState { public extension ValidationServiceState {
var isValid: Bool { var isValid: Bool {
self == .valid return self == .valid
} }
} }
public final class ValidationService { public final class ValidationService {
@ -27,12 +28,12 @@ public final class ValidationService {
private(set) var validationItems: [ValidationItem] = [] private(set) var validationItems: [ValidationItem] = []
private let stateHolder = BehaviorRelay<ValidationServiceState>(value: .initial) private let stateHolder = Variable<ValidationServiceState>(.initial)
public var state: ValidationServiceState { public var state: ValidationServiceState {
stateHolder.value return stateHolder.value
} }
public var stateObservable: Observable<ValidationServiceState> { public var stateObservable: Observable<ValidationServiceState> {
stateHolder.asObservable() return stateHolder.asObservable()
} }
private var validationStateReactType: ValidationServiceStateReactType = .none private var validationStateReactType: ValidationServiceStateReactType = .none
@ -61,7 +62,7 @@ public final class ValidationService {
public func unregister(items: [ValidationItem]) { public func unregister(items: [ValidationItem]) {
items.forEach { item in items.forEach { item in
if let removeIndex = validationItems.firstIndex(where: { $0 === item }) { if let removeIndex = validationItems.index(where: { $0 === item }) {
validationItems.remove(at: removeIndex) validationItems.remove(at: removeIndex)
} }
} }
@ -71,7 +72,7 @@ public final class ValidationService {
public func validate() -> Bool { public func validate() -> Bool {
validationStateReactType = .all validationStateReactType = .all
let isValid = validationItems.allSatisfy { $0.manualValidate() } let isValid = validationItems.map { $0.manualValidate() }.reduce(true) { $0 && $1 }
validationStateReactType = .each validationStateReactType = .each
return isValid return isValid
@ -84,26 +85,23 @@ public final class ValidationService {
let zipStates = Observable let zipStates = Observable
.zip(allValidationStateObservables) { $0 } .zip(allValidationStateObservables) { $0 }
.filter { [weak self] _ in .filter { [weak self] _ in self?.validationStateReactType == .all }
self?.validationStateReactType == .all
}
let combineLatestStates = Observable let combineLatestStates = Observable
.combineLatest(allValidationStateObservables) { $0 } .combineLatest(allValidationStateObservables) { $0 }
.filter { [weak self] _ in .filter { [weak self] _ in self?.validationStateReactType == .each }
self?.validationStateReactType == .each
}
let stateObservables = [zipStates, combineLatestStates] let stateObservables = [zipStates, combineLatestStates]
stateObservables.forEach { observable in stateObservables.forEach { observable in
observable observable
.map { states -> Bool in .map { states -> Bool in
states.allSatisfy { $0.isValid } states.map { $0.isValid }.reduce(true) { $0 && $1 }
} }
.map { $0 ? ValidationServiceState.valid : .invalid } .map { $0 ? ValidationServiceState.valid : .invalid }
.bind(to: stateHolder) .bind(to: stateHolder)
.disposed(by: disposeBag) .disposed(by: disposeBag)
} }
} }
} }

@ -1 +1 @@
Subproject commit 2799169e2103c034b0e1be221860a23292766f36 Subproject commit 622b2d34508fe0d4103af643bc0d91c75f7bf026

View File

@ -1,14 +0,0 @@
#!/bin/sh
readonly CONFIG_PATH=${PROJECT_DIR}/build-scripts/xcode/.swiftlint.yml
readonly SWIFTLINT_VERSION=0.45.1
readonly SWIFTLINT_PORTABLE_FILENAME=portable_swiftlint.zip
readonly SWIFTLINT_PORTABLE_URL=https://github.com/realm/SwiftLint/releases/download/${SWIFTLINT_VERSION}/${SWIFTLINT_PORTABLE_FILENAME}
. build-scripts/xcode/aux_scripts/download_file.sh ${SWIFTLINT_PORTABLE_FILENAME} ${SWIFTLINT_PORTABLE_URL} Downloads --remove-cached
cd Downloads && unzip -o ${SWIFTLINT_PORTABLE_FILENAME}
${PROJECT_DIR}/Downloads/swiftlint autocorrect --path ${PROJECT_DIR}/Sources --config ${CONFIG_PATH} && ${PROJECT_DIR}/Downloads/swiftlint --path ${PROJECT_DIR}/Sources --config ${CONFIG_PATH}