Compare commits

...

143 Commits

Author SHA1 Message Date
Ivan Smolin 4c4db7e5a5
Merge pull request #66 from TouchInstinct/feature/xcode_13_1
update libraries to build with Xcode 13.1; bump version
2022-01-26 20:26:33 +03:00
Ivan Smolin b6fe2474f5 update libraries to build with Xcode 13.1; bump version 2022-01-26 18:14:05 +03:00
Nikita 91d4eb2dd9
Merge pull request #65 from TouchInstinct/feature/podspecs_update
Updated podspecs
2020-09-03 12:06:09 +03:00
krasich74 42afb31d73 Updated podspecs 2020-09-03 10:32:40 +03:00
Nikita e22c6813cb
Merge pull request #64 from TouchInstinct/feature/podspecs_update
Updated podspecs
2020-09-02 10:55:04 +03:00
krasich74 0e99ac3ccc updated podspecs 2020-09-01 17:28:13 +03:00
Ivan Smolin de0b3b9da7
Merge pull request #63 from TouchInstinct/carthage__without_binary
Carthage  without binary
2020-01-23 12:11:08 +03:00
Ivan Smolin 6daa551dd3 update changelog 2020-01-22 20:07:27 +03:00
Ivan Smolin ffffc847f7 update LeadKit version 2020-01-22 20:06:33 +03:00
Ivan Smolin 4904449b85 remove Romefile 2020-01-22 19:04:59 +03:00
Ivan Smolin 131d3c7b78 fix swiftlint 2020-01-22 14:12:35 +03:00
Ivan Smolin 4fd075cbc8 add PinLayout 2020-01-16 12:49:17 +03:00
Ivan Smolin b91144f0ff update .gitignore 2020-01-15 22:03:05 +03:00
Ivan Smolin 8320179e01 fix linter warnings 2020-01-15 22:01:54 +03:00
Ivan Smolin cb8a37f9c9 add Romefile 2020-01-15 21:13:13 +03:00
Ivan Smolin 0f0a1861f1 add forgotten KeychainAccess 2020-01-15 21:13:07 +03:00
Ivan Smolin fc168d6d24 update dependencies, pin swiftlint version 2020-01-14 13:35:16 +03:00
Ivan Smolin 4006a3570f carthage without binaries 2020-01-13 19:07:53 +03:00
Pavel Lukandiy c3be916033
Merge pull request #62 from TouchInstinct/feature/pin_layout_removal
Feature/pin layout removal
2019-04-25 13:22:53 +03:00
Pavel Lukandiy beecb2955b Podspec update 2019-04-17 16:34:21 +03:00
Pavel Lukandiy 817be88e76 Podspec update 2019-04-17 15:32:48 +03:00
Pavel Lukandiy 98719044b0 Version update 2019-04-15 15:32:03 +03:00
Pavel Lukandiy 246bd21260 Removed pin cell 2019-04-15 15:29:12 +03:00
Ivan Smolin a63bfe6e7f
Merge pull request #61 from TouchInstinct/swift_5
migrate to swift 5
2019-03-28 12:46:42 +03:00
Ivan Smolin 61ae9439d3 migrate to swift 5 2019-03-28 12:41:09 +03:00
Ivan Smolin ee4c9cd6d9
Merge pull request #60 from TouchInstinct/feature/carthage
Feature/carthage
2019-02-11 14:35:47 +03:00
Ivan Smolin 0c2acda3c6 update swiftlint lint rules, podspec and changelog 2019-02-11 14:13:24 +03:00
Ivan Smolin 3d17f139c3 fix swiftlint script 2019-02-08 18:56:27 +03:00
Ivan Smolin 3830e59b69 add shared scheme 2019-02-08 18:52:46 +03:00
Ivan Smolin 498c0b970e remove extension target. move to carthage 2019-02-08 18:48:14 +03:00
Ivan Zinovyev b634eb0162
Merge pull request #59 from TouchInstinct/fix_changelog
Fix readme
2019-01-25 16:33:56 +03:00
Ivan Zinovyev 3d6a4d39f3 Fix readme 2019-01-25 16:30:21 +03:00
Ivan Zinovyev e42fece1c6
Merge pull request #58 from TouchInstinct/feature/biometry_type
Add BiometryType for BiometricsService
2019-01-25 15:49:24 +03:00
Ivan Zinovyev 7ff1786ab3 Add BiometryType for BiometricsService 2019-01-25 15:28:04 +03:00
SoriUR 4843f6b60d
Merge pull request #57 from SoriUR/feature/BiometricsService
Feature/biometrics service
2019-01-21 14:46:46 +03:00
Iurii 611046e649 increment podspec version 2019-01-21 06:19:01 +03:00
Iurii 70f0d35cdb update changelog 2019-01-21 06:16:55 +03:00
Iurii a4ff6a667a recreate the context 2019-01-21 05:44:07 +03:00
Iurii fb86b0ffe9 Add a comment 2019-01-21 05:37:05 +03:00
Iurii a60d211a15 Add a functions to service 2019-01-21 05:33:17 +03:00
Alexey Gerasimov 554a890fce
Merge pull request #56 from TouchInstinct/fix/validator
Fix/validator
2018-12-03 12:53:57 +03:00
scoreyou d42aeec894 Extensions target clear 2018-12-03 12:51:16 +03:00
scoreyou 3cdabcadd7 New validator fork linked 2018-12-03 12:42:55 +03:00
Alexey Gerasimov bbef78ccd7
Merge pull request #55 from TouchInstinct/fix/passCodeCount
TooManyAttempts logic fixed
2018-10-24 13:32:16 +03:00
Alexey Gerasimov 94269b86e5 TooManyAttempts logic fixed 2018-10-24 13:29:36 +03:00
Alexey Gerasimov 9b5c16b7da
Merge pull request #54 from TouchInstinct/feature/passCodeCount
Feature/pass code count
2018-10-22 18:33:43 +03:00
Alexey Gerasimov 94aaeba4ad Version update 2018-10-22 16:38:03 +03:00
Alexey Gerasimov d8285db06f Forgotten public 2018-10-22 16:28:40 +03:00
Alexey Gerasimov aec1b23df7 Too many attempts logic updated 2018-10-22 16:24:55 +03:00
Alexey Gerasimov 77309f0b77 Make showBiometricsRequestIfNeeded method public 2018-10-22 15:48:03 +03:00
Alexey Gerasimov 5c4ba04e2c LegacyConfigurableController PassCode fix 2018-10-22 15:44:47 +03:00
Alexey Gerasimov c8a6000b25 PassCodeControllerType renamed 2018-10-22 15:44:17 +03:00
iON1k 36d8602e8f
Merge pull request #53 from TouchInstinct/feature/swift4_2_wip
Swift 4.2
2018-10-15 17:03:16 +03:00
Anton Popkov 4f9658ecbf LeadKit updating 2018-10-15 16:05:47 +03:00
Anton Popkov 050f4439d5 Podspec update 2018-10-15 14:36:53 +03:00
Anton Popkov 6c39765788 LeadKit version update 2018-10-15 14:36:40 +03:00
Anton Popkov ca8581bb9a Pods update 2018-10-15 13:05:08 +03:00
Ivan Smolin 3486f990c1 migration to swift 4.2 and LeadKit 0.8.13 2018-10-15 12:36:33 +03:00
Aliona 9399fcb511
Merge pull request #52 from TouchInstinct/add_touch_id_recognition
Add isTouchId to BiometricsService
2018-07-25 16:18:54 +03:00
Aliona af4ca742c0
Update BiometricsService.swift 2018-07-25 16:04:52 +03:00
Aliona c27a031dcc Move isFaceIdSupported to Leadkit 2018-07-25 16:01:07 +03:00
Aliona b6c8200015
Merge pull request #51 from TouchInstinct/biometrics_service_upgrade
Add public init to biometricsService
2018-07-24 18:24:04 +03:00
Aliona c11573e493 Add public init to biometricsService 2018-07-24 18:22:14 +03:00
Andrey Ovsyannikov f5349272af
Merge pull request #50 from TouchInstinct/fix/pass_code
BasePassCodeViewController
2018-07-18 18:42:43 +03:00
Madhas 1570ce331d add methods to BasePassCodeViewController to make fakeTextField become and resign first responder 2018-07-18 18:32:35 +03:00
Aliona 1f8f0dc893
Merge pull request #48 from TouchInstinct/exclude_file
Exclude stuff
2018-06-26 15:30:23 +03:00
Aliona d43746777a
Update LeadKitAdditions.podspec 2018-06-26 00:22:29 +03:00
Aliona 1d54d44670
Update CHANGELOG.md 2018-06-25 22:07:05 +03:00
Aliona 4b87230329
Add version 2018-06-25 22:02:56 +03:00
Aliona 0cf938faad Merge remote-tracking branch 'origin/exclude_file' into exclude_file 2018-06-25 20:32:37 +03:00
Aliona 23f784019b Add cells 2018-06-25 20:32:28 +03:00
Aliona 48a31c4847
Update LeadKitAdditions.podspec 2018-06-25 20:27:53 +03:00
Aliona c35c768faa Revert "Exclude stuff"
This reverts commit 0dd82d15de.
2018-06-25 19:57:54 +03:00
Aliona f3d41ac0f9 Add separator cell 2018-06-25 19:57:29 +03:00
Aliona a6514267e6 Remove version 2018-06-25 18:08:07 +03:00
Aliona 0dd82d15de Exclude stuff 2018-06-25 17:20:23 +03:00
Andrey Ovsyannikov 061cc82741
Merge pull request #47 from TouchInstinct/test
Codable blast zone
2018-05-28 14:56:47 +03:00
Madhas ab19e3cc91 changelog updated 2018-05-28 14:22:20 +03:00
Madhas 3c9ec27f42 podspec updated 2018-05-28 14:11:50 +03:00
Madhas 29ce2bc74b remove network and object mapper stuff 2018-05-25 17:59:41 +03:00
Madhas 6f7c4f27e1 buid-scripts submodule added 2018-05-25 17:52:50 +03:00
Alexey Gerasimov 9b6357a170 Try to remove version dependency 2018-05-25 16:40:58 +03:00
Pavel 24839fdc25
Merge pull request #46 from TouchInstinct/feature/domain_state_property
Feature/domain state property
2018-05-24 12:27:43 +03:00
Pavel Lukandiy 7789dc9be1 Version up. Podspec update 2018-05-23 20:07:27 +03:00
Pavel Lukandiy b7d751baeb Description added 2018-05-23 19:56:51 +03:00
Pavel Lukandiy 3c3f28f766 Removed public init 2018-05-23 19:44:27 +03:00
Pavel Lukandiy 412f7ccd00 Initialization and description 2018-05-23 18:52:23 +03:00
Pavel Lukandiy dd9a9d6d34 Added domain state property 2018-05-23 18:15:16 +03:00
Ivan Smolin 128fa2826a
Merge pull request #45 from TouchInstinct/feature/pass_code_changes
Pass code changes and dependencies update
2018-04-23 11:06:35 +03:00
Ivan Smolin 90324c49e2 - Update: validationResult of BasePassCodeViewModel renamed to validationResultDriver and becomes public.
- Remove: MaskFieldTextProxy.
- Remove: InputMask dependency
- Replace: IDZSwiftCommonCrypto with CryptoSwift
2018-04-20 19:06:07 +03:00
Ivan Smolin a4fcd2026c
Merge pull request #44 from TouchInstinct/fix/build
fix build with new LeadKit
2018-04-11 19:51:43 +03:00
Ivan Smolin fa6684830e fix build with new LeadKit 2018-04-11 19:46:07 +03:00
Alexey Gerasimov 5937727493
Merge pull request #43 from TouchInstinct/fix/networkService
DefaultNetworkService refactor
2018-04-06 14:21:26 +03:00
Alexey Gerasimov 6d51575c38 DefaultNetworkService refactor 2018-04-06 14:19:09 +03:00
Alexey Gerasimov c9697df89e
Merge pull request #42 from TouchInstinct/feature/biometrics
Feature/biometrics
2018-04-05 13:38:05 +03:00
Alexey Gerasimov 4bd3bc7b5f Changelog and version 2018-04-05 13:25:43 +03:00
Alexey Gerasimov 52aa86c123 Merge branch 'master' into feature/biometrics 2018-04-05 13:24:05 +03:00
Alexey Gerasimov 5494db6ae9 Biometrics start and end notifications added 2018-04-05 13:22:57 +03:00
Ivan Smolin cf38f0905f
Merge pull request #41 from TouchInstinct/fix/pods
update podfile.lock and podspec
2018-04-05 13:09:26 +03:00
Ivan Smolin 7e22545762 update podfile.lock and podspec 2018-04-05 13:06:29 +03:00
Ivan Smolin 03123ae15b
Merge pull request #40 from TouchInstinct/feature/pin_layout_cells
Add LabelTableViewCell
2018-04-05 11:40:35 +03:00
Ivan Smolin 671000c217 Add PinLayout` dependency.
Add PinLayoutTableViewCell, SeparatorTableViewCell and LabelTableViewCell powered by PinLayout.
Add LabelCellViewModel default view model for label cell.
Add Playground to project.
2018-04-02 20:39:07 +03:00
Ivan Smolin dc16eb63b9
Merge pull request #39 from TouchInstinct/feature/xcode_migration
Feature/xcode migration
2018-04-02 16:11:19 +03:00
Ivan Smolin 7229194481 update to LeadKit 0.7.4 2018-04-02 15:58:51 +03:00
Ivan Smolin 2ff40285c6 Xcode 9.3 migration 2018-04-02 15:13:22 +03:00
Alexey Gerasimov c17742cd8b
Merge pull request #38 from TouchInstinct/fix/passCode
Delayed messages implemented
2018-03-30 17:23:56 +03:00
Alexey Gerasimov 4ef7dd0e19 Delayed messages implemented 2018-03-30 17:13:11 +03:00
Alexey Gerasimov a1483eeee9
Merge pull request #37 from TouchInstinct/fix/highlight
Fix/highlight
2018-03-30 14:47:57 +03:00
Alexey Gerasimov 63d748a46e Version incremented 2018-03-30 14:44:36 +03:00
Alexey Gerasimov eeeac04989 Try delay 2018-03-30 14:39:18 +03:00
Alexey Gerasimov ec8966a1fb Pin input throttled 2018-03-30 14:31:20 +03:00
Ivan Smolin 6adb4a1838
Merge pull request #36 from TouchInstinct/feature/text_field_view_model
base implementation of text cell view model
2018-03-27 20:53:31 +03:00
Ivan Smolin 0fbf19574c update pods and changelog 2018-03-27 20:06:20 +03:00
Ivan Smolin 7316f8f6a7 code review note 2018-03-27 18:13:04 +03:00
Ivan Smolin 46933ff42d code review note 2018-03-27 17:49:53 +03:00
Ivan Smolin dc0343993a linter 2018-03-27 11:38:09 +03:00
Ivan Smolin 468adfd5be DefaultNetworkService deprecation 2018-03-27 11:33:19 +03:00
Ivan Smolin 4988dd65d9 base implementation of text cell view model. remove old implementation of same stuff 2018-03-26 23:41:30 +03:00
Igor Kislyuk 17561204c3
Merge pull request #35 from TouchInstinct/fix/general
Fix/general
2018-03-26 20:27:49 +03:00
Igor Kislyuk 11df63f2f4 Update 2018-03-26 20:22:45 +03:00
Igor Kislyuk 15ccb075b4 Update 2018-03-26 20:21:37 +03:00
Igor Kislyuk d53b4b84f6 Private outlet fix 2018-03-26 18:49:42 +03:00
Igor Kislyuk 3a104b1566 Merge branch 'master' into fix/general 2018-03-26 18:37:32 +03:00
Ivan Smolin cb84b0b68b
Merge pull request #34 from TouchInstinct/feature/linters_update
Feature/linters update
2018-03-26 18:36:31 +03:00
Ivan Smolin 09dadff106 change project structure for linter 2018-03-26 17:58:55 +03:00
Ivan Smolin 26ed7cdd6f remove code-quality, add build-scripts. fix warnings 2018-03-26 17:48:22 +03:00
Igor Kislyuk 7bc61d062c
Merge pull request #33 from TouchInstinct/fix/pincode
Update for Swift 4. Refactor
2018-03-26 16:50:01 +03:00
Igor Kislyuk 1b26dd25bd Update podspec 2018-03-26 16:49:37 +03:00
Igor Kislyuk d91a81b225 Remove empty lines 2018-03-26 16:48:05 +03:00
Igor Kislyuk e602c1db59 Update logic 2018-03-26 14:11:46 +03:00
Igor Kislyuk 5d9a456d1d Code review notes 2018-03-26 13:33:04 +03:00
Igor Kislyuk 9baa92a303 Add attempt index 2018-03-23 20:00:46 +03:00
Igor Kislyuk 8bba538e4a Renamification & configuration 2018-03-23 17:40:19 +03:00
Igor Kislyuk f612914307 Rename TouchID to biometrics 2018-03-23 16:37:03 +03:00
Igor Kislyuk 27f42e55a1 Rename to biometrics 2018-03-23 15:52:34 +03:00
Igor Kislyuk 7b6e73e789 Update name & refactor error 2018-03-23 15:21:12 +03:00
Igor Kislyuk ce2c69e854 Passcode refactoring & improvements 2018-03-23 12:48:36 +03:00
Igor Kislyuk 4861749415 Update configuration 2018-03-22 19:49:53 +03:00
Igor Kislyuk 8a01633e03 Update for swift 4 2018-03-22 19:12:43 +03:00
Pavel 126a68aa81
Merge pull request #32 from TouchInstinct/fix/default_api_response
Updated typical api response
2017-12-07 19:30:34 +03:00
Pavel Lukandiy bbf3cd6974 Updated typical api response 2017-12-07 19:12:29 +03:00
Ivan Smolin c3252b13a4
Merge pull request #30 from TouchInstinct/validation_update
update access modifiers of ValidationService
2017-12-04 13:49:08 +03:00
Ivan Smolin 5c6423d901 update access modifiers of ValidationService 2017-12-04 12:42:07 +03:00
69 changed files with 1990 additions and 2538 deletions

3
.gitignore vendored
View File

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

6
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "code-quality"] [submodule "build-scripts"]
path = code-quality path = build-scripts
url = https://github.com/TouchInstinct/code-quality-ios url = git@github.com:TouchInstinct/BuildScripts.git

View File

@ -1 +1 @@
4.0 5.0

View File

@ -1,6 +1,135 @@
# Changelog # Changelog
## 0.1.1 ### 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
- **Add**: `isFaceIdSupported` variable to `BiometricsService` to distinguish FaceID from TouchID.
### 0.3.3
- **Add**: Public `init` to `BiometricsService`
### 0.3.2
- **Add**: functions to `BasePassCodeViewController` to make `fakeTextField` become and resign first responder
### 0.3.1
- **Add**: `PinLayoutTableViewCell` and `SeparatorTableViewCell` to `Core-iOS-Extension`.
### 0.3.0
- **Remove**: `ApiResponse` class
- **Remove**: Occurrences of `ObjectMapper` in `BaseDateFormatter`
- **Remove**: `ApiError` enum and `ApiErrorProtocol`
- **Remove**: `Observable` extension related to error handling
- **Remove**: `ApiNetworkService`
- **Remove**: `DefaultNetworkService` and its extensions
### 0.2.9
- **Add**: `evaluatedPolicyDomainState` to `BiometricsService`.
### 0.2.8
- **Update**: `validationResult` of `BasePassCodeViewModel` renamed to `validationResultDriver` and becomes public.
- **Remove**: `MaskFieldTextProxy`.
- **Remove**: `InputMask` dependency
- **Replace**: `IDZSwiftCommonCrypto` with `CryptoSwift`
### 0.2.7
- **Fix**: Build with new LeadKit 0.7.9
### 0.2.6
- **Update**: `DefaultNetworkService` supported `NetworkServiceConfiguration`
### 0.2.5
- **Add**: Methods to notify when biometrics auth begins and ends.
### 0.2.4
- **Add**: `PinLayout` dependency.
- **Add**: `PinLayoutTableViewCell`, `SeparatorTableViewCell` and `LabelTableViewCell` powered by PinLayout.
- **Add**: `LabelCellViewModel` default view model for label cell.
- **Add**: Playground to project.
### 0.2.3
- **Update**: Xcode 9.3 migration.
### 0.2.2
- **Add**: `PassCodeDelayedDescription` to schedule error messages
### 0.2.1
- **Fixed**: BasePassCodeViewController doesn't draw last dot filled
## 0.2.0
- **Updated**: LeadKit to `0.7.x` version
- **Removed**: `CellField*` and `FormField*` protocols and classes.
- **Add**: `BaseTextFieldViewEvents` and `BaseTextFieldViewModelEvents` with default offline and online validation.
- **Deprecated**: `DefaultNetworkService`.
### 0.1.5
- **Update**: Passcode private configuration
### 0.1.4
- **Update**: Refactor PassCode
### 0.1.3
- **Update**: Typical api response keys naming
### 0.1.2
- **Update**: Access modifiers of `ValidationService`
### 0.1.1
- **Add**: `acceptableStatusCodes` property in `DefaultNetworkService`. - **Add**: `acceptableStatusCodes` property in `DefaultNetworkService`.
- **Add**: `retry(retryLimit:canRetryClosure:)` to `Observable` extension. - **Add**: `retry(retryLimit:canRetryClosure:)` to `Observable` extension.
@ -13,4 +142,3 @@
## 0.1.0 ## 0.1.0
- **Add**: support for Swift 3.2 / 4 - **Add**: support for Swift 3.2 / 4

5
Cartfile Normal file
View File

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

12
Cartfile.resolved Normal file
View File

@ -0,0 +1,12 @@
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,41 +1,21 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "LeadKitAdditions" s.name = "LeadKitAdditions"
s.version = "0.1.1" s.version = "0.4.0"
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, "9.0" s.platform = :ios, "10.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 = '9.0' ss.ios.deployment_target = '10.0'
ss.source_files = "LeadKitAdditions/Sources/**/*.swift" ss.source_files = "Sources/**/*.swift"
ss.exclude_files = [ ss.dependency "LeadKit", '~> 1.7.0'
"LeadKitAdditions/Sources/Services/Network/DefaultNetworkService+ActivityIndicator+Extension.swift", ss.dependency "KeychainAccess", '~> 4.2.0'
] ss.dependency "CryptoSwift", '~> 1.4.0'
ss.dependency "SwiftValidator", '4.0.2'
ss.dependency "LeadKit", '~> 0.6.0'
ss.dependency "KeychainAccess", '3.1.0'
ss.dependency "IDZSwiftCommonCrypto", '0.9.1'
ss.dependency "InputMask", '3.0.0'
ss.dependency "SwiftValidator", '5.0.0'
end
s.subspec 'Core-iOS-Extension' do |ss|
ss.platform = :ios, '9.0'
ss.source_files = "LeadKitAdditions/Sources/**/*.swift"
ss.exclude_files = [
"LeadKitAdditions/Sources/Services/Network/DefaultNetworkService+ActivityIndicator.swift",
]
ss.dependency "LeadKit/Core-iOS-Extension", '~> 0.6.0'
ss.dependency "KeychainAccess", '3.1.0'
ss.dependency "IDZSwiftCommonCrypto", '0.9.1'
ss.dependency "InputMask", '3.0.0'
ss.dependency "SwiftValidator", '5.0.0'
end end
s.default_subspec = 'Core' s.default_subspec = 'Core'

View File

@ -0,0 +1,637 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
14E3AD49502B3D81264560DB /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3EDEF7F6C22BB04CBB754C14 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */; };
67779CBC206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67779CBB206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift */; };
678D26AA206935B900B05B93 /* BiometricsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D26A9206935B900B05B93 /* BiometricsService.swift */; };
67B4E6F6206945DD00E233EA /* OnlineValidationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F5206945DC00E233EA /* OnlineValidationResult.swift */; };
67B4E6F9206945F900E233EA /* OnlineValidationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F8206945F900E233EA /* OnlineValidationState.swift */; };
67B4E6FB20694A4200E233EA /* BaseTextFieldViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B4E6F1206945D200E233EA /* BaseTextFieldViewModel.swift */; };
67D6041821627600002DAF5B /* LegacyConfigurableController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D6041721627600002DAF5B /* LegacyConfigurableController.swift */; };
786CBB2F220DD7A70017587F /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB22220DD7A70017587F /* RxSwift.framework */; };
786CBB30220DD7A70017587F /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB23220DD7A70017587F /* Alamofire.framework */; };
786CBB31220DD7A70017587F /* TableKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB24220DD7A70017587F /* TableKit.framework */; };
786CBB32220DD7A70017587F /* LeadKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB25220DD7A70017587F /* LeadKit.framework */; };
786CBB33220DD7A70017587F /* SwiftValidator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 786CBB26220DD7A70017587F /* SwiftValidator.framework */; };
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 */; };
CAE698E81E968820000394B0 /* LeadKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = CAE698E61E968820000394B0 /* LeadKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
ED0C34071F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.swift */; };
ED0C34091F2906EC00FAE9FD /* PassCodeConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D91F2906EC00FAE9FD /* PassCodeConfiguration.swift */; };
ED0C340B1F2906EC00FAE9FD /* PassCodeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DA1F2906EC00FAE9FD /* PassCodeError.swift */; };
ED0C340D1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DB1F2906EC00FAE9FD /* PassCodeHolder.swift */; };
ED0C340F1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DC1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift */; };
ED0C34111F2906EC00FAE9FD /* PassCodeValidationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DD1F2906EC00FAE9FD /* PassCodeValidationResult.swift */; };
ED0C34131F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DF1F2906EC00FAE9FD /* BasePassCodeViewController.swift */; };
ED0C34151F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */; };
ED0C341F1F2906EC00FAE9FD /* 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 */; };
ED0C342D1F2906EC00FAE9FD /* BasePassCodeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F11F2906EC00FAE9FD /* BasePassCodeService.swift */; };
ED0C342F1F2906EC00FAE9FD /* BaseUserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F21F2906EC00FAE9FD /* BaseUserService.swift */; };
ED0C343F1F2906EC00FAE9FD /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FC1F2906EC00FAE9FD /* ValidationError.swift */; };
ED0C34411F2906EC00FAE9FD /* ValidationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FD1F2906EC00FAE9FD /* ValidationItem.swift */; };
ED0C34431F2906EC00FAE9FD /* ValidationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FE1F2906EC00FAE9FD /* ValidationService.swift */; };
EF5A43B1206E7A67003CED07 /* PassCodeDelayedDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF5A43B0206E7A67003CED07 /* PassCodeDelayedDescription.swift */; };
/* End PBXBuildFile 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>"; };
3EDEF7F6C22BB04CBB754C14 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
67528BCE206E3CC6009F2525 /* iOS.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = iOS.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
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>"; };
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>"; };
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>"; };
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>"; };
786CBB22220DD7A70017587F /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = "<group>"; };
786CBB23220DD7A70017587F /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = "<group>"; };
786CBB24220DD7A70017587F /* TableKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TableKit.framework; path = Carthage/Build/iOS/TableKit.framework; sourceTree = "<group>"; };
786CBB25220DD7A70017587F /* LeadKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LeadKit.framework; path = Carthage/Build/iOS/LeadKit.framework; sourceTree = "<group>"; };
786CBB26220DD7A70017587F /* SwiftValidator.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftValidator.framework; path = Carthage/Build/iOS/SwiftValidator.framework; sourceTree = "<group>"; };
786CBB27220DD7A70017587F /* UIScrollView_InfiniteScroll.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIScrollView_InfiniteScroll.framework; path = Carthage/Build/iOS/UIScrollView_InfiniteScroll.framework; 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>"; };
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>"; };
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>"; };
ED0C33DA1F2906EC00FAE9FD /* PassCodeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeError.swift; sourceTree = "<group>"; };
ED0C33DB1F2906EC00FAE9FD /* PassCodeHolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolder.swift; sourceTree = "<group>"; };
ED0C33DC1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolderProtocol.swift; sourceTree = "<group>"; };
ED0C33DD1F2906EC00FAE9FD /* PassCodeValidationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeValidationResult.swift; sourceTree = "<group>"; };
ED0C33DF1F2906EC00FAE9FD /* BasePassCodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeViewController.swift; sourceTree = "<group>"; };
ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeViewModel.swift; sourceTree = "<group>"; };
ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Extensions.swift"; sourceTree = "<group>"; };
ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+UserService.swift"; sourceTree = "<group>"; };
ED0C33F11F2906EC00FAE9FD /* BasePassCodeService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeService.swift; sourceTree = "<group>"; };
ED0C33F21F2906EC00FAE9FD /* BaseUserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseUserService.swift; sourceTree = "<group>"; };
ED0C33FC1F2906EC00FAE9FD /* ValidationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationError.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>"; };
EF5A43B0206E7A67003CED07 /* PassCodeDelayedDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassCodeDelayedDescription.swift; 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>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
CAE698DF1E968820000394B0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
786CBB2F220DD7A70017587F /* RxSwift.framework in Frameworks */,
786CBB30220DD7A70017587F /* Alamofire.framework in Frameworks */,
786CBB31220DD7A70017587F /* TableKit.framework in Frameworks */,
786CBB32220DD7A70017587F /* LeadKit.framework in Frameworks */,
786CBB33220DD7A70017587F /* SwiftValidator.framework in Frameworks */,
786CBB34220DD7A70017587F /* UIScrollView_InfiniteScroll.framework in Frameworks */,
786CBB36220DD7A70017587F /* RxAlamofire.framework in Frameworks */,
786CBB37220DD7A70017587F /* SwiftDate.framework in Frameworks */,
786CBB38220DD7A70017587F /* RxCocoa.framework in Frameworks */,
786CBB39220DD7A70017587F /* CryptoSwift.framework in Frameworks */,
786CBB3A220DD7A70017587F /* KeychainAccess.framework in Frameworks */,
14E3AD49502B3D81264560DB /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
67B4E6F0206945D200E233EA /* BaseTextFieldViewModel */ = {
isa = PBXGroup;
children = (
67B4E6F1206945D200E233EA /* BaseTextFieldViewModel.swift */,
);
path = BaseTextFieldViewModel;
sourceTree = "<group>";
};
67B4E6F4206945DC00E233EA /* ValidationService */ = {
isa = PBXGroup;
children = (
67B4E6F5206945DC00E233EA /* OnlineValidationResult.swift */,
);
path = ValidationService;
sourceTree = "<group>";
};
A3117951840B8B7D2E7A8A80 /* Frameworks */ = {
isa = PBXGroup;
children = (
786CBB23220DD7A70017587F /* Alamofire.framework */,
786CBB2C220DD7A70017587F /* CryptoSwift.framework */,
786CBB2D220DD7A70017587F /* KeychainAccess.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;
sourceTree = "<group>";
};
CAE698D91E968820000394B0 = {
isa = PBXGroup;
children = (
A3117951840B8B7D2E7A8A80 /* Frameworks */,
CAE698E41E968820000394B0 /* Products */,
CAE698E51E968820000394B0 /* Sources */,
67528BCE206E3CC6009F2525 /* iOS.playground */,
FBC34E01DAB566B85ED77BAD /* Pods */,
);
sourceTree = "<group>";
};
CAE698E41E968820000394B0 /* Products */ = {
isa = PBXGroup;
children = (
CAE698E31E968820000394B0 /* LeadKitAdditions.framework */,
);
name = Products;
sourceTree = "<group>";
};
CAE698E51E968820000394B0 /* Sources */ = {
isa = PBXGroup;
children = (
ED0C33D21F2906EC00FAE9FD /* Classes */,
ED0C33D61F2906EC00FAE9FD /* Controllers */,
ED0C33E61F2906EC00FAE9FD /* Extensions */,
ED0C33EA1F2906EC00FAE9FD /* Protocols */,
ED0C33F01F2906EC00FAE9FD /* Services */,
EFBD55781EBB893F0062AA63 /* Info-iOS-Extensions.plist */,
EFBD55791EBB893F0062AA63 /* Info-iOS.plist */,
CAE698E61E968820000394B0 /* LeadKitAdditions.h */,
);
path = Sources;
sourceTree = "<group>";
};
ED0C33D21F2906EC00FAE9FD /* Classes */ = {
isa = PBXGroup;
children = (
67B4E6F0206945D200E233EA /* BaseTextFieldViewModel */,
ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.swift */,
);
path = Classes;
sourceTree = "<group>";
};
ED0C33D61F2906EC00FAE9FD /* Controllers */ = {
isa = PBXGroup;
children = (
ED0C33D71F2906EC00FAE9FD /* PassCode */,
);
path = Controllers;
sourceTree = "<group>";
};
ED0C33D71F2906EC00FAE9FD /* PassCode */ = {
isa = PBXGroup;
children = (
ED0C33D81F2906EC00FAE9FD /* Model */,
ED0C33DE1F2906EC00FAE9FD /* View */,
ED0C33E01F2906EC00FAE9FD /* ViewModel */,
);
path = PassCode;
sourceTree = "<group>";
};
ED0C33D81F2906EC00FAE9FD /* Model */ = {
isa = PBXGroup;
children = (
ED0C33D91F2906EC00FAE9FD /* PassCodeConfiguration.swift */,
EF5A43B0206E7A67003CED07 /* PassCodeDelayedDescription.swift */,
ED0C33DA1F2906EC00FAE9FD /* PassCodeError.swift */,
ED0C33DB1F2906EC00FAE9FD /* PassCodeHolder.swift */,
ED0C33DC1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift */,
ED0C33DD1F2906EC00FAE9FD /* PassCodeValidationResult.swift */,
);
path = Model;
sourceTree = "<group>";
};
ED0C33DE1F2906EC00FAE9FD /* View */ = {
isa = PBXGroup;
children = (
ED0C33DF1F2906EC00FAE9FD /* BasePassCodeViewController.swift */,
);
path = View;
sourceTree = "<group>";
};
ED0C33E01F2906EC00FAE9FD /* ViewModel */ = {
isa = PBXGroup;
children = (
ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
};
ED0C33E61F2906EC00FAE9FD /* Extensions */ = {
isa = PBXGroup;
children = (
ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */,
ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */,
A6CFB8D81F5024A500A42CC2 /* Error+NetworkingExtensions.swift */,
67779CBB206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
ED0C33EA1F2906EC00FAE9FD /* Protocols */ = {
isa = PBXGroup;
children = (
67B4E6F4206945DC00E233EA /* ValidationService */,
67D6041721627600002DAF5B /* LegacyConfigurableController.swift */,
);
path = Protocols;
sourceTree = "<group>";
};
ED0C33F01F2906EC00FAE9FD /* Services */ = {
isa = PBXGroup;
children = (
ED0C33FB1F2906EC00FAE9FD /* ValidationService */,
ED0C33F11F2906EC00FAE9FD /* BasePassCodeService.swift */,
ED0C33F21F2906EC00FAE9FD /* BaseUserService.swift */,
678D26A9206935B900B05B93 /* BiometricsService.swift */,
);
path = Services;
sourceTree = "<group>";
};
ED0C33FB1F2906EC00FAE9FD /* ValidationService */ = {
isa = PBXGroup;
children = (
67B4E6F8206945F900E233EA /* OnlineValidationState.swift */,
ED0C33FC1F2906EC00FAE9FD /* ValidationError.swift */,
ED0C33FD1F2906EC00FAE9FD /* ValidationItem.swift */,
ED0C33FE1F2906EC00FAE9FD /* ValidationService.swift */,
);
path = ValidationService;
sourceTree = "<group>";
};
FBC34E01DAB566B85ED77BAD /* Pods */ = {
isa = PBXGroup;
children = (
6FEE03727D0B955F00DE8429 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */,
1AC235099AA10D83D608A253 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
CAE698E01E968820000394B0 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
CAE698E81E968820000394B0 /* LeadKitAdditions.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
CAE698E21E968820000394B0 /* LeadKitAdditions iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = CAE698EB1E968820000394B0 /* Build configuration list for PBXNativeTarget "LeadKitAdditions iOS" */;
buildPhases = (
DA96E76177E2633117FCBE55 /* [CP] Check Pods Manifest.lock */,
CAE698DE1E968820000394B0 /* Sources */,
CAE698DF1E968820000394B0 /* Frameworks */,
CAE698E01E968820000394B0 /* Headers */,
CAE698E11E968820000394B0 /* Resources */,
CAE6990A1E969A7A000394B0 /* Swiftlint */,
);
buildRules = (
);
dependencies = (
);
name = "LeadKitAdditions iOS";
productName = LeadKitAdditions;
productReference = CAE698E31E968820000394B0 /* LeadKitAdditions.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
CAE698DA1E968820000394B0 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = TouchInstinct;
TargetAttributes = {
CAE698E21E968820000394B0 = {
CreatedOnToolsVersion = 8.3;
LastSwiftMigration = 1020;
ProvisioningStyle = Manual;
};
};
};
buildConfigurationList = CAE698DD1E968820000394B0 /* Build configuration list for PBXProject "LeadKitAdditions" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = CAE698D91E968820000394B0;
productRefGroup = CAE698E41E968820000394B0 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
CAE698E21E968820000394B0 /* LeadKitAdditions iOS */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
CAE698E11E968820000394B0 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
CAE6990A1E969A7A000394B0 /* Swiftlint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = Swiftlint;
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = ". run_swiftlint.sh\n";
};
DA96E76177E2633117FCBE55 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-LeadKitAdditions-LeadKitAdditions iOS-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;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
CAE698DE1E968820000394B0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
67D6041821627600002DAF5B /* LegacyConfigurableController.swift in Sources */,
67B4E6FB20694A4200E233EA /* BaseTextFieldViewModel.swift in Sources */,
ED0C340F1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */,
ED0C343F1F2906EC00FAE9FD /* ValidationError.swift in Sources */,
ED0C342F1F2906EC00FAE9FD /* BaseUserService.swift in Sources */,
678D26AA206935B900B05B93 /* BiometricsService.swift in Sources */,
ED0C34411F2906EC00FAE9FD /* ValidationItem.swift in Sources */,
ED0C341F1F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */,
ED0C34091F2906EC00FAE9FD /* PassCodeConfiguration.swift in Sources */,
EF5A43B1206E7A67003CED07 /* PassCodeDelayedDescription.swift in Sources */,
ED0C34131F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */,
ED0C342D1F2906EC00FAE9FD /* BasePassCodeService.swift in Sources */,
67B4E6F9206945F900E233EA /* OnlineValidationState.swift in Sources */,
67779CBC206986390098F024 /* BaseTextFieldViewEvents+Extensions.swift in Sources */,
ED0C340D1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */,
A6CFB8D91F5024A500A42CC2 /* Error+NetworkingExtensions.swift in Sources */,
ED0C34071F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */,
ED0C34211F2906EC00FAE9FD /* UserDefaults+UserService.swift in Sources */,
ED0C34111F2906EC00FAE9FD /* PassCodeValidationResult.swift in Sources */,
ED0C34431F2906EC00FAE9FD /* ValidationService.swift in Sources */,
ED0C340B1F2906EC00FAE9FD /* PassCodeError.swift in Sources */,
67B4E6F6206945DD00E233EA /* OnlineValidationResult.swift in Sources */,
ED0C34151F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
CAE698E91E968820000394B0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
CAE698EA1E968820000394B0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
CAE698EC1E968820000394B0 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6FEE03727D0B955F00DE8429 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = 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.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "touchinstinct.LeadKitAdditions-iOS";
PRODUCT_NAME = LeadKitAdditions;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
CAE698ED1E968820000394B0 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 1AC235099AA10D83D608A253 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = 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.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "touchinstinct.LeadKitAdditions-iOS";
PRODUCT_NAME = LeadKitAdditions;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
CAE698DD1E968820000394B0 /* Build configuration list for PBXProject "LeadKitAdditions" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CAE698E91E968820000394B0 /* Debug */,
CAE698EA1E968820000394B0 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CAE698EB1E968820000394B0 /* Build configuration list for PBXNativeTarget "LeadKitAdditions iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CAE698EC1E968820000394B0 /* Debug */,
CAE698ED1E968820000394B0 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = CAE698DA1E968820000394B0 /* Project object */;
}

View File

@ -0,0 +1,80 @@
<?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>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -1 +0,0 @@
../code-quality/.swiftlint.yml

View File

@ -1,900 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
0A08E37F1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A08E37E1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift */; };
248389A288C0A6D7914F0546 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED4A1B793EAA73C9E95969F /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */; };
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, ); }; };
ED0C34031F2906EC00FAE9FD /* ApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D31F2906EC00FAE9FD /* ApiResponse.swift */; };
ED0C34041F2906EC00FAE9FD /* ApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D31F2906EC00FAE9FD /* ApiResponse.swift */; };
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 */; };
ED0C34081F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.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 */; };
ED0C340C1F2906EC00FAE9FD /* PassCodeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DA1F2906EC00FAE9FD /* PassCodeError.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 */; };
ED0C34101F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DC1F2906EC00FAE9FD /* PassCodeHolderProtocol.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 */; };
ED0C34141F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33DF1F2906EC00FAE9FD /* BasePassCodeViewController.swift */; };
ED0C34151F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */; };
ED0C34161F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */; };
ED0C34171F2906EC00FAE9FD /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E31F2906EC00FAE9FD /* ApiError.swift */; };
ED0C34181F2906EC00FAE9FD /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E31F2906EC00FAE9FD /* ApiError.swift */; };
ED0C34191F2906EC00FAE9FD /* ApiErrorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E41F2906EC00FAE9FD /* ApiErrorProtocol.swift */; };
ED0C341A1F2906EC00FAE9FD /* ApiErrorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E41F2906EC00FAE9FD /* ApiErrorProtocol.swift */; };
ED0C341D1F2906EC00FAE9FD /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E71F2906EC00FAE9FD /* Observable+Extensions.swift */; };
ED0C341E1F2906EC00FAE9FD /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E71F2906EC00FAE9FD /* Observable+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 */; };
ED0C34221F2906EC00FAE9FD /* UserDefaults+UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */; };
ED0C34231F2906EC00FAE9FD /* CellFieldJumpingProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33EB1F2906EC00FAE9FD /* CellFieldJumpingProtocol.swift */; };
ED0C34241F2906EC00FAE9FD /* CellFieldJumpingProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33EB1F2906EC00FAE9FD /* CellFieldJumpingProtocol.swift */; };
ED0C34251F2906EC00FAE9FD /* CellFieldMaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33EC1F2906EC00FAE9FD /* CellFieldMaskProtocol.swift */; };
ED0C34261F2906EC00FAE9FD /* CellFieldMaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33EC1F2906EC00FAE9FD /* CellFieldMaskProtocol.swift */; };
ED0C34271F2906EC00FAE9FD /* CellFieldsToolBarProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33ED1F2906EC00FAE9FD /* CellFieldsToolBarProtocol.swift */; };
ED0C34281F2906EC00FAE9FD /* CellFieldsToolBarProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33ED1F2906EC00FAE9FD /* CellFieldsToolBarProtocol.swift */; };
ED0C34291F2906EC00FAE9FD /* CellFieldValidationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33EE1F2906EC00FAE9FD /* CellFieldValidationProtocol.swift */; };
ED0C342A1F2906EC00FAE9FD /* CellFieldValidationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33EE1F2906EC00FAE9FD /* CellFieldValidationProtocol.swift */; };
ED0C342B1F2906EC00FAE9FD /* FormCellViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33EF1F2906EC00FAE9FD /* FormCellViewModelProtocol.swift */; };
ED0C342C1F2906EC00FAE9FD /* FormCellViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33EF1F2906EC00FAE9FD /* FormCellViewModelProtocol.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 */; };
ED0C34301F2906EC00FAE9FD /* BaseUserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F21F2906EC00FAE9FD /* BaseUserService.swift */; };
ED0C34311F2906EC00FAE9FD /* CellFieldsJumpingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F31F2906EC00FAE9FD /* CellFieldsJumpingService.swift */; };
ED0C34321F2906EC00FAE9FD /* CellFieldsJumpingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F31F2906EC00FAE9FD /* CellFieldsJumpingService.swift */; };
ED0C34331F2906EC00FAE9FD /* MaskFieldTextProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F41F2906EC00FAE9FD /* MaskFieldTextProxy.swift */; };
ED0C34341F2906EC00FAE9FD /* MaskFieldTextProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F41F2906EC00FAE9FD /* MaskFieldTextProxy.swift */; };
ED0C34351F2906EC00FAE9FD /* ApiNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F61F2906EC00FAE9FD /* ApiNetworkService.swift */; };
ED0C34361F2906EC00FAE9FD /* ApiNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F61F2906EC00FAE9FD /* ApiNetworkService.swift */; };
ED0C34381F2906EC00FAE9FD /* DefaultNetworkService+ActivityIndicator+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F71F2906EC00FAE9FD /* DefaultNetworkService+ActivityIndicator+Extension.swift */; };
ED0C34391F2906EC00FAE9FD /* DefaultNetworkService+ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F81F2906EC00FAE9FD /* DefaultNetworkService+ActivityIndicator.swift */; };
ED0C343B1F2906EC00FAE9FD /* DefaultNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F91F2906EC00FAE9FD /* DefaultNetworkService.swift */; };
ED0C343C1F2906EC00FAE9FD /* DefaultNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33F91F2906EC00FAE9FD /* DefaultNetworkService.swift */; };
ED0C343D1F2906EC00FAE9FD /* TouchIDService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FA1F2906EC00FAE9FD /* TouchIDService.swift */; };
ED0C343E1F2906EC00FAE9FD /* TouchIDService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FA1F2906EC00FAE9FD /* TouchIDService.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 */; };
ED0C34421F2906EC00FAE9FD /* ValidationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FD1F2906EC00FAE9FD /* ValidationItem.swift */; };
ED0C34431F2906EC00FAE9FD /* ValidationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FE1F2906EC00FAE9FD /* ValidationService.swift */; };
ED0C34441F2906EC00FAE9FD /* ValidationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33FE1F2906EC00FAE9FD /* ValidationService.swift */; };
ED0C34451F2906EC00FAE9FD /* CellTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C34011F2906EC00FAE9FD /* CellTextField.swift */; };
ED0C34461F2906EC00FAE9FD /* CellTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C34011F2906EC00FAE9FD /* CellTextField.swift */; };
ED0C34471F2906EC00FAE9FD /* CellTextFieldViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C34021F2906EC00FAE9FD /* CellTextFieldViewModel.swift */; };
ED0C34481F2906EC00FAE9FD /* CellTextFieldViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C34021F2906EC00FAE9FD /* CellTextFieldViewModel.swift */; };
ED69E7E81F2AC7CC00C74895 /* CellTextFieldToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A08E37E1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift */; };
EFBD55921EBB9A980062AA63 /* LeadKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = CAE698E61E968820000394B0 /* LeadKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
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>"; };
0A08E37E1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellTextFieldToolBar.swift; sourceTree = "<group>"; };
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>"; };
7B7F57C5E5275C4D8DC71992 /* Pods_LeadKitAdditions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKitAdditions.framework; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
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; };
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>"; };
ED0C33D31F2906EC00FAE9FD /* ApiResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiResponse.swift; 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>"; };
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>"; };
ED0C33DB1F2906EC00FAE9FD /* PassCodeHolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolder.swift; sourceTree = "<group>"; };
ED0C33DC1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolderProtocol.swift; sourceTree = "<group>"; };
ED0C33DD1F2906EC00FAE9FD /* PassCodeValidationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeValidationResult.swift; sourceTree = "<group>"; };
ED0C33DF1F2906EC00FAE9FD /* BasePassCodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeViewController.swift; sourceTree = "<group>"; };
ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeViewModel.swift; sourceTree = "<group>"; };
ED0C33E31F2906EC00FAE9FD /* ApiError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiError.swift; sourceTree = "<group>"; };
ED0C33E41F2906EC00FAE9FD /* ApiErrorProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiErrorProtocol.swift; sourceTree = "<group>"; };
ED0C33E71F2906EC00FAE9FD /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Extensions.swift"; sourceTree = "<group>"; };
ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Extensions.swift"; sourceTree = "<group>"; };
ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+UserService.swift"; sourceTree = "<group>"; };
ED0C33EB1F2906EC00FAE9FD /* CellFieldJumpingProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellFieldJumpingProtocol.swift; sourceTree = "<group>"; };
ED0C33EC1F2906EC00FAE9FD /* CellFieldMaskProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellFieldMaskProtocol.swift; sourceTree = "<group>"; };
ED0C33ED1F2906EC00FAE9FD /* CellFieldsToolBarProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellFieldsToolBarProtocol.swift; sourceTree = "<group>"; };
ED0C33EE1F2906EC00FAE9FD /* CellFieldValidationProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellFieldValidationProtocol.swift; sourceTree = "<group>"; };
ED0C33EF1F2906EC00FAE9FD /* FormCellViewModelProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormCellViewModelProtocol.swift; sourceTree = "<group>"; };
ED0C33F11F2906EC00FAE9FD /* BasePassCodeService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeService.swift; sourceTree = "<group>"; };
ED0C33F21F2906EC00FAE9FD /* BaseUserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseUserService.swift; sourceTree = "<group>"; };
ED0C33F31F2906EC00FAE9FD /* CellFieldsJumpingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellFieldsJumpingService.swift; sourceTree = "<group>"; };
ED0C33F41F2906EC00FAE9FD /* MaskFieldTextProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MaskFieldTextProxy.swift; sourceTree = "<group>"; };
ED0C33F61F2906EC00FAE9FD /* ApiNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiNetworkService.swift; sourceTree = "<group>"; };
ED0C33F71F2906EC00FAE9FD /* DefaultNetworkService+ActivityIndicator+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DefaultNetworkService+ActivityIndicator+Extension.swift"; sourceTree = "<group>"; };
ED0C33F81F2906EC00FAE9FD /* DefaultNetworkService+ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DefaultNetworkService+ActivityIndicator.swift"; sourceTree = "<group>"; };
ED0C33F91F2906EC00FAE9FD /* DefaultNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkService.swift; sourceTree = "<group>"; };
ED0C33FA1F2906EC00FAE9FD /* TouchIDService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchIDService.swift; sourceTree = "<group>"; };
ED0C33FC1F2906EC00FAE9FD /* ValidationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationError.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>"; };
ED0C34011F2906EC00FAE9FD /* CellTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellTextField.swift; sourceTree = "<group>"; };
ED0C34021F2906EC00FAE9FD /* CellTextFieldViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellTextFieldViewModel.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>"; };
EFBD55791EBB893F0062AA63 /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
CAE698DF1E968820000394B0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
248389A288C0A6D7914F0546 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
EFBD556C1EBB87100062AA63 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B326804BA6CC8B8BB136A46A /* Pods_LeadKitAdditions_LeadKitAdditions_iOS_Extensions.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
A3117951840B8B7D2E7A8A80 /* Frameworks */ = {
isa = PBXGroup;
children = (
7B7F57C5E5275C4D8DC71992 /* Pods_LeadKitAdditions.framework */,
0ED4A1B793EAA73C9E95969F /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */,
CFD5627139CAB27705F75C07 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS_Extensions.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
CAE698D91E968820000394B0 = {
isa = PBXGroup;
children = (
A3117951840B8B7D2E7A8A80 /* Frameworks */,
F8A65FEC7C0EB4B93746E50F /* Pods */,
CAE698E41E968820000394B0 /* Products */,
CAE698E51E968820000394B0 /* Sources */,
);
sourceTree = "<group>";
};
CAE698E41E968820000394B0 /* Products */ = {
isa = PBXGroup;
children = (
CAE698E31E968820000394B0 /* LeadKitAdditions.framework */,
EFBD55701EBB87100062AA63 /* LeadKitAdditions.framework */,
);
name = Products;
sourceTree = "<group>";
};
CAE698E51E968820000394B0 /* Sources */ = {
isa = PBXGroup;
children = (
ED0C33D21F2906EC00FAE9FD /* Classes */,
ED0C33D61F2906EC00FAE9FD /* Controllers */,
ED0C33E21F2906EC00FAE9FD /* Enums */,
ED0C33E61F2906EC00FAE9FD /* Extensions */,
ED0C33EA1F2906EC00FAE9FD /* Protocols */,
ED0C33F01F2906EC00FAE9FD /* Services */,
ED0C33FF1F2906EC00FAE9FD /* Views */,
EFBD55781EBB893F0062AA63 /* Info-iOS-Extensions.plist */,
EFBD55791EBB893F0062AA63 /* Info-iOS.plist */,
CAE698E61E968820000394B0 /* LeadKitAdditions.h */,
);
path = Sources;
sourceTree = "<group>";
};
ED0C33D21F2906EC00FAE9FD /* Classes */ = {
isa = PBXGroup;
children = (
ED0C33D31F2906EC00FAE9FD /* ApiResponse.swift */,
ED0C33D41F2906EC00FAE9FD /* BaseDateFormatter.swift */,
ED0C33D51F2906EC00FAE9FD /* LoadingBarButton.swift */,
);
path = Classes;
sourceTree = "<group>";
};
ED0C33D61F2906EC00FAE9FD /* Controllers */ = {
isa = PBXGroup;
children = (
ED0C33D71F2906EC00FAE9FD /* PassCode */,
);
path = Controllers;
sourceTree = "<group>";
};
ED0C33D71F2906EC00FAE9FD /* PassCode */ = {
isa = PBXGroup;
children = (
ED0C33D81F2906EC00FAE9FD /* Model */,
ED0C33DE1F2906EC00FAE9FD /* View */,
ED0C33E01F2906EC00FAE9FD /* ViewModel */,
);
path = PassCode;
sourceTree = "<group>";
};
ED0C33D81F2906EC00FAE9FD /* Model */ = {
isa = PBXGroup;
children = (
ED0C33D91F2906EC00FAE9FD /* PassCodeConfiguration.swift */,
ED0C33DA1F2906EC00FAE9FD /* PassCodeError.swift */,
ED0C33DB1F2906EC00FAE9FD /* PassCodeHolder.swift */,
ED0C33DC1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift */,
ED0C33DD1F2906EC00FAE9FD /* PassCodeValidationResult.swift */,
);
path = Model;
sourceTree = "<group>";
};
ED0C33DE1F2906EC00FAE9FD /* View */ = {
isa = PBXGroup;
children = (
ED0C33DF1F2906EC00FAE9FD /* BasePassCodeViewController.swift */,
);
path = View;
sourceTree = "<group>";
};
ED0C33E01F2906EC00FAE9FD /* ViewModel */ = {
isa = PBXGroup;
children = (
ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
};
ED0C33E21F2906EC00FAE9FD /* Enums */ = {
isa = PBXGroup;
children = (
ED0C33E31F2906EC00FAE9FD /* ApiError.swift */,
ED0C33E41F2906EC00FAE9FD /* ApiErrorProtocol.swift */,
);
path = Enums;
sourceTree = "<group>";
};
ED0C33E61F2906EC00FAE9FD /* Extensions */ = {
isa = PBXGroup;
children = (
ED0C33E71F2906EC00FAE9FD /* Observable+Extensions.swift */,
ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */,
ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */,
A6CFB8D81F5024A500A42CC2 /* Error+NetworkingExtensions.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
ED0C33EA1F2906EC00FAE9FD /* Protocols */ = {
isa = PBXGroup;
children = (
ED0C33EB1F2906EC00FAE9FD /* CellFieldJumpingProtocol.swift */,
ED0C33EC1F2906EC00FAE9FD /* CellFieldMaskProtocol.swift */,
ED0C33EE1F2906EC00FAE9FD /* CellFieldValidationProtocol.swift */,
ED0C33ED1F2906EC00FAE9FD /* CellFieldsToolBarProtocol.swift */,
ED0C33EF1F2906EC00FAE9FD /* FormCellViewModelProtocol.swift */,
);
path = Protocols;
sourceTree = "<group>";
};
ED0C33F01F2906EC00FAE9FD /* Services */ = {
isa = PBXGroup;
children = (
ED0C33F51F2906EC00FAE9FD /* Network */,
ED0C33FB1F2906EC00FAE9FD /* ValidationService */,
ED0C33F11F2906EC00FAE9FD /* BasePassCodeService.swift */,
ED0C33F21F2906EC00FAE9FD /* BaseUserService.swift */,
ED0C33F31F2906EC00FAE9FD /* CellFieldsJumpingService.swift */,
ED0C33F41F2906EC00FAE9FD /* MaskFieldTextProxy.swift */,
ED0C33FA1F2906EC00FAE9FD /* TouchIDService.swift */,
);
path = Services;
sourceTree = "<group>";
};
ED0C33F51F2906EC00FAE9FD /* Network */ = {
isa = PBXGroup;
children = (
ED0C33F61F2906EC00FAE9FD /* ApiNetworkService.swift */,
ED0C33F71F2906EC00FAE9FD /* DefaultNetworkService+ActivityIndicator+Extension.swift */,
ED0C33F81F2906EC00FAE9FD /* DefaultNetworkService+ActivityIndicator.swift */,
ED0C33F91F2906EC00FAE9FD /* DefaultNetworkService.swift */,
);
path = Network;
sourceTree = "<group>";
};
ED0C33FB1F2906EC00FAE9FD /* ValidationService */ = {
isa = PBXGroup;
children = (
ED0C33FC1F2906EC00FAE9FD /* ValidationError.swift */,
ED0C33FD1F2906EC00FAE9FD /* ValidationItem.swift */,
ED0C33FE1F2906EC00FAE9FD /* ValidationService.swift */,
);
path = ValidationService;
sourceTree = "<group>";
};
ED0C33FF1F2906EC00FAE9FD /* Views */ = {
isa = PBXGroup;
children = (
0A08E37E1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift */,
ED0C34001F2906EC00FAE9FD /* CellTextField */,
);
path = Views;
sourceTree = "<group>";
};
ED0C34001F2906EC00FAE9FD /* CellTextField */ = {
isa = PBXGroup;
children = (
ED0C34011F2906EC00FAE9FD /* CellTextField.swift */,
ED0C34021F2906EC00FAE9FD /* CellTextFieldViewModel.swift */,
);
path = CellTextField;
sourceTree = "<group>";
};
F8A65FEC7C0EB4B93746E50F /* Pods */ = {
isa = PBXGroup;
children = (
49738551AC648B0AFA74E57F /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */,
D0AB283D10B2175EFDBF7924 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig */,
9D549FA5A7579702358E07DF /* Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.debug.xcconfig */,
01605ECA03749D49C27FA3DD /* Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
CAE698E01E968820000394B0 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
CAE698E81E968820000394B0 /* LeadKitAdditions.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
EFBD556D1EBB87100062AA63 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
EFBD55921EBB9A980062AA63 /* LeadKitAdditions.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
CAE698E21E968820000394B0 /* LeadKitAdditions iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = CAE698EB1E968820000394B0 /* Build configuration list for PBXNativeTarget "LeadKitAdditions iOS" */;
buildPhases = (
E8E82E34792B38EF225575D7 /* [CP] Check Pods Manifest.lock */,
CAE698DE1E968820000394B0 /* Sources */,
CAE698DF1E968820000394B0 /* Frameworks */,
CAE698E01E968820000394B0 /* Headers */,
CAE698E11E968820000394B0 /* Resources */,
94F6E1BA5AD68C6E2F10062B /* [CP] Copy Pods Resources */,
CAE6990A1E969A7A000394B0 /* Swiftlint */,
);
buildRules = (
);
dependencies = (
);
name = "LeadKitAdditions iOS";
productName = LeadKitAdditions;
productReference = CAE698E31E968820000394B0 /* LeadKitAdditions.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 */
/* Begin PBXProject section */
CAE698DA1E968820000394B0 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = TouchInstinct;
TargetAttributes = {
CAE698E21E968820000394B0 = {
CreatedOnToolsVersion = 8.3;
LastSwiftMigration = 0900;
ProvisioningStyle = Manual;
};
EFBD556F1EBB87100062AA63 = {
CreatedOnToolsVersion = 8.3.1;
ProvisioningStyle = Manual;
};
};
};
buildConfigurationList = CAE698DD1E968820000394B0 /* Build configuration list for PBXProject "LeadKitAdditions" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = CAE698D91E968820000394B0;
productRefGroup = CAE698E41E968820000394B0 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
CAE698E21E968820000394B0 /* LeadKitAdditions iOS */,
EFBD556F1EBB87100062AA63 /* LeadKitAdditions iOS Extensions */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
CAE698E11E968820000394B0 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
EFBD556E1EBB87100062AA63 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase 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 */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = Swiftlint;
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
};
E8E82E34792B38EF225575D7 /* [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-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;
};
ED69E7E91F2AD0E000C74895 /* Swiftlint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = Swiftlint;
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
CAE698DE1E968820000394B0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
ED0C34171F2906EC00FAE9FD /* ApiError.swift in Sources */,
ED0C343B1F2906EC00FAE9FD /* DefaultNetworkService.swift in Sources */,
ED0C34351F2906EC00FAE9FD /* ApiNetworkService.swift in Sources */,
ED0C340F1F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */,
ED0C343D1F2906EC00FAE9FD /* TouchIDService.swift in Sources */,
ED0C343F1F2906EC00FAE9FD /* ValidationError.swift in Sources */,
ED0C342F1F2906EC00FAE9FD /* BaseUserService.swift in Sources */,
ED0C34231F2906EC00FAE9FD /* CellFieldJumpingProtocol.swift in Sources */,
ED0C34411F2906EC00FAE9FD /* ValidationItem.swift in Sources */,
ED0C341F1F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */,
ED0C34091F2906EC00FAE9FD /* PassCodeConfiguration.swift in Sources */,
ED0C342B1F2906EC00FAE9FD /* FormCellViewModelProtocol.swift in Sources */,
ED0C341D1F2906EC00FAE9FD /* Observable+Extensions.swift in Sources */,
ED0C34471F2906EC00FAE9FD /* CellTextFieldViewModel.swift in Sources */,
ED0C34191F2906EC00FAE9FD /* ApiErrorProtocol.swift in Sources */,
ED0C34131F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */,
ED0C342D1F2906EC00FAE9FD /* BasePassCodeService.swift in Sources */,
ED0C340D1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */,
ED0C34451F2906EC00FAE9FD /* CellTextField.swift in Sources */,
ED0C34271F2906EC00FAE9FD /* CellFieldsToolBarProtocol.swift in Sources */,
ED0C34291F2906EC00FAE9FD /* CellFieldValidationProtocol.swift in Sources */,
0A08E37F1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift in Sources */,
ED0C34031F2906EC00FAE9FD /* ApiResponse.swift in Sources */,
A6CFB8D91F5024A500A42CC2 /* Error+NetworkingExtensions.swift in Sources */,
ED0C34071F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */,
ED0C34251F2906EC00FAE9FD /* CellFieldMaskProtocol.swift in Sources */,
ED0C34211F2906EC00FAE9FD /* UserDefaults+UserService.swift in Sources */,
ED0C34391F2906EC00FAE9FD /* DefaultNetworkService+ActivityIndicator.swift in Sources */,
ED0C34311F2906EC00FAE9FD /* CellFieldsJumpingService.swift in Sources */,
ED0C34111F2906EC00FAE9FD /* PassCodeValidationResult.swift in Sources */,
ED0C34051F2906EC00FAE9FD /* BaseDateFormatter.swift in Sources */,
ED0C34431F2906EC00FAE9FD /* ValidationService.swift in Sources */,
ED0C34331F2906EC00FAE9FD /* MaskFieldTextProxy.swift in Sources */,
ED0C340B1F2906EC00FAE9FD /* PassCodeError.swift in Sources */,
ED0C34151F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
EFBD556B1EBB87100062AA63 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
ED0C34181F2906EC00FAE9FD /* ApiError.swift in Sources */,
ED0C343C1F2906EC00FAE9FD /* DefaultNetworkService.swift in Sources */,
ED0C34361F2906EC00FAE9FD /* ApiNetworkService.swift in Sources */,
ED0C34101F2906EC00FAE9FD /* PassCodeHolderProtocol.swift in Sources */,
ED0C343E1F2906EC00FAE9FD /* TouchIDService.swift in Sources */,
ED0C34401F2906EC00FAE9FD /* ValidationError.swift in Sources */,
ED0C34301F2906EC00FAE9FD /* BaseUserService.swift in Sources */,
ED0C34241F2906EC00FAE9FD /* CellFieldJumpingProtocol.swift in Sources */,
ED0C34421F2906EC00FAE9FD /* ValidationItem.swift in Sources */,
ED0C34201F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */,
ED0C340A1F2906EC00FAE9FD /* PassCodeConfiguration.swift in Sources */,
ED0C342C1F2906EC00FAE9FD /* FormCellViewModelProtocol.swift in Sources */,
ED0C34381F2906EC00FAE9FD /* DefaultNetworkService+ActivityIndicator+Extension.swift in Sources */,
ED0C341E1F2906EC00FAE9FD /* Observable+Extensions.swift in Sources */,
ED0C34481F2906EC00FAE9FD /* CellTextFieldViewModel.swift in Sources */,
ED0C341A1F2906EC00FAE9FD /* ApiErrorProtocol.swift in Sources */,
ED0C34141F2906EC00FAE9FD /* BasePassCodeViewController.swift in Sources */,
ED0C342E1F2906EC00FAE9FD /* BasePassCodeService.swift in Sources */,
ED0C340E1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */,
ED0C34461F2906EC00FAE9FD /* CellTextField.swift in Sources */,
ED0C34281F2906EC00FAE9FD /* CellFieldsToolBarProtocol.swift in Sources */,
ED69E7E81F2AC7CC00C74895 /* CellTextFieldToolBar.swift in Sources */,
ED0C342A1F2906EC00FAE9FD /* CellFieldValidationProtocol.swift in Sources */,
ED0C34041F2906EC00FAE9FD /* ApiResponse.swift in Sources */,
ED0C34081F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */,
ED0C34261F2906EC00FAE9FD /* CellFieldMaskProtocol.swift in Sources */,
ED0C34221F2906EC00FAE9FD /* UserDefaults+UserService.swift in Sources */,
ED0C34321F2906EC00FAE9FD /* CellFieldsJumpingService.swift in Sources */,
ED0C34121F2906EC00FAE9FD /* PassCodeValidationResult.swift in Sources */,
ED0C34061F2906EC00FAE9FD /* BaseDateFormatter.swift in Sources */,
ED0C34441F2906EC00FAE9FD /* ValidationService.swift in Sources */,
ED0C34341F2906EC00FAE9FD /* MaskFieldTextProxy.swift in Sources */,
ED0C340C1F2906EC00FAE9FD /* PassCodeError.swift in Sources */,
ED0C34161F2906EC00FAE9FD /* BasePassCodeViewModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
CAE698E91E968820000394B0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
CAE698EA1E968820000394B0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
CAE698EC1E968820000394B0 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 49738551AC648B0AFA74E57F /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
CLANG_ENABLE_MODULES = 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.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";
PRODUCT_NAME = LeadKitAdditions;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
};
name = Debug;
};
CAE698ED1E968820000394B0 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D0AB283D10B2175EFDBF7924 /* Pods-LeadKitAdditions-LeadKitAdditions iOS.release.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
CLANG_ENABLE_MODULES = 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.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";
PRODUCT_NAME = LeadKitAdditions;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
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;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
CAE698DD1E968820000394B0 /* Build configuration list for PBXProject "LeadKitAdditions" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CAE698E91E968820000394B0 /* Debug */,
CAE698EA1E968820000394B0 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CAE698EB1E968820000394B0 /* Build configuration list for PBXNativeTarget "LeadKitAdditions iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CAE698EC1E968820000394B0 /* Debug */,
CAE698ED1E968820000394B0 /* Release */,
);
defaultConfigurationIsVisible = 0;
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 */
};
rootObject = CAE698DA1E968820000394B0 /* Project object */;
}

View File

@ -1,30 +0,0 @@
source "https://github.com/CocoaPods/Specs.git"
source "https://github.com/TouchInstinct/Podspecs.git"
abstract_target 'LeadKitAdditions' do
pod 'KeychainAccess', '3.1.0'
pod 'IDZSwiftCommonCrypto'
pod "InputMask", '3.0.0'
pod "SwiftValidator", '5.0.0'
inhibit_all_warnings!
target 'LeadKitAdditions iOS' do
platform :ios, '9.0'
use_frameworks!
pod "LeadKit", '~> 0.6.0'
end
target 'LeadKitAdditions iOS Extensions' do
platform :ios, '9.0'
use_frameworks!
pod "LeadKit/Core-iOS-Extension", '~> 0.6.0'
end
end
# If you have slow HDD
ENV['COCOAPODS_DISABLE_STATS'] = "true"

View File

@ -1,63 +0,0 @@
PODS:
- Alamofire (4.5.1)
- CocoaLumberjack/Default (3.3.0)
- CocoaLumberjack/Swift (3.3.0):
- CocoaLumberjack/Default
- IDZSwiftCommonCrypto (0.10.0)
- InputMask (3.0.0)
- KeychainAccess (3.1.0)
- LeadKit (0.6.5):
- LeadKit/Core (= 0.6.5)
- LeadKit/Core (0.6.5):
- CocoaLumberjack/Swift (~> 3.3.0)
- ObjectMapper (~> 3.0.0)
- RxAlamofire (= 4.0.0)
- RxCocoa (= 4.0.0)
- RxSwift (= 4.0.0)
- TableKit (~> 2.5.0)
- UIScrollView-InfiniteScroll (~> 1.0.0)
- LeadKit/Core-iOS-Extension (0.6.5):
- CocoaLumberjack/Swift (~> 3.3.0)
- ObjectMapper (~> 3.0.0)
- RxAlamofire (= 4.0.0)
- RxCocoa (= 4.0.0)
- RxSwift (= 4.0.0)
- ObjectMapper (3.0.0)
- RxAlamofire (4.0.0):
- RxAlamofire/Core (= 4.0.0)
- RxAlamofire/Core (4.0.0):
- Alamofire (~> 4.5)
- RxSwift (~> 4.0)
- RxCocoa (4.0.0):
- RxSwift (~> 4.0)
- RxSwift (4.0.0)
- SwiftValidator (5.0.0)
- TableKit (2.5.0)
- UIScrollView-InfiniteScroll (1.0.2)
DEPENDENCIES:
- IDZSwiftCommonCrypto
- InputMask (= 3.0.0)
- KeychainAccess (= 3.1.0)
- LeadKit (~> 0.6.0)
- LeadKit/Core-iOS-Extension (~> 0.6.0)
- SwiftValidator (= 5.0.0)
SPEC CHECKSUMS:
Alamofire: 2d95912bf4c34f164fdfc335872e8c312acaea4a
CocoaLumberjack: 3c8c74683302f9012bb168e1c4b7ae3c0b558431
IDZSwiftCommonCrypto: 4eef2c46e262dfbcbc1fd76365e066336680ad7d
InputMask: 37c273bde6705187d80cf0b4240cb42ea92096c3
KeychainAccess: 94c5540b32eabf7bc32bfb976a268e8ea05fd6da
LeadKit: 583c724f25852e40eebad8af5a945e101c282fde
ObjectMapper: 92230db59bf8f341a5c3a3cf0b9fbdde3cf0d87f
RxAlamofire: 6ea579ac53bf14cb4bc7049a3866e5a769989b1d
RxCocoa: d62846ca96495d862fa4c59ea7d87e5031d7340e
RxSwift: fd680d75283beb5e2559486f3c0ff852f0d35334
SwiftValidator: 46cdd2061962df3ee8bab3c536dea9b34191d459
TableKit: 42d4dff2944f273cdeec2ef6352064eb6a9a355b
UIScrollView-InfiniteScroll: c132d6d5851daff229ab4a1060ccf70a05a051c9
PODFILE CHECKSUM: 9ba64f509b50aaf860b0df0c12cca7969aa50842
COCOAPODS: 1.3.1

View File

@ -1,51 +0,0 @@
//
// Copyright (c) 2017 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 ObjectMapper
/// Class describes typical response from server, which designed by TouchInstinct
public class ApiResponse: ApiResponseProtocol, ImmutableMappable {
/// nil in case of error, result of request otherwise
public let result: Any?
/// In case of error contains error code, 0 (zero) otherwise
public let errorCode: Int
/// nil in case of success, error description otherwise
public let errorMessage: String?
public required init(map: Map) throws {
result = try? map.value("result")
errorCode = try map.value("error_code")
errorMessage = try? map.value("error_message")
}
}
/// Describes error, which received from server designed by TouchInstinct
public protocol ApiResponseProtocol: ImmutableMappable {
/// Error code
var errorCode: Int { get }
/// Error description
var errorMessage: String? { get }
}

View File

@ -1,127 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import ObjectMapper
/// 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)
}
// MARK: - Transformers
/// Transformer to workaround with dates in Mappable (ObjectMapper) objects
public static var transformFromStringToDate: TransformOf<Date, String> {
return TransformOf<Date, String>(fromJSON: { (stringValue) -> Date? in
if let stringValue = stringValue {
return backendDate(fromStrDate: stringValue)
} else {
return nil
}
}, toJSON: { (dateValue) -> String? in
if let dateValue = dateValue {
return backendStrDate(withDate: dateValue)
} else {
return nil
}
})
}
}

View File

@ -1,200 +0,0 @@
//
// Copyright (c) 2017 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 LeadKit
import RxSwift
import RxCocoa
/// Describes types of authentication
public enum PassCodeAuthType {
case passCode(String)
case touchId
}
/// Base view model for passCodeViewController
open class BasePassCodeViewModel: BaseViewModel {
public let controllerType: PassCodeControllerType
public let disposeBag = DisposeBag()
/// TouchId service, which can answer if user is authorized by finger
public let touchIdService: TouchIDService?
/// Contains configuration for pass code operations
public let passCodeConfiguration: PassCodeConfiguration
fileprivate let validationResultHolder = Variable<PassCodeValidationResult?>(nil)
var validationResult: Driver<PassCodeValidationResult?> {
return validationResultHolder.asDriver()
}
fileprivate let passCodeControllerStateHolder = Variable<PassCodeControllerState>(.enter)
public var passCodeControllerState: Driver<PassCodeControllerState> {
return passCodeControllerStateHolder.asDriver()
}
private let passCodeText = Variable<String?>(nil)
fileprivate var attemptsNumber = 0
fileprivate lazy var passCodeHolder: PassCodeHolderProtocol = {
return PassCodeHolderBuilder.build(with: self.controllerType)
}()
public init(controllerType: PassCodeControllerType,
passCodeConfiguration: PassCodeConfiguration,
touchIdService: TouchIDService? = nil) {
self.controllerType = controllerType
self.passCodeConfiguration = passCodeConfiguration
self.touchIdService = touchIdService
bindViewModel()
}
private func bindViewModel() {
passCodeText.asDriver()
.distinctUntilChanged { $0 == $1 }
.drive(onNext: { [weak self] passCode in
if let passCode = passCode,
passCode.characters.count == Int(self?.passCodeConfiguration.passCodeCharactersNumber ?? 0) {
self?.set(passCode: passCode)
}
})
.disposed(by: disposeBag)
validationResultHolder.asDriver()
.drive(onNext: { [weak self] validationResult in
guard let sSelf = self else {
return
}
if sSelf.passCodeHolder.type == .change {
if validationResult?.isValid ?? false,
sSelf.passCodeHolder.enterStep == .repeatEnter,
let passCode = validationResult?.passCode {
sSelf.authSucceed(.passCode(passCode))
} else {
sSelf.passCodeControllerStateHolder.value = sSelf.passCodeHolder.enterStep
}
} else {
if validationResult?.isValid ?? false, let passCode = validationResult?.passCode {
sSelf.authSucceed(.passCode(passCode))
} else {
sSelf.passCodeControllerStateHolder.value = sSelf.passCodeHolder.enterStep
}
}
})
.disposed(by: disposeBag)
}
// MARK: - Public
public var passCodeTextValue: String? {
return passCodeText.value
}
public func setPassCodeText(_ value: String?) {
passCodeText.value = value
}
public func reset() {
passCodeText.value = nil
validationResultHolder.value = nil
passCodeControllerStateHolder.value = controllerType == .change ? .oldEnter : .enter
attemptsNumber = 0
passCodeHolder.reset()
}
// MARK: - HAVE TO OVERRIDE
/// Override to check if entered pass code is equal to stored
open func isEnteredPassCodeValid(_ passCode: String) -> Bool {
assertionFailure("You should override this method: isEnteredPassCodeValid(_ passCode: String)")
return false
}
/// Handler called after successful authentication
open func authSucceed(_ type: PassCodeAuthType) {
assertionFailure("You should override this method: authSucceed(_ type: PassCodeAuthType)")
}
// MARK: - Functions that can you can override to use TouchId
/// Override to be able use touchId during authentication
open var isTouchIdEnabled: Bool {
return false
}
/// You should save user choice about authenticate by touchId
open func activateTouchIdForUser() {
assertionFailure("You should override this method: activateTouchIdForUser()")
}
}
extension BasePassCodeViewModel {
fileprivate func set(passCode: String) {
passCodeHolder.add(passCode: passCode)
validateIfNeeded()
if shouldUpdateControllerState {
passCodeControllerStateHolder.value = passCodeHolder.enterStep
}
}
private var shouldUpdateControllerState: Bool {
return !passCodeHolder.shouldValidate ||
!(validationResultHolder.value?.isValid ?? true) ||
validationResultHolder.value?.error == .tooManyAttempts
}
private func validateIfNeeded() {
guard passCodeHolder.shouldValidate else {
return
}
var validationResult = passCodeHolder.validate()
if passCodeHolder.type == .enter || (passCodeHolder.type == .change && passCodeHolder.enterStep == .newEnter) {
attemptsNumber += 1
if let passCode = validationResult.passCode, !isEnteredPassCodeValid(passCode) {
validationResult = .inValid(.wrongCode)
}
if (!validationResult.isValid && attemptsNumber == Int(passCodeConfiguration.maxAttemptsLoginNumber)) ||
attemptsNumber > Int(passCodeConfiguration.maxAttemptsLoginNumber) {
validationResult = .inValid(.tooManyAttempts)
}
}
if !validationResult.isValid {
passCodeHolder.reset()
}
validationResultHolder.value = validationResult
}
}

View File

@ -1,88 +0,0 @@
//
// Copyright (c) 2017 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 RxSwift
import Alamofire
import CocoaLumberjack
import LeadKit
public typealias VoidBlock = () -> Void
public extension Observable {
/// A closure that checks for "retryable" error
typealias CanRetryClosure = (Error) -> Bool
/// Allow to configure request to restart if error occured
///
/// - Parameters:
/// - retryLimit: how many times request can restarts
/// - canRetryClosure: a closure that checks for "retryable" error
/// - Returns: An observable sequence producing the elements of the given sequence repeatedly
/// until it terminates successfully or is notified to error or complete.
func retry(retryLimit: UInt = DefaultNetworkService.retryLimit,
canRetryClosure: @escaping CanRetryClosure) -> Observable<Observable.E> {
return observeOn(CurrentThreadScheduler.instance)
.retryWhen { errorsObservable -> Observable<Observable.E> in
return errorsObservable.enumerated().flatMap {
(canRetryClosure($1) && $0 < retryLimit - 1) ? self : .error($1)
}
}
}
/**
Add block that executes, when error, described by ApiErrorProtocol, occured during request
- parameters:
- apiErrorType: type of errors, received frim server
- handler: block, that executes, when error occured
*/
func handleApiError<T: ApiErrorProtocol>(_ apiErrorType: T,
handler: @escaping () -> Void) -> Observable<Observable.E>
where T.RawValue == Int {
return observeOn(CurrentThreadScheduler.instance)
.do(onError: { error in
if error.isApiError(apiErrorType) {
handler()
}
})
}
/**
Add ability to monitor request status
- parameter isLoading: subject, request state bind to
*/
func changeLoadingBehaviour(isLoading: PublishSubject<Bool>) -> Observable<Observable.E> {
return observeOn(CurrentThreadScheduler.instance)
.do(onNext: { _ in
isLoading.onNext(false)
}, onError: { _ in
isLoading.onNext(false)
}, onSubscribe: {
isLoading.onNext(true)
})
}
}

View File

@ -1,38 +0,0 @@
import RxSwift
import RxCocoa
import UIKit
typealias ItemSettingsBlock<UIItem> = (UIItem) -> Void where UIItem: UIView
protocol CellFieldJumpingProtocol: FormCellViewModelProtocol {
var toolBar: UIToolbar? { get set }
var shouldGoForward: PublishSubject<Void> { get }
var shouldBecomeFirstResponder: PublishSubject<Void> { get }
var shouldResignFirstResponder: PublishSubject<Void> { get }
var returnButtonType: UIReturnKeyType { get set }
}
extension CellFieldJumpingProtocol {
func bind(for textField: UITextField, to disposeBag: DisposeBag) {
shouldResignFirstResponder.asObservable()
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak textField] _ in
textField?.resignFirstResponder()
})
.disposed(by: disposeBag)
shouldBecomeFirstResponder.asObservable()
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak textField] _ in
textField?.becomeFirstResponder()
})
.disposed(by: disposeBag)
}
}

View File

@ -1,14 +0,0 @@
protocol CellFieldMaskProtocol {
var haveMask: Bool { get }
var maskFieldTextProxy: MaskFieldTextProxy? { get set }
}
extension CellFieldMaskProtocol {
var haveMask: Bool {
return maskFieldTextProxy != nil
}
}

View File

@ -1,5 +0,0 @@
protocol CellFieldValidationProtocol {
var validationItem: ValidationItem? { get set }
}

View File

@ -1,16 +0,0 @@
import UIKit
import RxSwift
protocol CellFieldsToolBarProtocol: class {
var needArrows: Bool { get set }
var canGoForward: Bool { get set }
var canGoBackward: Bool { get set }
var shouldGoForward: PublishSubject<Void> { get }
var shouldGoBackward: PublishSubject<Void> { get }
var shouldEndEditing: PublishSubject<Void> { get }
}

View File

@ -1,15 +0,0 @@
import RxCocoa
import RxSwift
protocol FormCellViewModelProtocol: class {
var isActive: Bool { get set }
}
extension FormCellViewModelProtocol {
func activate(_ isActive: Bool) -> Self {
self.isActive = isActive
return self
}
}

View File

@ -1,138 +0,0 @@
import RxSwift
import UIKit
struct CellFieldsJumpingServiceConfig {
var toolBarNeedArrows = true
init() {}
static var `default`: CellFieldsJumpingServiceConfig {
return CellFieldsJumpingServiceConfig()
}
}
class CellFieldsJumpingService {
private var disposeBag = DisposeBag()
// MARK: - Private properties
private var cellFields: [CellFieldJumpingProtocol] = []
// MARK: - Public propertries
var config: CellFieldsJumpingServiceConfig = .default {
didSet {
configure()
}
}
let didDone = PublishSubject<Void>()
// MARK: - Initialization
init() {}
// MARK: - Public
func removeAll() {
cellFields.removeAll()
disposeBag = DisposeBag()
}
func add(fieled: CellFieldJumpingProtocol, shouldConfigure: Bool = true) {
add(fieleds: [fieled], shouldConfigure: shouldConfigure)
}
func add(fieleds: [CellFieldJumpingProtocol], shouldConfigure: Bool = true) {
cellFields += fieleds
if shouldConfigure {
configure()
}
}
func configure() {
disposeBag = DisposeBag()
let cellFields = self.cellFields
cellFields
.filter { $0.isActive }
.enumerated()
.forEach { offset, field in
field.toolBar = toolBar(for: field, with: offset)
field.returnButtonType = .next
field.shouldGoForward.asObservable()
.subscribe(onNext: { [weak self] in
self?.shouldGoForwardAction(from: offset)
})
.disposed(by: disposeBag)
}
cellFields.lastActive?.returnButtonType = .done
}
private func toolBar(for field: CellFieldJumpingProtocol, with index: Int) -> UIToolbar {
let toolBar = CellTextFieldToolBar()
toolBar.canGoForward = cellFields.nextActive(from: index) != nil
toolBar.canGoBackward = cellFields.previousActive(from: index) != nil
toolBar.needArrows = config.toolBarNeedArrows
toolBar.shouldGoForward.asObservable()
.subscribe(onNext: { [weak self] in
self?.shouldGoForwardAction(from: index)
})
.disposed(by: disposeBag)
toolBar.shouldGoBackward
.subscribe(onNext: { [weak self] in
if let previousActive = self?.cellFields.previousActive(from: index) {
previousActive.shouldBecomeFirstResponder.onNext(Void())
}
})
.disposed(by: disposeBag)
toolBar.shouldEndEditing
.subscribe(onNext: { [field] in
field.shouldResignFirstResponder.onNext(Void())
})
.disposed(by: disposeBag)
return toolBar
}
private func shouldGoForwardAction(from index: Int) {
if let nextActive = cellFields.nextActive(from: index) {
nextActive.shouldBecomeFirstResponder.onNext(Void())
} else {
didDone.onNext(Void())
}
}
}
extension Array where Element == CellFieldJumpingProtocol {
var firstActive: CellFieldJumpingProtocol? {
return first { $0.isActive }
}
var lastActive: CellFieldJumpingProtocol? {
return reversed().first { $0.isActive }
}
func nextActive(from index: Int) -> CellFieldJumpingProtocol? {
return enumerated().first { $0 > index && $1.isActive }?.element
}
func previousActive(from index: Int) -> CellFieldJumpingProtocol? {
let reversedIndex = count - index - 1
return reversed().enumerated().first { $0 > reversedIndex && $1.isActive }?.element
}
}

View File

@ -1,60 +0,0 @@
import InputMask
import RxCocoa
import RxSwift
class MaskFieldTextProxy: NSObject {
private var disposeBag = DisposeBag()
fileprivate let text = Variable("")
fileprivate let isCompleteHolder = Variable(false)
var isComplete: Bool {
return isCompleteHolder.value
}
var isCompleteObservable: Observable<Bool> {
return isCompleteHolder.asObservable()
}
private(set) var field: UITextField?
private let maskedProxy: PolyMaskTextFieldDelegate
init(primaryFormat: String, affineFormats: [String] = []) {
maskedProxy = PolyMaskTextFieldDelegate(primaryFormat: primaryFormat, affineFormats: affineFormats)
super.init()
maskedProxy.listener = self
}
func configure(with field: UITextField) {
self.field = field
field.delegate = maskedProxy
}
private func bindData() {
disposeBag = DisposeBag()
text.asDriver()
.distinctUntilChanged()
.drive(onNext: { [weak self] value in
guard let textField = self?.field else {
return
}
self?.maskedProxy.put(text: value, into: textField)
})
.disposed(by: disposeBag)
}
}
extension MaskFieldTextProxy: MaskedTextFieldDelegateListener {
func textField(_ textField: UITextField, didFillMandatoryCharacters complete: Bool, didExtractValue value: String) {
text.value = value
isCompleteHolder.value = complete
}
}

View File

@ -1,60 +0,0 @@
//
// Copyright (c) 2017 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 LeadKit
import Alamofire
import ObjectMapper
import RxSwift
/// Base network service implementation for back-end designed by TouchInstinct
open class ApiNetworkService: DefaultNetworkService {
/// Returns observable for ImmutableMappable response model by parameters
open func request<T: ImmutableMappable>(with parameters: ApiRequestParameters) -> Observable<T> {
let apiResponseRequest = rxRequest(with: parameters) as Observable<(response: HTTPURLResponse, model: ApiResponse)>
return apiResponseRequest
.map {
if $0.model.errorCode == 0 {
return try T(JSON: try cast($0.model.result) as [String: Any])
} else {
throw ApiError(apiResponse: $0.model)
}
}
}
/// Returns observable for boolean response by parameters
open func requestForResult(with parameters: ApiRequestParameters) -> Observable<Bool> {
let apiResponseRequest = rxRequest(with: parameters) as Observable<(response: HTTPURLResponse, model: ApiResponse)>
return apiResponseRequest
.map {
if $0.model.errorCode == 0,
let result = $0.model.result as? Bool {
return result
} else {
throw ApiError(apiResponse: $0.model)
}
}
}
}

View File

@ -1,84 +0,0 @@
//
// Copyright (c) 2017 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 Alamofire
import LeadKit
import RxSwift
/// Default implementation of network service, which trust any server and use default timeout interval
open class DefaultNetworkService: NetworkService {
open class var retryLimit: UInt {
return 3
}
private let disposeBag = DisposeBag()
/// Override to set base server url
open class var baseUrl: String {
fatalError("You should override this var: baseUrl")
}
/// Override to change timeout interval default value
open class var defaultTimeoutInterval: TimeInterval {
return 20.0
}
/// The default acceptable range 200299
open var acceptableStatusCodes: [Int] {
return Alamofire.SessionManager.defaultAcceptableStatusCodes
}
public init(sessionManager: SessionManager) {
super.init(sessionManager: sessionManager, acceptableStatusCodes: acceptableStatusCodes)
// Fatal error: `drive*` family of methods can be only called from `MainThread`
DispatchQueue.main.async {
self.activityIndicatorBinding()?.disposed(by: self.disposeBag)
}
}
// MARK: - Default Values
/// Override to change server trust policies
open class var serverTrustPolicies: [String: ServerTrustPolicy] {
return [
baseUrl: .disableEvaluation
]
}
/// Override to change default urlSession configuration
open class var configuration: URLSessionConfiguration {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = defaultTimeoutInterval
return configuration
}
/// Override to configure alamofire session manager
open class var sessionManager: SessionManager {
let sessionManager = SessionManager(configuration: configuration,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
return sessionManager
}
}

View File

@ -1,56 +0,0 @@
//
// Copyright (c) 2017 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 LocalAuthentication
public typealias TouchIDServiceAuthHandler = (Bool) -> Void
/// Represents service that provides access to authentication via touch id
public class TouchIDService {
private lazy var laContext: LAContext = {
return LAContext()
}()
public init() {}
/// Indicates is it possible to authenticate on this device via touch id
public var canAuthenticateByTouchId: Bool {
return laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
}
/**
Initiates system touch id authentication process
- parameters:
- description: prompt on the system alert that describes what for user should attach finger to device
- authHandler: callback, with parameter, indicates if user authenticate successfuly
*/
public func authenticateByTouchId(description: String, authHandler: @escaping TouchIDServiceAuthHandler) {
laContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
localizedReason: description) { success, _ in
authHandler(success)
}
}
}

View File

@ -1,15 +0,0 @@
import SwiftValidator
struct ValidationError: Error {
let failedRule: Rule
let errorMessage: String?
let errorHint: String?
init(failedRule: Rule, errorMessage: String?, errorHint: String? = nil) {
self.failedRule = failedRule
self.errorMessage = errorMessage
self.errorHint = errorHint
}
}

View File

@ -1,96 +0,0 @@
import SwiftValidator
import RxSwift
import RxCocoa
enum ValidationItemState {
case initial
case correction(ValidationError)
case error(ValidationError)
case valid
}
extension ValidationItemState {
var isInitial: Bool {
switch self {
case .initial:
return true
default:
return false
}
}
var isValid: Bool {
switch self {
case .valid:
return true
default:
return false
}
}
}
class ValidationItem {
private let disposeBag = DisposeBag()
private let validationStateHolder = Variable<ValidationItemState>(.initial)
var validationState: ValidationItemState {
return validationStateHolder.value
}
var validationStateObservable: Observable<ValidationItemState> {
return validationStateHolder.asObservable()
}
private let text = Variable<String?>(nil)
private(set) var rules: [Rule] = []
init(rules: [Rule]) {
self.rules = rules
bindText()
}
private func bindText() {
text.asObservable()
.filter { [weak self] _ in !(self?.validationState.isInitial ?? true)}
.subscribe(onNext: { [weak self] value in
self?.validate(text: value)
})
.disposed(by: disposeBag)
}
@discardableResult
func manualValidate() -> Bool {
return validate(text: text.value, isManual: true)
}
@discardableResult
private func validate(text: String?, isManual: Bool = false) -> Bool {
let error = rules.filter {
return !$0.validate(text ?? "")
}
.map { rule -> ValidationError in
return ValidationError(failedRule: rule, errorMessage: rule.errorMessage())
}
.first
if let validationError = error {
switch validationStateHolder.value {
case .error where !isManual,
.correction where !isManual,
.valid where !isManual:
validationStateHolder.value = .correction(validationError)
default:
validationStateHolder.value = .error(validationError)
}
} else {
validationStateHolder.value = .valid
}
return validationStateHolder.value.isValid
}
}

View File

@ -1,46 +0,0 @@
import UIKit
import RxCocoa
import RxSwift
class CellTextField: UITextField {
private var disposeBag = DisposeBag()
var viewModel: CellTextFieldViewModel? {
didSet {
configure()
}
}
// MARK: - Init
private func configure() {
disposeBag = DisposeBag()
guard let viewModel = viewModel else {
return
}
inputAccessoryView = viewModel.toolBar
returnKeyType = viewModel.returnButtonType
text = viewModel.textValue
placeholder = viewModel.placeholder
viewModel.textFieldSettingsBlock?(self)
viewModel.bind(for: self, to: disposeBag)
rx.text.asDriver()
.drive(onNext: {
viewModel.setTextValue($0)
})
.disposed(by: disposeBag)
rx.controlEvent(.editingDidEndOnExit)
.subscribe(onNext: { [viewModel] in
viewModel.shouldGoForward.onNext(Void())
})
.disposed(by: disposeBag)
}
}

View File

@ -1,41 +0,0 @@
import UIKit
import RxSwift
class CellTextFieldViewModel: CellFieldJumpingProtocol {
private let text: Variable<String?>
let placeholder: String
let textFieldSettingsBlock: ItemSettingsBlock<UITextField>?
// MARK: - CellFieldJumpingProtocol
var toolBar: UIToolbar?
let shouldGoForward = PublishSubject<Void>()
let shouldBecomeFirstResponder = PublishSubject<Void>()
let shouldResignFirstResponder = PublishSubject<Void>()
var returnButtonType: UIReturnKeyType = .default
var isActive: Bool = true
init(initialText: String = "", placeholder: String = "", textFieldSettingsBlock: ItemSettingsBlock<UITextField>? = nil) {
text = Variable(initialText)
self.placeholder = placeholder
self.textFieldSettingsBlock = textFieldSettingsBlock
}
// MARK: - Internal
var textValue: String? {
return text.value
}
func setTextValue(_ value: String?) {
text.value = value
}
}

View File

@ -1,107 +0,0 @@
import UIKit
import RxSwift
import RxCocoa
import LeadKit
class CellTextFieldToolBar: UIToolbar, CellFieldsToolBarProtocol {
private let buttonSpace: CGFloat = 20
// MARK: - CellFieldsToolBarProtocol
var needArrows: Bool = true
var canGoForward: Bool = false {
didSet {
forwardButton.isEnabled = canGoForward
}
}
var canGoBackward: Bool = false {
didSet {
backButton.isEnabled = canGoBackward
}
}
var shouldGoForward = PublishSubject<Void>()
var shouldGoBackward = PublishSubject<Void>()
var shouldEndEditing = PublishSubject<Void>()
// MARK: - UIBarButtonItems
var backButtonImage: UIImage? {
didSet {
backButton.image = backButtonImage
}
}
var forwardButtonImage: UIImage? {
didSet {
backButton.image = backButtonImage
}
}
private(set) lazy var backButton: UIBarButtonItem = {
let backButton = UIBarButtonItem(image: self.backButtonImage,
style: .plain,
target: self,
action: #selector(backAction))
return backButton
}()
private(set) lazy var forwardButton: UIBarButtonItem = {
let forwardButton = UIBarButtonItem(image: self.forwardButtonImage,
style: .plain,
target: self,
action: #selector(forwardAction))
return forwardButton
}()
private(set) lazy var closeButton: UIBarButtonItem = {
let doneButton = UIBarButtonItem(barButtonSystemItem: .done,
target: self,
action: #selector(doneAction))
return doneButton
}()
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
initialization()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialization()
}
private func initialization() {
barStyle = .default
isTranslucent = true
tintColor = UIColor(hex6: 0x0A84DF)
sizeToFit()
let leftSpacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let firstButtonsSpacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
firstButtonsSpacer.width = buttonSpace
let secondButtonsSpacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
secondButtonsSpacer.width = buttonSpace
setItems([leftSpacer, backButton, firstButtonsSpacer, forwardButton, secondButtonsSpacer, closeButton], animated: true)
items?.forEach { $0.tintColor = tintColor }
}
// MARK: - Actions
@objc private func backAction() {
shouldGoBackward.onNext(Void())
}
@objc private func forwardAction() {
shouldGoForward.onNext(Void())
}
@objc private func doneAction() {
shouldEndEditing.onNext(Void())
}
}

22
Podfile Normal file
View File

@ -0,0 +1,22 @@
source 'https://cdn.cocoapods.org/'
source "https://github.com/TouchInstinct/Podspecs.git"
abstract_target 'LeadKitAdditions' do
pod "KeychainAccess", '~> 4.2.0'
pod "CryptoSwift", "~> 1.4.0"
pod "SwiftValidator", '4.0.2'
pod "SwiftLint", '~> 0.45.0'
pod "PinLayout", '~> 1.6'
inhibit_all_warnings!
use_frameworks!
target 'LeadKitAdditions iOS' do
platform :ios, '10.0'
pod 'LeadKit', '~> 1.7.0'
end
end
# If you have slow HDD
ENV['COCOAPODS_DISABLE_STATS'] = "true"

81
Podfile.lock Normal file
View File

@ -0,0 +1,81 @@
PODS:
- Alamofire (5.5.0)
- CryptoSwift (1.4.2)
- KeychainAccess (4.2.2)
- LeadKit (1.7.0):
- LeadKit/Core (= 1.7.0)
- LeadKit/Core (1.7.0):
- RxAlamofire (~> 6.1)
- RxCocoa (~> 6.2)
- RxSwift (~> 6.2)
- SnapKit (~> 5.0.1)
- SwiftDate (~> 6)
- TableKit (~> 2.11)
- UIScrollView-InfiniteScroll (~> 1.1.0)
- PinLayout (1.8.6)
- RxAlamofire (6.1.1):
- RxAlamofire/Core (= 6.1.1)
- RxAlamofire/Core (6.1.1):
- Alamofire (~> 5.4)
- RxSwift (~> 6.0)
- RxCocoa (6.2.0):
- RxRelay (= 6.2.0)
- RxSwift (= 6.2.0)
- RxRelay (6.2.0):
- RxSwift (= 6.2.0)
- RxSwift (6.2.0)
- SnapKit (5.0.1)
- SwiftDate (6.3.1)
- SwiftLint (0.45.1)
- SwiftValidator (4.0.2)
- TableKit (2.11.0)
- UIScrollView-InfiniteScroll (1.1.0)
DEPENDENCIES:
- CryptoSwift (~> 1.4.0)
- KeychainAccess (~> 4.2.0)
- LeadKit (~> 1.7.0)
- PinLayout (~> 1.6)
- SwiftLint (~> 0.45.0)
- SwiftValidator (= 4.0.2)
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:
Alamofire: 1c4fb5369c3fe93d2857c780d8bbe09f06f97e7c
CryptoSwift: a532e74ed010f8c95f611d00b8bbae42e9fe7c17
KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51
LeadKit: e5765a6edac2813042304e940fd7a43b37be80ca
PinLayout: fe2a2432d6982588e208572005c941aeeae417ab
RxAlamofire: beb75a1c452d0de225651db4903f5d29d034a620
RxCocoa: 4baf94bb35f2c0ab31bc0cb9f1900155f646ba42
RxRelay: e72dbfd157807478401ef1982e1c61c945c94b2f
RxSwift: d356ab7bee873611322f134c5f9ef379fa183d8f
SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb
SwiftDate: 72d28954e8e1c6c1c0f917ccc8005e4f83c7d4b2
SwiftLint: 06ac37e4d38c7068e0935bb30cda95f093bec761
SwiftValidator: 8517a67bceebd7286cb487da259db7dd44c5fc74
TableKit: 8a1a267d841a2da638ea09bdb0db33aedb459fa8
UIScrollView-InfiniteScroll: 3ef456bcbe759c19f510a383cff96e6647c98c98
PODFILE CHECKSUM: 1d0f91b670998d56b8cf9bc24d5b265a89e8409b
COCOAPODS: 1.10.1

View File

@ -0,0 +1,164 @@
//
// 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 LeadKit
import RxSwift
import RxCocoa
import SwiftValidator
/// Base implementation of TextFieldViewEvents.
open class BaseTextFieldViewEvents: TextFieldViewEvents {
public let textChangedDriver: Driver<String?>
/// Memberwise initializer.
///
/// - Parameter textChangedDriver: Driver that emits text changes from a view.
public init(textChangedDriver: Driver<String?>) {
self.textChangedDriver = textChangedDriver
}
}
/// Base implementation of text field view model events.
open class BaseTextFieldViewModelEvents: TextFieldViewModelEvents {
public let setTextDriver: Driver<String?>
public let changeValidationStateDriver: Driver<ValidationItemState>
public let changeOnlineValidationStateDriver: Driver<OnlineValidationState>
/// Memberwise initializer.
///
/// - Parameters:
/// - setTextDriver: Driver that emit text that should be set inside a view.
/// - changeValidationStateDriver: Driver that emit validation state changes events.
/// - changeOnlineValidationStateDriver: Driver that emit online validation state changes events.
public init(setTextDriver: Driver<String?>,
changeValidationStateDriver: Driver<ValidationItemState>,
changeOnlineValidationStateDriver: Driver<OnlineValidationState>) {
self.setTextDriver = setTextDriver
self.changeValidationStateDriver = changeValidationStateDriver
self.changeOnlineValidationStateDriver = changeOnlineValidationStateDriver
}
}
public extension BaseTextFieldViewModelEvents {
/// Method that binds text driver to validation service via validation rules.
///
/// - Parameters:
/// - textDriver: Driver that emits text changes.
/// - rules: Rules to validate for.
/// - validationService: Validation service to register in.
/// - Returns: Driver that emit validation state changes.
static func offlineValidationDriver(with textDriver: Driver<String?>,
using rules: [Rule] = [RequiredRule()],
in validationService: ValidationService) -> Driver<ValidationItemState> {
let validationItem = ValidationItem(rules: rules, textDriver: textDriver)
validationService.register(item: validationItem)
let validationStateDriver = validationItem
.validationStateObservable
.asDriver(onErrorJustReturn: .initial)
return validationStateDriver
}
typealias OnlineValidationClosure = (String) -> Single<OnlineValidateable>
/// Method that binds text driver to validation chain (offline validation -> online validation)
/// and returns online validation state driver.
///
/// - Parameters:
/// - textDriver: Driver that emits text changes.
/// - offlineRules: Rules to validate before online validation will be requested.
/// - validationClosure: Closure that will be called for online validation request.
/// - Returns: Driver that emit online validation state changes.
static func onlineValidationDriver(with textDriver: Driver<String?>,
using offlineRules: [Rule] = [],
validationClosure: @escaping OnlineValidationClosure) -> Driver<OnlineValidationState> {
textDriver.flatMap { string -> Driver<OnlineValidationState> in
guard let nonEmptyString = string, !nonEmptyString.isEmpty else {
return .just(.initial)
}
let passedRules = offlineRules
.allSatisfy { $0.validate(nonEmptyString) }
guard passedRules else {
return .just(.initial)
}
let validationDriver = validationClosure(nonEmptyString).map { result -> OnlineValidationState in
if result.isValid {
return .valid
} else {
return .invalid(error: result.error)
}
}
.asDriver(onErrorJustReturn: .initial)
return Driver.merge([.just(.initial), .just(.processing), validationDriver])
}
}
/// Convenience initializer with offline and online validation.
///
/// - Parameters:
/// - binding: Data model field binding.
/// - rules: Rules to validate before online validation will be requested.
/// - validationService: Validation service to register in.
/// - onlineValidationClosure: Closure that will be called for online validation request.
/// - onlineValidationThrottle: Throttling duration for each text change.
convenience init<T>(binding: DataModelFieldBinding<T>,
rules: [Rule] = [RequiredRule()],
validationService: ValidationService,
onlineValidationClosure: OnlineValidationClosure? = nil,
onlineValidationThrottle: RxTimeInterval = .milliseconds(500)) {
let dataModelFieldDriver = binding.fieldDriver
let offlineValidationDriver = BaseTextFieldViewModelEvents.offlineValidationDriver(with: dataModelFieldDriver,
using: rules,
in: validationService)
let onlineValidationDriver: Driver<OnlineValidationState>
if let onlineValidationClosure = onlineValidationClosure {
let throttledTextDriver = dataModelFieldDriver.throttle(onlineValidationThrottle)
onlineValidationDriver = BaseTextFieldViewModelEvents
.onlineValidationDriver(with: throttledTextDriver,
using: rules,
validationClosure: onlineValidationClosure)
} else {
onlineValidationDriver = .just(.initial)
}
self.init(setTextDriver: dataModelFieldDriver,
changeValidationStateDriver: offlineValidationDriver,
changeOnlineValidationStateDriver: onlineValidationDriver)
}
}

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -42,6 +42,7 @@ 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
} }
@ -50,6 +51,7 @@ 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
} }
@ -71,17 +73,16 @@ 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
} }
} }
} }
extension Observable { public extension Observable {
/** /**
Reactive extension for LoadingBarButton Reactive extension for LoadingBarButton
@ -92,13 +93,12 @@ extension Observable {
- Returns: - Returns:
- observable, that handles LoadingBarButton behaviour - observable, that handles LoadingBarButton behaviour
*/ */
public func changeLoadingUI(using barButton: LoadingBarButton) -> Observable<Observable.E> { func changeLoadingUI(using barButton: LoadingBarButton) -> Observable<Observable.Element> {
return observeOn(MainScheduler.instance) observe(on: 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

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -24,26 +24,22 @@
public struct PassCodeConfiguration { public struct PassCodeConfiguration {
/// Pass code length /// Pass code length
public var passCodeCharactersNumber: UInt = 4 public let passCodeLength: Int
/// Incorrect pass code attempts count /// Incorrect pass code attempts count
public var maxAttemptsLoginNumber: UInt = 5 public let maxAttemptsNumber: Int
/// Clear input progress when application goes to background /// Clear input progress when application goes to background
public var shouldResetWhenGoBackground: Bool = true public let shouldResetWhenGoBackground: Bool
private init() {} public init(passCodeLength: Int = 4, maxAttemptsNumber: Int = 5, shouldResetWhenGoBackground: Bool = true) {
self.passCodeLength = passCodeLength
init?(passCodeCharactersNumber: UInt) { self.maxAttemptsNumber = maxAttemptsNumber
guard passCodeCharactersNumber > 0 else { self.shouldResetWhenGoBackground = shouldResetWhenGoBackground
assertionFailure("passCodeCharactersNumber must be greater then 0")
return nil
}
self.passCodeCharactersNumber = passCodeCharactersNumber
} }
/// Returns configuration with default values /// Returns configuration with default values
public static var defaultConfiguration: PassCodeConfiguration { public static var defaultConfiguration: PassCodeConfiguration {
return PassCodeConfiguration() PassCodeConfiguration()
} }
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -20,9 +20,16 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
/// Describes error, which may occur during pass code entering import Foundation
public enum PassCodeError: Error {
case codesNotMatch /// Describes attributed string and time interval that message should be displayed after
case wrongCode public struct PassCodeDelayedDescription {
case tooManyAttempts
let delay: TimeInterval
let description: NSAttributedString
public init(description: NSAttributedString, delay: TimeInterval = 0) {
self.description = description
self.delay = delay
}
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -20,20 +20,26 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
/// Describes error by raw value (more likely - Int code), received from back-end /// Describes error, which may occur during pass code entering
public protocol ApiErrorProtocol: RawRepresentable {} public enum PassCodeError: Error {
/// Different codes
case codesNotMatch
extension Error { /// Value is remaining attemps
case wrongCode(attemptsRemaining: Int)
/// Method indicates that error is back-end error /// Attempts limit reached (for create, change or enter)
public func isApiError<T: ApiErrorProtocol>(_ apiErrorType: T) -> Bool where T.RawValue == Int { case tooManyAttempts(type: PassCodeOperationType)
if let error = self as? ApiError, }
case let .error(code: code, message: _) = error,
code == apiErrorType.rawValue { public extension PassCodeError {
var isTooManyAttempts: Bool {
switch self {
case .tooManyAttempts:
return true return true
} else {
default:
return false return false
} }
} }
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -23,25 +23,25 @@
import RxSwift import RxSwift
import RxCocoa import RxCocoa
extension PassCodeHolderProtocol { public extension PassCodeHolderProtocol {
public var passCodeHolderCreate: PassCodeHolderCreate? { var passCodeHolderCreate: PassCodeHolderCreate? {
return self as? PassCodeHolderCreate self as? PassCodeHolderCreate
} }
public var passCodeHolderEnter: PassCodeHolderEnter? { var passCodeHolderEnter: PassCodeHolderEnter? {
return self as? PassCodeHolderEnter self as? PassCodeHolderEnter
} }
public var passCodeHolderChange: PassCodeHolderChange? { var passCodeHolderChange: PassCodeHolderChange? {
return self as? PassCodeHolderChange 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: PassCodeControllerType = .create public let type: PassCodeOperationType = .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 {
return firstPassCode != nil && secondPassCode != nil firstPassCode != nil && secondPassCode != nil
} }
public var passCode: String? { public var passCode: String? {
@ -70,8 +70,10 @@ 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
} }
@ -81,25 +83,24 @@ public class PassCodeHolderCreate: PassCodeHolderProtocol {
if let passCode = passCode { if let passCode = passCode {
return .valid(passCode) return .valid(passCode)
} else { } else {
return .inValid(.codesNotMatch) return .invalid(.codesNotMatch)
} }
} }
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: PassCodeControllerType = .enter public let type: PassCodeOperationType = .enter
public let enterStep: PassCodeControllerState = .enter public let enterStep: PassCodeControllerState = .enter
public var shouldValidate: Bool { public var shouldValidate: Bool {
return passCode != nil passCode != nil
} }
public var passCode: String? public var passCode: String?
@ -112,20 +113,19 @@ public class PassCodeHolderEnter: PassCodeHolderProtocol {
if let passCode = passCode { if let passCode = passCode {
return .valid(passCode) return .valid(passCode)
} else { } else {
return .inValid(nil) return .invalid(nil)
} }
} }
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: PassCodeControllerType = .change public let type: PassCodeOperationType = .change
private var oldPassCode: String? private var oldPassCode: String?
private var newFirstPassCode: String? private var newFirstPassCode: String?
@ -153,12 +153,15 @@ 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
} }
@ -178,7 +181,7 @@ public class PassCodeHolderChange: PassCodeHolderProtocol {
if let passCode = passCode { if let passCode = passCode {
return .valid(passCode) return .valid(passCode)
} else { } else {
return .inValid(enterStep == .newEnter ? nil : .codesNotMatch) return .invalid(enterStep == .newEnter ? nil : .codesNotMatch)
} }
} }
@ -190,5 +193,4 @@ public class PassCodeHolderChange: PassCodeHolderProtocol {
newSecondPassCode = nil newSecondPassCode = nil
} }
} }
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -25,7 +25,7 @@
public protocol PassCodeHolderProtocol { public protocol PassCodeHolderProtocol {
/// Type of operation with pass code /// Type of operation with pass code
var type: PassCodeControllerType { get } var type: PassCodeOperationType { get }
/// Operation step /// Operation step
var enterStep: PassCodeControllerState { get } var enterStep: PassCodeControllerState { get }
@ -41,7 +41,6 @@ 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 {
@ -54,15 +53,16 @@ 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: PassCodeControllerType) -> PassCodeHolderProtocol { public static func build(with type: PassCodeOperationType) -> 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

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -24,33 +24,37 @@
public enum PassCodeValidationResult { public enum PassCodeValidationResult {
case valid(String) case valid(String)
case inValid(PassCodeError?) case invalid(PassCodeError?)
}
public var isValid: Bool { public extension PassCodeValidationResult {
var isValid: Bool {
switch self { switch self {
case .valid: case .valid:
return true return true
default:
case .invalid:
return false return false
} }
} }
public var passCode: String? { var passCode: String? {
switch self { switch self {
case let .valid(passCode): case let .valid(passCode):
return passCode return passCode
default:
case .invalid:
return nil return nil
} }
} }
public var error: PassCodeError? { var error: PassCodeError? {
switch self { switch self {
case let .inValid(error): case let .invalid(error):
return error return error
default:
case .valid:
return nil return nil
} }
} }
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -32,7 +32,7 @@ public enum PinImageType {
} }
/// Pass code operation type /// Pass code operation type
public enum PassCodeControllerType { public enum PassCodeOperationType {
case create case create
case enter case enter
case change case change
@ -47,19 +47,20 @@ public enum PassCodeControllerState {
} }
/// Base view controller that operates with pass code /// Base view controller that operates with pass code
open class BasePassCodeViewController: UIViewController { open class BasePassCodeViewController: UIViewController, LegacyConfigurableController {
public var viewModel: BasePassCodeViewModel! public var viewModel: BasePassCodeViewModel! // swiftlint:disable:this implicitly_unwrapped_optional
// MARK: - IBOutlets // MARK: - IBOutlets
@IBOutlet public weak var titleLabel: UILabel? @IBOutlet private weak var titleLabel: UILabel?
@IBOutlet public weak var errorLabel: UILabel? @IBOutlet private weak var errorLabel: UILabel?
@IBOutlet public weak var dotStackView: UIStackView! @IBOutlet private weak var dotStackView: UIStackView!
public let disposeBag = DisposeBag() public let disposeBag = DisposeBag()
private var delayedErrorDescriptions: Disposable?
fileprivate lazy var fakeTextField: UITextField = { private lazy var fakeTextField: UITextField = {
let fakeTextField = UITextField() let fakeTextField = UITextField()
fakeTextField.isSecureTextEntry = true fakeTextField.isSecureTextEntry = true
fakeTextField.keyboardType = .numberPad fakeTextField.keyboardType = .numberPad
@ -76,9 +77,20 @@ open class BasePassCodeViewController: UIViewController {
initialLoadView() initialLoadView()
initialDotNumberConfiguration() initialDotNumberConfiguration()
enebleKeyboard()
configureBackgroundNotifications() configureBackgroundNotifications()
showTouchIdIfNeeded(with: touchIdHint) showBiometricsRequestIfNeeded()
}
override open func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fakeTextField.becomeFirstResponder()
}
override open func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
fakeTextField.resignFirstResponder()
} }
// MARK: - Private functions // MARK: - Private functions
@ -88,21 +100,17 @@ open class BasePassCodeViewController: UIViewController {
return return
} }
NotificationCenter.default.rx.notification(.UIApplicationWillResignActive) NotificationCenter.default.rx.notification(UIApplication.willResignActiveNotification)
.subscribe(onNext: { [weak self] _ in .subscribe(onNext: { [weak self] _ in
self?.resetUI() self?.resetUI()
}) })
.disposed(by: disposeBag) .disposed(by: disposeBag)
} }
private func enebleKeyboard() {
fakeTextField.becomeFirstResponder()
}
private func initialDotNumberConfiguration() { private func initialDotNumberConfiguration() {
dotStackView.arrangedSubviews.forEach { dotStackView.removeArrangedSubview($0) } dotStackView.arrangedSubviews.forEach { dotStackView.removeArrangedSubview($0) }
for _ in 0..<viewModel.passCodeConfiguration.passCodeCharactersNumber { for _ in 0 ..< viewModel.passCodeConfiguration.passCodeLength {
let dotImageView = UIImageView() let dotImageView = UIImageView()
dotImageView.translatesAutoresizingMaskIntoConstraints = false dotImageView.translatesAutoresizingMaskIntoConstraints = false
dotImageView.widthAnchor.constraint(equalTo: dotImageView.heightAnchor, multiplier: 1) dotImageView.widthAnchor.constraint(equalTo: dotImageView.heightAnchor, multiplier: 1)
@ -113,10 +121,10 @@ open class BasePassCodeViewController: UIViewController {
resetDotsUI() resetDotsUI()
} }
fileprivate func resetDotsUI() { private func resetDotsUI() {
fakeTextField.text = nil fakeTextField.text = nil
dotStackView.arrangedSubviews dotStackView.arrangedSubviews
.flatMap { $0 as? UIImageView } .compactMap { $0 as? UIImageView }
.forEach { $0.image = self.imageFor(type: .clear) } .forEach { $0.image = self.imageFor(type: .clear) }
} }
@ -129,11 +137,11 @@ open class BasePassCodeViewController: UIViewController {
imageView.image = imageFor(type: state) imageView.image = imageFor(type: state)
} }
fileprivate func setStates(for passCodeText: String) { private func setStates(for passCodeText: String) {
var statesArray: [PinImageType] = [] var statesArray: [PinImageType] = []
for characterIndex in 0..<viewModel.passCodeConfiguration.passCodeCharactersNumber { for characterIndex in 0..<viewModel.passCodeConfiguration.passCodeLength {
let state: PinImageType = Int(characterIndex) <= passCodeText.characters.count - 1 ? .entered : .clear let state: PinImageType = Int(characterIndex) <= passCodeText.count - 1 ? .entered : .clear
statesArray.append(state) statesArray.append(state)
} }
@ -142,19 +150,7 @@ open class BasePassCodeViewController: UIViewController {
} }
} }
fileprivate func showTouchIdIfNeeded(with description: String) { private func resetUI() {
guard viewModel.isTouchIdEnabled && viewModel.controllerType == .enter else {
return
}
viewModel.touchIdService?.authenticateByTouchId(description: description) { [weak self] isSuccess in
if isSuccess {
self?.viewModel.authSucceed(.touchId)
}
}
}
fileprivate func resetUI() {
resetDotsUI() resetDotsUI()
viewModel.reset() viewModel.reset()
} }
@ -162,11 +158,23 @@ open class BasePassCodeViewController: UIViewController {
// MARK: - HAVE TO OVERRIDE // MARK: - HAVE TO OVERRIDE
/// Returns prompt that appears on touch id system alert /// Returns prompt that appears on touch id system alert
open var touchIdHint: String { open var biometricsAuthorizationHint: String {
assertionFailure("You should override this var: touchIdHint") assertionFailure("You should override this \(#function)")
return "" return ""
} }
/// Returns prompt that appears on touch id system alert
open var biometricsFallbackButtonTitle: String? {
assertionFailure("You should override this \(#function)")
return nil
}
/// Returns prompt that appears on touch id system alert
open var biometricsCancelButtonTitle: String? {
assertionFailure("You should override this \(#function)")
return nil
}
/// Override to point certain images /// Override to point certain images
open func imageFor(type: PinImageType) -> UIImage { open func imageFor(type: PinImageType) -> UIImage {
assertionFailure("You should override this method: imageFor(type: PinImageType)") assertionFailure("You should override this method: imageFor(type: PinImageType)")
@ -174,23 +182,41 @@ open class BasePassCodeViewController: UIViewController {
} }
/// Override to change error description /// Override to change error description
open func errorDescription(for error: PassCodeError) -> String { open func errorDescription(for error: PassCodeError) -> [PassCodeDelayedDescription] {
assertionFailure("You should override this method: errorDescription(for error: PassCodeError)") assertionFailure("You should override this method: errorDescription(for error: PassCodeError)")
return "" return []
} }
/// Override to change action title text /// Override to change action title text
open func actionTitle(for passCodeControllerState: PassCodeControllerState) -> String { open func actionTitle(for passCodeControllerState: PassCodeControllerState) -> NSAttributedString {
assertionFailure("You should override this method: actionTitle(for passCodeControllerState: PassCodeControllerState)") assertionFailure("You should override this method: actionTitle(for passCodeControllerState: PassCodeControllerState)")
return "" return NSAttributedString(string: "")
} }
// MARK: - Functions that you can override to customize your controller // MARK: - Functions that you can override to customize your controller
/// Call to show error /// Call to show error
open func showError(for error: PassCodeError) { open func showError(for error: PassCodeError) {
errorLabel?.text = errorDescription(for: error) let descriptionsObservables = errorDescription(for: error)
.sorted { $0.delay < $1.delay }
.map { [weak self] delayedDescription in
Observable<Int>
.interval(.seconds(Int(delayedDescription.delay)),
scheduler: MainScheduler.instance)
.take(1)
.do(onNext: { _ in
self?.errorLabel?.attributedText = delayedDescription.description
})
}
delayedErrorDescriptions?.dispose()
errorLabel?.attributedText = nil
errorLabel?.isHidden = false errorLabel?.isHidden = false
delayedErrorDescriptions = Observable
.merge(descriptionsObservables)
.subscribe()
} }
/// Call to disappear error label /// Call to disappear error label
@ -201,25 +227,47 @@ open class BasePassCodeViewController: UIViewController {
/// Override to change UI for state /// Override to change UI for state
open func configureUI(for passCodeControllerState: PassCodeControllerState) { open func configureUI(for passCodeControllerState: PassCodeControllerState) {
resetDotsUI() resetDotsUI()
titleLabel?.text = actionTitle(for: passCodeControllerState) titleLabel?.attributedText = actionTitle(for: passCodeControllerState)
} }
} // MARK: - Public functions
// MARK: - ConfigurableController /// Make fakeTextField become first responder
// We need to implement all functions of ConfigurableController protocol to give ability to override them. public func enableInput() {
extension BasePassCodeViewController: ConfigurableController { fakeTextField.becomeFirstResponder()
}
/// Make fakeTextField resign first responder
public func disableInput() {
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
open func bindViews() { open func bindViews() {
fakeTextField.rx.text.asDriver() fakeTextField.rx.text.asDriver()
.drive(onNext: { [weak self] text in .do(onNext: { [weak self] text in
self?.setStates(for: text ?? "") self?.setStates(for: text ?? "")
self?.hideError() self?.hideError()
})
.delay(.milliseconds(100)) // time to draw dots
.drive(onNext: { [weak self] text in
self?.viewModel.setPassCodeText(text) self?.viewModel.setPassCodeText(text)
}) })
.disposed(by: disposeBag) .disposed(by: disposeBag)
viewModel.validationResult viewModel.validationResultDriver
.drive(onNext: { [weak self] validationResult in .drive(onNext: { [weak self] validationResult in
guard let validationResult = validationResult else { guard let validationResult = validationResult else {
return return
@ -227,13 +275,13 @@ extension BasePassCodeViewController: ConfigurableController {
if validationResult.isValid { if validationResult.isValid {
self?.hideError() self?.hideError()
} else if let pasCodeError = validationResult.error { } else if let passCodeError = validationResult.error {
self?.showError(for: pasCodeError) self?.showError(for: passCodeError)
} }
}) })
.disposed(by: disposeBag) .disposed(by: disposeBag)
viewModel.passCodeControllerState viewModel.passCodeControllerStateDriver
.drive(onNext: { [weak self] controllerState in .drive(onNext: { [weak self] controllerState in
self?.configureUI(for: controllerState) self?.configureUI(for: controllerState)
}) })
@ -242,15 +290,15 @@ extension BasePassCodeViewController: ConfigurableController {
open func addViews() {} open func addViews() {}
open func setAppearance() {} open func configureAppearance() {}
open func configureBarButtons() {} open func configureBarButtons() {}
open func localize() {} open func localize() {}
} }
// MARK: - UITextFieldDelegate // MARK: - UITextFieldDelegate
extension BasePassCodeViewController: UITextFieldDelegate { extension BasePassCodeViewController: UITextFieldDelegate {
public func textField(_ textField: UITextField, public func textField(_ textField: UITextField,
@ -260,5 +308,4 @@ 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

@ -0,0 +1,235 @@
//
// 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 RxSwift
import RxCocoa
/// Describes types of authentication
public enum PassCodeAuthType {
case passCode(String)
case touchId
}
/// Base view model for passCodeViewController
open class BasePassCodeViewModel {
public let operationType: PassCodeOperationType
public let disposeBag = DisposeBag()
/// Service that can answer if user is authorized by biometrics
public let biometricsService = BiometricsService()
/// Contains configuration for pass code operations
public let passCodeConfiguration: PassCodeConfiguration
private let validationResultHolder = BehaviorRelay<PassCodeValidationResult?>(value: nil)
public var validationResultDriver: Driver<PassCodeValidationResult?> {
validationResultHolder.asDriver()
}
private let passCodeControllerStateVariable = BehaviorRelay<PassCodeControllerState>(value: .enter)
public var passCodeControllerStateDriver: Driver<PassCodeControllerState> {
passCodeControllerStateVariable.asDriver()
}
private let passCodeText = BehaviorRelay<String?>(value: nil)
private var attemptsNumber = 0
private lazy var passCodeHolder: PassCodeHolderProtocol = PassCodeHolderBuilder.build(with: self.operationType)
public init(operationType: PassCodeOperationType, passCodeConfiguration: PassCodeConfiguration) {
self.operationType = operationType
self.passCodeConfiguration = passCodeConfiguration
bindViewModel()
}
private func bindViewModel() {
passCodeText.asDriver()
.distinctUntilChanged { $0 == $1 }
.drive(onNext: { [weak self] passCode in
if let passCode = passCode,
passCode.count == Int(self?.passCodeConfiguration.passCodeLength ?? 0) {
self?.set(passCode: passCode)
}
})
.disposed(by: disposeBag)
validationResultHolder.asObservable()
.bind(to: validationResultBinder)
.disposed(by: disposeBag)
}
// MARK: - Public
public var passCodeTextValue: String? {
passCodeText.value
}
public func setPassCodeText(_ value: String?) {
passCodeText.accept(value)
}
public func reset() {
passCodeText.accept(nil)
validationResultHolder.accept(nil)
passCodeControllerStateVariable.accept(operationType == .change ? .oldEnter : .enter)
attemptsNumber = 0
passCodeHolder.reset()
}
public func authenticateUsingBiometrics(with description: String, fallback: String?, cancel: String?) {
biometricsAuthBegins()
biometricsService.authenticateWithBiometrics(with: description,
fallback: fallback,
cancel: cancel) { [weak self] success, error in
self?.biometricsAuthEnds()
if success {
self?.authSucceed(.touchId)
} else {
self?.authFailed(with: error)
}
}
}
// MARK: - HAVE TO OVERRIDE
/// Override to check if entered pass code is equal to stored
open func isEnteredPassCodeValid(_ passCode: String) -> Bool {
assertionFailure("You should override this method: isEnteredPassCodeValid(_ passCode: String)")
return false
}
/// Method is called after successful authentication
open func authSucceed(_ type: PassCodeAuthType) {
assertionFailure("You should override this method: authSucceed(_ type: PassCodeAuthType)")
}
/// Called when authentication failed
open func authFailed(with: Error?) {
assertionFailure("You should override this method: authFailed(with: Error)")
}
// MARK: - Biometrics
/// Posibility to use biometrics for authentication
open var isBiometricsEnabled: Bool {
false
}
/// Notify about activation for biometrics. Remember to save user choice
open func activateBiometricsForUser() {
assertionFailure("You should override this method: activateBiometricsForUser()")
}
/// Notify before system alert with biometrics
open func biometricsAuthBegins() {}
/// Notify after system alert with biometrics
open func biometricsAuthEnds() {}
}
private extension BasePassCodeViewModel {
var validationResultBinder: Binder<PassCodeValidationResult?> {
Binder(self) { model, validationResult in
let isValid = validationResult?.isValid ?? false
let passCode = validationResult?.passCode
if model.passCodeHolder.type == .change {
if isValid, model.passCodeHolder.enterStep == .repeatEnter, let passCode = passCode {
model.authSucceed(.passCode(passCode))
} else {
model.passCodeControllerStateVariable.accept(model.passCodeHolder.enterStep)
}
} else {
if isValid, let passCode = passCode {
model.authSucceed(.passCode(passCode))
} else {
model.passCodeControllerStateVariable.accept(model.passCodeHolder.enterStep)
}
}
}
}
}
extension BasePassCodeViewModel {
private func set(passCode: String) {
passCodeHolder.add(passCode: passCode)
validateIfNeeded()
if shouldUpdateControllerState {
passCodeControllerStateVariable.accept(passCodeHolder.enterStep)
}
}
private var shouldUpdateControllerState: Bool {
!passCodeHolder.shouldValidate ||
!(validationResultHolder.value?.isValid ?? true) ||
validationResultHolder.value?.error?.isTooManyAttempts ?? false
}
private func validateIfNeeded() {
guard passCodeHolder.shouldValidate else {
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()
// if entered (in .enter mode) code is invalid -> .wrongCode
if passCodeHolder.type == .enter,
let passCode = validationResult.passCode,
!isEnteredPassCodeValid(passCode) {
let remainingAttemptsCount = passCodeConfiguration.maxAttemptsNumber - attemptsNumber
validationResult = .invalid(.wrongCode(attemptsRemaining: remainingAttemptsCount))
}
// if entered code (in any mode) is mismatched too many times -> .tooManyAttempts
if (!validationResult.isValid && attemptsNumber == passCodeConfiguration.maxAttemptsNumber) ||
attemptsNumber > passCodeConfiguration.maxAttemptsNumber {
validationResult = .invalid(.tooManyAttempts(type: operationType))
}
if !validationResult.isValid {
passCodeHolder.reset()
}
validationResultHolder.accept(validationResult)
}
}

View File

@ -0,0 +1,38 @@
//
// 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 RxSwift
public extension BaseTextFieldViewEvents {
/// Convenience init with UITextField as textChangedDriver.
///
/// - Parameter textField: UITextField to use for text events.
convenience init(textField: UITextField) {
let textChangedDriver = textField.rx
.text
.asDriver()
.distinctUntilChanged { $0 == $1 }
self.init(textChangedDriver: textChangedDriver)
}
}

View File

@ -15,7 +15,6 @@ 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 {
return (self as? AFError)?.isResponseSerializationError ?? false (self as? AFError)?.isResponseSerializationError ?? false
} }
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -22,13 +22,12 @@
import UIKit import UIKit
extension UIBarButtonItem { 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)
public static var activityIndicator: (barButton: UIBarButtonItem, activityIndicator: UIActivityIndicatorView) { static var activityIndicator: (barButton: UIBarButtonItem, activityIndicator: UIActivityIndicatorView) {
let indicatorView = UIActivityIndicatorView(activityIndicatorStyle: .white) let indicatorView = UIActivityIndicatorView(style: .white)
let indicatorBar = UIBarButtonItem(customView: indicatorView) let indicatorBar = UIBarButtonItem(customView: indicatorView)
return (indicatorBar, indicatorView) return (indicatorBar, indicatorView)
} }
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -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 {
return string(forKey: Keys.sessionId) string(forKey: Keys.sessionId)
} }
set { set {
set(newValue, forKey: Keys.sessionId) set(newValue, forKey: Keys.sessionId)
@ -42,11 +42,10 @@ public extension UserDefaults {
/// Default place to store userLogin /// Default place to store userLogin
var userLogin: String? { var userLogin: String? {
get { get {
return string(forKey: Keys.userLogin) 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.0.1</string> <string>0.3.13</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

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -20,32 +20,32 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
/// Describes possible error, received from back-end import LeadKit
public enum ApiError: Error {
case error(code: Int, message: String) public protocol LegacyConfigurableController: InitializableView {
case none
associatedtype ViewModelT
var viewModel: ViewModelT! { get } // swiftlint:disable:this implicitly_unwrapped_optional
func configureBarButtons()
func initialLoadView()
} }
// MARK: - LocalizedError public extension LegacyConfigurableController where Self: UIViewController {
extension ApiError: LocalizedError {
public init(apiResponse: ApiResponseProtocol) { func initializeView() {
if apiResponse.errorCode != 0, let message = apiResponse.errorMessage { assertionFailure("Use \(String(describing: initialLoadView)) for UIViewController instead!")
self = ApiError.error(code: apiResponse.errorCode, message: message)
} else {
self = ApiError.none
}
} }
public var errorDescription: String? { /// Method that should be called in viewDidLoad method of UIViewController.
switch self { func initialLoadView() {
case .error(_, let message): addViews()
return message configureLayout()
case .none: configureAppearance()
return nil configureBarButtons()
} localize()
bindViews()
} }
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -20,12 +20,14 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
import RxSwift import Foundation
extension DefaultNetworkService { /// Prototol with two fields that describes result of online validation.
public protocol OnlineValidateable {
func activityIndicatorBinding() -> Disposable? { /// Contains true if online validation did passed.
return nil var isValid: Bool { get }
}
/// May contain an error if validation did failed.
var error: Error? { get }
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -21,15 +21,25 @@
// //
import KeychainAccess import KeychainAccess
import CocoaLumberjack import CryptoSwift
import IDZSwiftCommonCrypto
private enum Keys {
static let passCodeHash = "passCodeHashKey"
static let isBiometricsEnabled = "isBiometricsEnabledKey"
static let isInitialLoad = "isInitialLoadKey"
}
private enum Values {
static let biometricsEnabled = "biometricsEnabled"
static let initialLoad = "initialLoad"
}
/// Represents base pass code service which encapsulates pass code storing /// Represents base pass code service which encapsulates pass code storing
open class BasePassCodeService { open class BasePassCodeService {
/// Override to set specific keychain service name /// Override to set specific keychain service name
open class var keychainService: String { open class var keychainServiceString: String {
return Bundle.main.bundleIdentifier ?? "" Bundle.main.bundleIdentifier ?? ""
} }
public init() { public init() {
@ -42,74 +52,47 @@ open class BasePassCodeService {
// MARK: - Private stuff // MARK: - Private stuff
fileprivate lazy var keychain: Keychain = { private lazy var keychain = Keychain(service: BasePassCodeService.keychainServiceString).synchronizable(false)
return Keychain(service: BasePassCodeService.keychainService)
.synchronizable(false)
}()
fileprivate var passCodeHash: String? { private var passCodeHash: String? {
return keychain[Keys.passCodeHash] keychain[Keys.passCodeHash]
} }
fileprivate enum Keys {
static let passCodeHash = "passCodeHash"
static let isTouchIdEnabled = "isTouchIdEnabled"
static let isInitialLoad = "isInitialLoad"
}
fileprivate enum Values {
static let touchIdEnabled = "touchIdEnabled"
static let initialLoad = "initialLoad"
}
} }
extension BasePassCodeService { public extension BasePassCodeService {
/// Indicates is pass code already saved on this device /// Indicates is pass code already saved on this device
public var isPassCodeSaved: Bool { var isPassCodeSaved: Bool {
return keychain[Keys.passCodeHash] != nil keychain[Keys.passCodeHash] != nil
} }
/// Indicates is it possible to authenticate on this device via touch id /// Possibility to authenticate via biometrics. TouchID or FaceID
public var isTouchIdEnabled: Bool { var isBiometricsAuthorizationEnabled: Bool {
get { get {
return keychain[Keys.isTouchIdEnabled] == Values.touchIdEnabled keychain[Keys.isBiometricsEnabled] == Values.biometricsEnabled
} }
set { set {
keychain[Keys.isTouchIdEnabled] = newValue ? Values.touchIdEnabled : nil keychain[Keys.isBiometricsEnabled] = newValue ? Values.biometricsEnabled : nil
} }
} }
/// Saves new pass code /// Saves new pass code
public func save(passCode: String?) { func save(passCode: String?) {
if let passCode = passCode { if let passCode = passCode {
keychain[Keys.passCodeHash] = sha256(passCode) keychain[Keys.passCodeHash] = passCode.sha256()
} else { } else {
keychain[Keys.passCodeHash] = nil keychain[Keys.passCodeHash] = nil
} }
} }
/// Check if pass code is correct /// Check if pass code is correct
public func check(passCode: String) -> Bool { func check(passCode: String) -> Bool {
return sha256(passCode) == passCodeHash passCode.sha256() == passCodeHash
} }
/// Reset pass code settings /// Reset pass code settings
public func reset() { func reset() {
save(passCode: nil) save(passCode: nil)
isTouchIdEnabled = false isBiometricsAuthorizationEnabled = false
} }
}
private extension BasePassCodeService {
func sha256(_ str: String) -> String? {
guard let digests = Digest(algorithm: .sha256).update(string: str)?.final() else {
return nil
}
return hexString(fromArray: digests)
}
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -21,7 +21,6 @@
// //
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 {
@ -51,7 +50,7 @@ open class BaseUserService {
/// Indicates if user is logged in /// Indicates if user is logged in
open var isLoggedIn: Bool { open var isLoggedIn: Bool {
return UserDefaults.standard.sessionId != nil UserDefaults.standard.sessionId != nil
} }
/// Reset user information /// Reset user information
@ -59,5 +58,4 @@ open class BaseUserService {
UserDefaults.standard.sessionId = nil UserDefaults.standard.sessionId = nil
UserDefaults.standard.userLogin = nil UserDefaults.standard.userLogin = nil
} }
} }

View File

@ -0,0 +1,122 @@
//
// 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 LocalAuthentication
public typealias BiometricsAuthHandler = (Bool, Error?) -> Void
/// Service that provide access to authentication via biometric
public final class BiometricsService {
public enum BiometryType {
case faceID
case touchID
case none
}
private lazy var laContext = LAContext()
/// If the user unlocks the device using biometrics within the specified time interval,
/// then authentication for the receiver succeeds automatically, without prompting the user for biometrics.
/// 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, *) {
switch laContext.biometryType {
case .touchID:
return .touchID
case .faceID:
return .faceID
case .none:
return .none
@unknown default:
return .none
}
}
return canEvaluate ? .touchID : .none
}
/// Returns current domain state
public var evaluatedPolicyDomainState: Data? {
// We need to call canEvaluatePolicy function for evaluatedPolicyDomainState to be updated
_ = canAuthenticateWithBiometrics
return laContext.evaluatedPolicyDomainState
}
/// Indicates is it possible to authenticate on this device via any biometric.
/// Returns false if not enrolled or lockedout.
public var canAuthenticateWithBiometrics: Bool {
laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
}
/**
Initiates system biometrics authentication process.
Once evaluated, will return success until the context is deallocated.
Call "clear" to use a new context instance.
- parameters:
- description: prompt on the system alert
- fallback: alternative action button title on system alert
- cancel: cancel button title on the system alert
- authHandler: callback, with parameter, indicates if user authenticate successfuly
*/
public func authenticateWithBiometrics(with description: String,
fallback fallbackTitle: String?,
cancel cancelTitle: String?,
authHandler: @escaping BiometricsAuthHandler) {
if #available(iOS 10.0, *) {
laContext.localizedCancelTitle = cancelTitle
}
laContext.localizedFallbackTitle = fallbackTitle
laContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: description) { success, error in
authHandler(success, error)
}
}
/// Replace old instance of the context with the new one
public func clear() {
laContext = LAContext()
}
public init() {}
}

View File

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Touch Instinct // Copyright (c) 2018 Touch Instinct
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal // of this software and associated documentation files (the Software), to deal
@ -20,13 +20,16 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
import RxSwift /// Enum that describes current state of online validation.
import LeadKit ///
/// - initial: Nothing did happen.
extension DefaultNetworkService { /// - processing: Processing validation.
/// - valid: Got a valid result.
func activityIndicatorBinding() -> Disposable? { /// - invalid: Got an invalid result.
return bindActivityIndicator() public enum OnlineValidationState {
}
case initial
case processing
case valid
case invalid(error: Error?)
} }

View File

@ -0,0 +1,14 @@
import SwiftValidator
public struct ValidationError: Error {
public let failedRule: Rule
public let errorMessage: String?
public let errorHint: String?
public init(failedRule: Rule, errorMessage: String?, errorHint: String? = nil) {
self.failedRule = failedRule
self.errorMessage = errorMessage
self.errorHint = errorHint
}
}

View File

@ -0,0 +1,102 @@
import SwiftValidator
import RxSwift
import RxCocoa
public enum ValidationItemState {
case initial
case correction(ValidationError)
case error(ValidationError)
case valid
}
public extension ValidationItemState {
var isInitial: Bool {
switch self {
case .initial:
return true
default:
return false
}
}
var isValid: Bool {
switch self {
case .valid:
return true
default:
return false
}
}
}
public final class ValidationItem {
private let disposeBag = DisposeBag()
private let validationStateHolder = BehaviorRelay<ValidationItemState>(value: .initial)
public var validationState: ValidationItemState {
validationStateHolder.value
}
public var validationStateObservable: Observable<ValidationItemState> {
validationStateHolder.asObservable()
}
private let text = BehaviorRelay<String?>(value: nil)
private(set) var rules: [Rule] = []
public init(rules: [Rule], textDriver: Driver<String?>) {
self.rules = rules
bindText(textDriver: textDriver)
}
private func bindText(textDriver: Driver<String?>) {
textDriver
.drive(text)
.disposed(by: disposeBag)
textDriver.asObservable()
.withLatestFrom(validationStateHolder.asObservable()) { (text: $0, validationState: $1) }
.filter { !$0.validationState.isInitial }
.map { $0.text }
.subscribe(onNext: { [weak self] text in
self?.validate(text: text)
})
.disposed(by: disposeBag)
}
public func manualValidate() -> Bool {
validate(text: text.value, isManual: true)
return validationStateHolder.value.isValid
}
private func validate(text: String?, isManual: Bool = false) {
let error = rules.filter {
!$0.validate(text ?? "")
}
.map { rule -> ValidationError in
ValidationError(failedRule: rule, errorMessage: rule.errorMessage())
}
.first
if let validationError = error {
switch validationStateHolder.value {
case .error where !isManual,
.correction where !isManual,
.valid where !isManual:
validationStateHolder.accept(.correction(validationError))
default:
validationStateHolder.accept(.error(validationError))
}
} else {
validationStateHolder.accept(.valid)
}
}
}

View File

@ -8,57 +8,60 @@ private enum ValidationServiceStateReactType {
case each case each
} }
enum ValidationServiceState { public enum ValidationServiceState {
case initial case initial
case valid case valid
case invalid case invalid
} }
extension ValidationServiceState { public extension ValidationServiceState {
var isValid: Bool { var isValid: Bool {
return self == .valid self == .valid
} }
} }
class ValidationService { public final class ValidationService {
private var disposeBag = DisposeBag() private var disposeBag = DisposeBag()
private(set) var validationItems: [ValidationItem] = [] private(set) var validationItems: [ValidationItem] = []
private let stateHolder = Variable<ValidationServiceState>(.initial) private let stateHolder = BehaviorRelay<ValidationServiceState>(value: .initial)
var state: ValidationServiceState { public var state: ValidationServiceState {
return stateHolder.value stateHolder.value
} }
var stateObservable: Observable<ValidationServiceState> { public var stateObservable: Observable<ValidationServiceState> {
return stateHolder.asObservable() stateHolder.asObservable()
} }
private var validationStateReactType: ValidationServiceStateReactType = .none private var validationStateReactType: ValidationServiceStateReactType = .none
func register(item: ValidationItem) { public init() {
// just to be accessible
}
public func register(item: ValidationItem) {
register(items: [item]) register(items: [item])
} }
func register(items: [ValidationItem]) { public func register(items: [ValidationItem]) {
validationItems += items validationItems += items
bindItems() bindItems()
} }
func unregisterAll() { public func unregisterAll() {
validationItems.removeAll() validationItems.removeAll()
bindItems() bindItems()
} }
func unregister(item: ValidationItem) { public func unregister(item: ValidationItem) {
unregister(items: [item]) unregister(items: [item])
} }
func unregister(items: [ValidationItem]) { public func unregister(items: [ValidationItem]) {
items.forEach { item in items.forEach { item in
if let removeIndex = validationItems.index(where: { $0 === item }) { if let removeIndex = validationItems.firstIndex(where: { $0 === item }) {
validationItems.remove(at: removeIndex) validationItems.remove(at: removeIndex)
} }
} }
@ -66,10 +69,9 @@ class ValidationService {
bindItems() bindItems()
} }
@discardableResult public func validate() -> Bool {
func validate() -> Bool {
validationStateReactType = .all validationStateReactType = .all
let isValid = validationItems.map { $0.manualValidate() }.reduce(true) { $0 && $1 } let isValid = validationItems.allSatisfy { $0.manualValidate() }
validationStateReactType = .each validationStateReactType = .each
return isValid return isValid
@ -82,23 +84,26 @@ class ValidationService {
let zipStates = Observable let zipStates = Observable
.zip(allValidationStateObservables) { $0 } .zip(allValidationStateObservables) { $0 }
.filter { [weak self] _ in self?.validationStateReactType == .all } .filter { [weak self] _ in
self?.validationStateReactType == .all
}
let combineLatestStates = Observable let combineLatestStates = Observable
.combineLatest(allValidationStateObservables) { $0 } .combineLatest(allValidationStateObservables) { $0 }
.filter { [weak self] _ in self?.validationStateReactType == .each } .filter { [weak self] _ in
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
return states.map { $0.isValid }.reduce(true) { $0 && $1 } states.allSatisfy { $0.isValid }
} }
.map { $0 ? ValidationServiceState.valid : .invalid } .map { $0 ? ValidationServiceState.valid : .invalid }
.bind(to: stateHolder) .bind(to: stateHolder)
.disposed(by: disposeBag) .disposed(by: disposeBag)
} }
} }
} }

1
build-scripts Submodule

@ -0,0 +1 @@
Subproject commit 2799169e2103c034b0e1be221860a23292766f36

@ -1 +0,0 @@
Subproject commit 817e845592f02fb0d22bf52ff9bb8db3210ddd89

View File

@ -0,0 +1,4 @@
import LeadKit
import LeadKitAdditions
import PlaygroundSupport

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='ios' executeOnSourceChanges='false'>
<timeline fileName='timeline.xctimeline'/>
</playground>

14
run_swiftlint.sh Normal file
View File

@ -0,0 +1,14 @@
#!/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}