Compare commits
1 Commits
master
...
build/inka
| Author | SHA1 | Date |
|---|---|---|
|
|
019488bc64 |
|
|
@ -63,6 +63,3 @@ fastlane/report.xml
|
||||||
fastlane/Preview.html
|
fastlane/Preview.html
|
||||||
fastlane/screenshots
|
fastlane/screenshots
|
||||||
fastlane/test_output
|
fastlane/test_output
|
||||||
|
|
||||||
# Touch Instinct custom
|
|
||||||
Downloads/
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "build-scripts"]
|
|
||||||
path = build-scripts
|
|
||||||
url = git@github.com:TouchInstinct/BuildScripts.git
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
5.0
|
|
||||||
144
CHANGELOG.md
144
CHANGELOG.md
|
|
@ -1,144 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
### 0.4.0
|
|
||||||
|
|
||||||
- **Update**: update libraries to build with Xcode 13.1
|
|
||||||
|
|
||||||
### 0.3.15
|
|
||||||
- **Update**: update LeadKit dependency
|
|
||||||
|
|
||||||
### 0.3.14
|
|
||||||
- **Update**: remove Carthage binary dependencies, update build scripts.
|
|
||||||
|
|
||||||
### 0.3.13
|
|
||||||
- **[Breaking change]**: `LabelTableViewCell` moved to `LeadKit`.
|
|
||||||
- **[Breaking change]**: Removed `PinLayout` dependency.
|
|
||||||
|
|
||||||
### 0.3.12
|
|
||||||
- **Update**: Swift 5 support
|
|
||||||
|
|
||||||
### 0.3.11
|
|
||||||
- **Add**: Carthage support.
|
|
||||||
- **[Breaking change]**: Remove `BaseDateFormatter`. Use `NumberFormattingService` from LeadKit instead.
|
|
||||||
|
|
||||||
### 0.3.10
|
|
||||||
|
|
||||||
- **[Breaking change]**: Remove `isTouchIdSupported` and `isFaceIdSupported`.
|
|
||||||
- **Add** BiometryType for BiometricsService: `touchID`, `faceID`, `none`.
|
|
||||||
|
|
||||||
### 0.3.9
|
|
||||||
|
|
||||||
- **Add**: `allowableReuseDuration` variable to `BiometricsService` which is a wrapper for touchIDAuthenticationAllowableReuseDuration.
|
|
||||||
- **Add**: individual `isTouchIdSupported` variable to `BiometricsService` to indicate if TouchId can be used.
|
|
||||||
- **Add**: `clear` function to `BiometricsService` so the context can be refreshed.
|
|
||||||
- **Update**: Documentations.
|
|
||||||
|
|
||||||
### 0.3.8
|
|
||||||
|
|
||||||
- **Fixed**: `SwiftValidator` fork moved to TouchInstinct repo, the used one is removed. Version number is downgraded to `4.0.2` to avoid collision when the original pod will update.
|
|
||||||
- **Fixed**: Extensions target building. Removed all PassCode logic, SeparatorTableViewCell and PinLayoutTableViewCell from extensions target.
|
|
||||||
|
|
||||||
### 0.3.7
|
|
||||||
|
|
||||||
- **Fixed**: `PassCodeError.tooManyAttempts` logic in `.create` and `.change` `PassCodeOperationType`
|
|
||||||
|
|
||||||
### 0.3.6
|
|
||||||
|
|
||||||
- **Update**: PassCodeError, now emmit `tooManyAttempts` in any operation (*create* / *change* / *enter*) type.
|
|
||||||
- **Update**: Rename `PassCodeControllerType` to `PassCodeOperationType`.
|
|
||||||
- **Update**: `showBiometricsRequestIfNeeded` method become public.
|
|
||||||
|
|
||||||
### 0.3.5
|
|
||||||
|
|
||||||
- **Update**: Migrate to Swift 4.2 & Xcode 10. Update dependencies.
|
|
||||||
|
|
||||||
### 0.3.4
|
|
||||||
|
|
||||||
- **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**: `retry(retryLimit:canRetryClosure:)` to `Observable` extension.
|
|
||||||
- **Removed**: `retryWithinErrors` method from `Observable` extension.
|
|
||||||
- **Update**: LeadKit to `0.6.x` version
|
|
||||||
- **Removed**: `ConnectionError`. (Replaced by `LeadKit.RequestError`)
|
|
||||||
- **Removed**: `handleConnectionErrors` from Observable+Extensions
|
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0
|
|
||||||
|
|
||||||
- **Add**: support for Swift 3.2 / 4
|
|
||||||
5
Cartfile
5
Cartfile
|
|
@ -1,5 +0,0 @@
|
||||||
github "krzyzanowskim/CryptoSwift"
|
|
||||||
github "TouchInstinct/LeadKit"
|
|
||||||
github "petropavel13/SwiftValidator"
|
|
||||||
github "kishikawakatsumi/KeychainAccess"
|
|
||||||
github "layoutBox/PinLayout"
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
github "Alamofire/Alamofire" "4.9.1"
|
|
||||||
github "ReactiveX/RxSwift" "4.5.0"
|
|
||||||
github "RxSwiftCommunity/RxAlamofire" "4.5.0"
|
|
||||||
github "SnapKit/SnapKit" "4.2.0"
|
|
||||||
github "TouchInstinct/LeadKit" "0.9.29"
|
|
||||||
github "TouchInstinct/TableKit" "2.10008.1"
|
|
||||||
github "krzyzanowskim/CryptoSwift" "1.3.0"
|
|
||||||
github "malcommac/SwiftDate" "6.1.0"
|
|
||||||
github "petropavel13/SwiftValidator" "be856202cd70e6549ebc8c9d07ec47bbff784b07"
|
|
||||||
github "pronebird/UIScrollView-InfiniteScroll" "1.1.0"
|
|
||||||
github "kishikawakatsumi/KeychainAccess" "v3.2.0"
|
|
||||||
github "layoutBox/PinLayout" "1.8.6"
|
|
||||||
201
LICENSE
201
LICENSE
|
|
@ -1,201 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
@ -1,23 +1,15 @@
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = "LeadKitAdditions"
|
s.name = "LeadKitAdditions"
|
||||||
s.version = "0.4.0"
|
s.version = "0.0.14"
|
||||||
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/NikAshanin/LeadKitAdditions"
|
||||||
s.license = "Apache License, Version 2.0"
|
s.license = "Apache License, Version 2.0"
|
||||||
s.author = "Touch Instinct"
|
s.author = "Touch Instinct"
|
||||||
s.platform = :ios, "10.0"
|
s.platform = :ios, "9.0"
|
||||||
s.source = { :git => "https://github.com/TouchInstinct/LeadKitAdditions.git", :tag => s.version }
|
s.source = { :git => "https://github.com/NikAshanin/LeadKitAdditions.git", :tag => s.version }
|
||||||
|
s.source_files = "LeadKitAdditions/LeadKitAdditions/**/*.swift"
|
||||||
s.subspec 'Core' do |ss|
|
|
||||||
ss.ios.deployment_target = '10.0'
|
|
||||||
ss.source_files = "Sources/**/*.swift"
|
|
||||||
|
|
||||||
ss.dependency "LeadKit", '~> 1.7.0'
|
|
||||||
ss.dependency "KeychainAccess", '~> 4.2.0'
|
|
||||||
ss.dependency "CryptoSwift", '~> 1.4.0'
|
|
||||||
ss.dependency "SwiftValidator", '4.0.2'
|
|
||||||
end
|
|
||||||
|
|
||||||
s.default_subspec = 'Core'
|
|
||||||
|
|
||||||
|
s.dependency "LeadKit", '~> 0.4.6'
|
||||||
|
s.dependency "KeychainAccess", '3.0.2'
|
||||||
|
s.dependency "IDZSwiftCommonCrypto", '0.9.1'
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,637 +0,0 @@
|
||||||
// !$*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 */;
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1020"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "CAE698E21E968820000394B0"
|
|
||||||
BuildableName = "LeadKitAdditions.framework"
|
|
||||||
BlueprintName = "LeadKitAdditions iOS"
|
|
||||||
ReferencedContainer = "container:LeadKitAdditions.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "CAE698E21E968820000394B0"
|
|
||||||
BuildableName = "LeadKitAdditions.framework"
|
|
||||||
BlueprintName = "LeadKitAdditions iOS"
|
|
||||||
ReferencedContainer = "container:LeadKitAdditions.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "CAE698E21E968820000394B0"
|
|
||||||
BuildableName = "LeadKitAdditions.framework"
|
|
||||||
BlueprintName = "LeadKitAdditions iOS"
|
|
||||||
ReferencedContainer = "container:LeadKitAdditions.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?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>
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
disabled_rules:
|
||||||
|
- variable_name
|
||||||
|
excluded:
|
||||||
|
- Carthage
|
||||||
|
- Pods
|
||||||
|
line_length: 128
|
||||||
|
type_body_length:
|
||||||
|
- 500 # warning
|
||||||
|
- 700 # error
|
||||||
|
file_length:
|
||||||
|
warning: 500
|
||||||
|
error: 1200
|
||||||
|
warning_threshold: 1
|
||||||
|
|
||||||
|
custom_rules:
|
||||||
|
uiwebview_disabled:
|
||||||
|
included: ".*.swift"
|
||||||
|
name: "UIWebView Usage Disabled"
|
||||||
|
regex: "(UIWebView)"
|
||||||
|
message: "Do not use UIWebView. Use WKWebView Instead. https://developer.apple.com/reference/uikit/uiwebview"
|
||||||
|
severity: error
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
exclude:
|
||||||
|
- 'Pods'
|
||||||
|
- 'Carthage'
|
||||||
|
- 'RxAlamofire'
|
||||||
|
|
@ -0,0 +1,568 @@
|
||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
7A94B4A5956B82BE1CEBA873 /* Pods_LeadKitAdditions.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B7F57C5E5275C4D8DC71992 /* Pods_LeadKitAdditions.framework */; };
|
||||||
|
CAE698E81E968820000394B0 /* LeadKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = CAE698E61E968820000394B0 /* LeadKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
|
EF05EDB41EAF703A00CAE7B6 /* BaseUserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDB31EAF703A00CAE7B6 /* BaseUserService.swift */; };
|
||||||
|
EF05EDB71EAF704800CAE7B6 /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDB51EAF704800CAE7B6 /* Observable+Extensions.swift */; };
|
||||||
|
EF05EDB81EAF704800CAE7B6 /* UserDefaults+UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDB61EAF704800CAE7B6 /* UserDefaults+UserService.swift */; };
|
||||||
|
EF05EDBB1EAF705500CAE7B6 /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDB91EAF705500CAE7B6 /* ApiError.swift */; };
|
||||||
|
EF05EDBC1EAF705500CAE7B6 /* ConnectionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBA1EAF705500CAE7B6 /* ConnectionError.swift */; };
|
||||||
|
EF05EDC01EAF706200CAE7B6 /* ApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBD1EAF706200CAE7B6 /* ApiResponse.swift */; };
|
||||||
|
EF05EDC11EAF706200CAE7B6 /* BaseDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */; };
|
||||||
|
EF05EDC61EAF70EB00CAE7B6 /* TouchIDService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */; };
|
||||||
|
EF05EDC81EAF91D500CAE7B6 /* BasePassCodeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */; };
|
||||||
|
EF05EDE11EAFA74200CAE7B6 /* BasePassCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDE01EAFA74200CAE7B6 /* BasePassCodeViewController.swift */; };
|
||||||
|
EF05EDE31EAFA7A600CAE7B6 /* BasePassCodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDE21EAFA7A600CAE7B6 /* BasePassCodeViewModel.swift */; };
|
||||||
|
EF05EDE51EAFA80D00CAE7B6 /* PassCodeConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDE41EAFA80D00CAE7B6 /* PassCodeConfiguration.swift */; };
|
||||||
|
EF05EDE71EAFA87300CAE7B6 /* PassCodeValidationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDE61EAFA87300CAE7B6 /* PassCodeValidationResult.swift */; };
|
||||||
|
EF05EDE91EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDE81EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift */; };
|
||||||
|
EF05EDEB1EAFA8E600CAE7B6 /* PassCodeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDEA1EAFA8E600CAE7B6 /* PassCodeError.swift */; };
|
||||||
|
EF05EDED1EAFA96D00CAE7B6 /* PassCodeHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDEC1EAFA96D00CAE7B6 /* PassCodeHolder.swift */; };
|
||||||
|
EF05EDF81EB0D5A600CAE7B6 /* UIBarButtonItem+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDF71EB0D5A600CAE7B6 /* UIBarButtonItem+Extensions.swift */; };
|
||||||
|
EF05EDFC1EB0D77400CAE7B6 /* DefaultNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDFA1EB0D77400CAE7B6 /* DefaultNetworkService.swift */; };
|
||||||
|
EF05EDFD1EB0D77400CAE7B6 /* ApiNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDFB1EB0D77400CAE7B6 /* ApiNetworkService.swift */; };
|
||||||
|
EF05EE021EB206C000CAE7B6 /* LoadingBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EE011EB206C000CAE7B6 /* LoadingBarButton.swift */; };
|
||||||
|
EF05EE041EB21A2D00CAE7B6 /* ApiErrorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EE031EB21A2D00CAE7B6 /* ApiErrorProtocol.swift */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
53E343F9E8F0A27C6114C160 /* Pods-LeadKitAdditions.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKitAdditions.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LeadKitAdditions/Pods-LeadKitAdditions.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
7B7F57C5E5275C4D8DC71992 /* Pods_LeadKitAdditions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKitAdditions.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
8A48492D1BE72C6D161A6E8E /* Pods-LeadKitAdditions.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKitAdditions.release.xcconfig"; path = "Pods/Target Support Files/Pods-LeadKitAdditions/Pods-LeadKitAdditions.release.xcconfig"; 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>"; };
|
||||||
|
CAE698E71E968820000394B0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
EF05EDB31EAF703A00CAE7B6 /* BaseUserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseUserService.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDB51EAF704800CAE7B6 /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
EF05EDB61EAF704800CAE7B6 /* UserDefaults+UserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+UserService.swift"; sourceTree = "<group>"; };
|
||||||
|
EF05EDB91EAF705500CAE7B6 /* ApiError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiError.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDBA1EAF705500CAE7B6 /* ConnectionError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionError.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDBD1EAF706200CAE7B6 /* ApiResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiResponse.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDateFormatter.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchIDService.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeService.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDE01EAFA74200CAE7B6 /* BasePassCodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeViewController.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDE21EAFA7A600CAE7B6 /* BasePassCodeViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDE41EAFA80D00CAE7B6 /* PassCodeConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeConfiguration.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDE61EAFA87300CAE7B6 /* PassCodeValidationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeValidationResult.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDE81EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolderProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDEA1EAFA8E600CAE7B6 /* PassCodeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeError.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDEC1EAFA96D00CAE7B6 /* PassCodeHolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolder.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDF71EB0D5A600CAE7B6 /* UIBarButtonItem+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
EF05EDFA1EB0D77400CAE7B6 /* DefaultNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkService.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EDFB1EB0D77400CAE7B6 /* ApiNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiNetworkService.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EE011EB206C000CAE7B6 /* LoadingBarButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingBarButton.swift; sourceTree = "<group>"; };
|
||||||
|
EF05EE031EB21A2D00CAE7B6 /* ApiErrorProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiErrorProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
CAE698DF1E968820000394B0 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
7A94B4A5956B82BE1CEBA873 /* Pods_LeadKitAdditions.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
A3117951840B8B7D2E7A8A80 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
7B7F57C5E5275C4D8DC71992 /* Pods_LeadKitAdditions.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
CAE698D91E968820000394B0 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CAE698E51E968820000394B0 /* LeadKitAdditions */,
|
||||||
|
CAE698E41E968820000394B0 /* Products */,
|
||||||
|
F8A65FEC7C0EB4B93746E50F /* Pods */,
|
||||||
|
A3117951840B8B7D2E7A8A80 /* Frameworks */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
CAE698E41E968820000394B0 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CAE698E31E968820000394B0 /* LeadKitAdditions.framework */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
CAE698E51E968820000394B0 /* LeadKitAdditions */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDDB1EAFA6FA00CAE7B6 /* Controllers */,
|
||||||
|
CAE698EE1E968B72000394B0 /* Classes */,
|
||||||
|
CAE699011E9693DE000394B0 /* Enums */,
|
||||||
|
CAE698F81E968F56000394B0 /* Extensions */,
|
||||||
|
CAE698E71E968820000394B0 /* Info.plist */,
|
||||||
|
CAE698E61E968820000394B0 /* LeadKitAdditions.h */,
|
||||||
|
CAE698F31E968E28000394B0 /* Services */,
|
||||||
|
);
|
||||||
|
path = LeadKitAdditions;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
CAE698EE1E968B72000394B0 /* Classes */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDBD1EAF706200CAE7B6 /* ApiResponse.swift */,
|
||||||
|
EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */,
|
||||||
|
EF05EE011EB206C000CAE7B6 /* LoadingBarButton.swift */,
|
||||||
|
);
|
||||||
|
path = Classes;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
CAE698F31E968E28000394B0 /* Services */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDF91EB0D75A00CAE7B6 /* Network */,
|
||||||
|
EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */,
|
||||||
|
EF05EDB31EAF703A00CAE7B6 /* BaseUserService.swift */,
|
||||||
|
EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */,
|
||||||
|
);
|
||||||
|
path = Services;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
CAE698F81E968F56000394B0 /* Extensions */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDB51EAF704800CAE7B6 /* Observable+Extensions.swift */,
|
||||||
|
EF05EDF71EB0D5A600CAE7B6 /* UIBarButtonItem+Extensions.swift */,
|
||||||
|
EF05EDB61EAF704800CAE7B6 /* UserDefaults+UserService.swift */,
|
||||||
|
);
|
||||||
|
path = Extensions;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
CAE699011E9693DE000394B0 /* Enums */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDB91EAF705500CAE7B6 /* ApiError.swift */,
|
||||||
|
EF05EE031EB21A2D00CAE7B6 /* ApiErrorProtocol.swift */,
|
||||||
|
EF05EDBA1EAF705500CAE7B6 /* ConnectionError.swift */,
|
||||||
|
);
|
||||||
|
path = Enums;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EF05EDDB1EAFA6FA00CAE7B6 /* Controllers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDDC1EAFA72600CAE7B6 /* PassCode */,
|
||||||
|
);
|
||||||
|
path = Controllers;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EF05EDDC1EAFA72600CAE7B6 /* PassCode */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDDD1EAFA72600CAE7B6 /* Model */,
|
||||||
|
EF05EDDE1EAFA72600CAE7B6 /* View */,
|
||||||
|
EF05EDDF1EAFA72600CAE7B6 /* ViewModel */,
|
||||||
|
);
|
||||||
|
path = PassCode;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EF05EDDD1EAFA72600CAE7B6 /* Model */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDE41EAFA80D00CAE7B6 /* PassCodeConfiguration.swift */,
|
||||||
|
EF05EDEA1EAFA8E600CAE7B6 /* PassCodeError.swift */,
|
||||||
|
EF05EDEC1EAFA96D00CAE7B6 /* PassCodeHolder.swift */,
|
||||||
|
EF05EDE81EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift */,
|
||||||
|
EF05EDE61EAFA87300CAE7B6 /* PassCodeValidationResult.swift */,
|
||||||
|
);
|
||||||
|
path = Model;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EF05EDDE1EAFA72600CAE7B6 /* View */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDE01EAFA74200CAE7B6 /* BasePassCodeViewController.swift */,
|
||||||
|
);
|
||||||
|
path = View;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EF05EDDF1EAFA72600CAE7B6 /* ViewModel */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDE21EAFA7A600CAE7B6 /* BasePassCodeViewModel.swift */,
|
||||||
|
);
|
||||||
|
path = ViewModel;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EF05EDF91EB0D75A00CAE7B6 /* Network */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EF05EDFB1EB0D77400CAE7B6 /* ApiNetworkService.swift */,
|
||||||
|
EF05EDFA1EB0D77400CAE7B6 /* DefaultNetworkService.swift */,
|
||||||
|
);
|
||||||
|
path = Network;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
F8A65FEC7C0EB4B93746E50F /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
53E343F9E8F0A27C6114C160 /* Pods-LeadKitAdditions.debug.xcconfig */,
|
||||||
|
8A48492D1BE72C6D161A6E8E /* Pods-LeadKitAdditions.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;
|
||||||
|
};
|
||||||
|
/* End PBXHeadersBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
CAE698E21E968820000394B0 /* LeadKitAdditions */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = CAE698EB1E968820000394B0 /* Build configuration list for PBXNativeTarget "LeadKitAdditions" */;
|
||||||
|
buildPhases = (
|
||||||
|
E8E82E34792B38EF225575D7 /* [CP] Check Pods Manifest.lock */,
|
||||||
|
CAE698DE1E968820000394B0 /* Sources */,
|
||||||
|
CAE698DF1E968820000394B0 /* Frameworks */,
|
||||||
|
CAE698E01E968820000394B0 /* Headers */,
|
||||||
|
CAE698E11E968820000394B0 /* Resources */,
|
||||||
|
94F6E1BA5AD68C6E2F10062B /* [CP] Copy Pods Resources */,
|
||||||
|
CAE6990A1E969A7A000394B0 /* Swiftlint */,
|
||||||
|
CAE6990B1E969A8D000394B0 /* Tailor */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = LeadKitAdditions;
|
||||||
|
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 = 0830;
|
||||||
|
ORGANIZATIONNAME = TouchInstinct;
|
||||||
|
TargetAttributes = {
|
||||||
|
CAE698E21E968820000394B0 = {
|
||||||
|
CreatedOnToolsVersion = 8.3;
|
||||||
|
LastSwiftMigration = 0830;
|
||||||
|
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 */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
CAE698E11E968820000394B0 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
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/Pods-LeadKitAdditions-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";
|
||||||
|
};
|
||||||
|
CAE6990B1E969A8D000394B0 /* Tailor */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = Tailor;
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "if hash tailor 2>/dev/null; then\n tailor\nelse\n echo \"warning: Please install Tailor from https://tailor.sh\"\nfi";
|
||||||
|
};
|
||||||
|
E8E82E34792B38EF225575D7 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
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";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
CAE698DE1E968820000394B0 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
EF05EE041EB21A2D00CAE7B6 /* ApiErrorProtocol.swift in Sources */,
|
||||||
|
EF05EDB81EAF704800CAE7B6 /* UserDefaults+UserService.swift in Sources */,
|
||||||
|
EF05EDE11EAFA74200CAE7B6 /* BasePassCodeViewController.swift in Sources */,
|
||||||
|
EF05EDC61EAF70EB00CAE7B6 /* TouchIDService.swift in Sources */,
|
||||||
|
EF05EDE31EAFA7A600CAE7B6 /* BasePassCodeViewModel.swift in Sources */,
|
||||||
|
EF05EDBB1EAF705500CAE7B6 /* ApiError.swift in Sources */,
|
||||||
|
EF05EDE91EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift in Sources */,
|
||||||
|
EF05EDFD1EB0D77400CAE7B6 /* ApiNetworkService.swift in Sources */,
|
||||||
|
EF05EDF81EB0D5A600CAE7B6 /* UIBarButtonItem+Extensions.swift in Sources */,
|
||||||
|
EF05EDED1EAFA96D00CAE7B6 /* PassCodeHolder.swift in Sources */,
|
||||||
|
EF05EDB71EAF704800CAE7B6 /* Observable+Extensions.swift in Sources */,
|
||||||
|
EF05EDC01EAF706200CAE7B6 /* ApiResponse.swift in Sources */,
|
||||||
|
EF05EDBC1EAF705500CAE7B6 /* ConnectionError.swift in Sources */,
|
||||||
|
EF05EE021EB206C000CAE7B6 /* LoadingBarButton.swift in Sources */,
|
||||||
|
EF05EDEB1EAFA8E600CAE7B6 /* PassCodeError.swift in Sources */,
|
||||||
|
EF05EDB41EAF703A00CAE7B6 /* BaseUserService.swift in Sources */,
|
||||||
|
EF05EDE51EAFA80D00CAE7B6 /* PassCodeConfiguration.swift in Sources */,
|
||||||
|
EF05EDFC1EB0D77400CAE7B6 /* DefaultNetworkService.swift in Sources */,
|
||||||
|
EF05EDC81EAF91D500CAE7B6 /* BasePassCodeService.swift in Sources */,
|
||||||
|
EF05EDC11EAF706200CAE7B6 /* BaseDateFormatter.swift in Sources */,
|
||||||
|
EF05EDE71EAFA87300CAE7B6 /* PassCodeValidationResult.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_BOOL_CONVERSION = 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_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
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 = 10.3;
|
||||||
|
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_BOOL_CONVERSION = 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_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
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 = 10.3;
|
||||||
|
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 = 53E343F9E8F0A27C6114C160 /* Pods-LeadKitAdditions.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 = LeadKitAdditions/Info.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;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 3.0;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
CAE698ED1E968820000394B0 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 8A48492D1BE72C6D161A6E8E /* Pods-LeadKitAdditions.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 = LeadKitAdditions/Info.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;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 3.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" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
CAE698EC1E968820000394B0 /* Debug */,
|
||||||
|
CAE698ED1E968820000394B0 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = CAE698DA1E968820000394B0 /* Project object */;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,19 +20,25 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
import RxSwift
|
import ObjectMapper
|
||||||
|
|
||||||
public extension BaseTextFieldViewEvents {
|
public class ApiResponse: ApiResponseProtocol, ImmutableMappable {
|
||||||
|
|
||||||
/// Convenience init with UITextField as textChangedDriver.
|
public let result: Any?
|
||||||
///
|
public let errorCode: Int
|
||||||
/// - Parameter textField: UITextField to use for text events.
|
public let errorMessage: String?
|
||||||
convenience init(textField: UITextField) {
|
|
||||||
let textChangedDriver = textField.rx
|
|
||||||
.text
|
|
||||||
.asDriver()
|
|
||||||
.distinctUntilChanged { $0 == $1 }
|
|
||||||
|
|
||||||
self.init(textChangedDriver: textChangedDriver)
|
public required init(map: Map) throws {
|
||||||
|
result = try? map.value("result")
|
||||||
|
errorCode = try map.value("error_code")
|
||||||
|
errorMessage = try? map.value("error_message")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol ApiResponseProtocol: ImmutableMappable {
|
||||||
|
|
||||||
|
var errorCode: Int { get }
|
||||||
|
var errorMessage: String? { get }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
open class var usedLocale: Locale {
|
||||||
|
return .current
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func backendDate(fromStrDate strDate: String) -> Date? {
|
||||||
|
apiFormatter.locale = usedLocale
|
||||||
|
return apiFormatter.date(from: strDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func backendStrDate(withDate date: Date) -> String {
|
||||||
|
apiFormatter.locale = usedLocale
|
||||||
|
return apiFormatter.string(from: date)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func backendDateWithoutTime(withDate date: Date) -> String {
|
||||||
|
apiDateWithoutTimeFormatter.locale = usedLocale
|
||||||
|
return apiDateWithoutTimeFormatter.string(from: date)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func hourAndMinuteStrDate(withDate date: Date) -> String {
|
||||||
|
hourAndMinuteFormatter.locale = usedLocale
|
||||||
|
return hourAndMinuteFormatter.string(from: date)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func dayAndMonthStrDate(withDate date: Date) -> String {
|
||||||
|
hourAndMinuteFormatter.locale = usedLocale
|
||||||
|
return dayAndMonthFormatter.string(from: date)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func dayMonthYearStrDate(withDate date: Date) -> String {
|
||||||
|
hourAndMinuteFormatter.locale = usedLocale
|
||||||
|
return dayMonthYearFormatter.string(from: date)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Transformers
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,13 +24,11 @@ import UIKit
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
|
|
||||||
/// Side to which activity indicator applied
|
|
||||||
public enum LoadingBarButtonSide {
|
public enum LoadingBarButtonSide {
|
||||||
case left
|
case left
|
||||||
case right
|
case right
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Workaround with navigationBarButton, that can change state (UI) into activity indicator
|
|
||||||
public class LoadingBarButton {
|
public class LoadingBarButton {
|
||||||
|
|
||||||
fileprivate weak var navigationItem: UINavigationItem?
|
fileprivate weak var navigationItem: UINavigationItem?
|
||||||
|
|
@ -42,7 +40,6 @@ public class LoadingBarButton {
|
||||||
switch side {
|
switch side {
|
||||||
case .left:
|
case .left:
|
||||||
return navigationItem?.leftBarButtonItem
|
return navigationItem?.leftBarButtonItem
|
||||||
|
|
||||||
case .right:
|
case .right:
|
||||||
return navigationItem?.rightBarButtonItem
|
return navigationItem?.rightBarButtonItem
|
||||||
}
|
}
|
||||||
|
|
@ -51,20 +48,12 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Create an instance of LoadingBarButton
|
|
||||||
|
|
||||||
- Parameters:
|
|
||||||
- navigationItem: item to which apply changes
|
|
||||||
- side: side where navigationItem would be placed
|
|
||||||
*/
|
|
||||||
public init(navigationItem: UINavigationItem, side: LoadingBarButtonSide) {
|
public init(navigationItem: UINavigationItem, side: LoadingBarButtonSide) {
|
||||||
self.navigationItem = navigationItem
|
self.navigationItem = navigationItem
|
||||||
self.side = side
|
self.side = side
|
||||||
|
|
@ -73,32 +62,25 @@ public class LoadingBarButton {
|
||||||
|
|
||||||
fileprivate func setState(waiting: Bool = false) {
|
fileprivate func setState(waiting: Bool = false) {
|
||||||
if waiting {
|
if waiting {
|
||||||
let activityIndicatorItem = UIBarButtonItem.activityIndicator
|
let activityIndicatorItem = UIBarButtonItem.activityIndicator
|
||||||
barButtonItem = activityIndicatorItem.barButton
|
barButtonItem = activityIndicatorItem.barButton
|
||||||
activityIndicatorItem.activityIndicator.startAnimating()
|
activityIndicatorItem.activityIndicator.startAnimating()
|
||||||
} else {
|
} else {
|
||||||
barButtonItem = initialBarButton
|
barButtonItem = initialBarButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension Observable {
|
extension Observable {
|
||||||
|
|
||||||
/**
|
public func changeLoadingUI(using barButton: LoadingBarButton) -> Observable<Observable.E> {
|
||||||
Reactive extension for LoadingBarButton
|
return observeOn(MainScheduler.instance)
|
||||||
Apply transformations on subscribe and on dispose events
|
|
||||||
|
|
||||||
- Parameters:
|
|
||||||
- barButton: LoadingBarButton instance to which transformations would applied
|
|
||||||
- Returns:
|
|
||||||
- observable, that handles LoadingBarButton behaviour
|
|
||||||
*/
|
|
||||||
func changeLoadingUI(using barButton: LoadingBarButton) -> Observable<Observable.Element> {
|
|
||||||
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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,26 +20,25 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
/// Configuration container for BasePassCodeViewController
|
|
||||||
public struct PassCodeConfiguration {
|
public struct PassCodeConfiguration {
|
||||||
|
|
||||||
/// Pass code length
|
public var passCodeCharactersNumber: UInt = 4
|
||||||
public let passCodeLength: Int
|
public var maxAttemptsLoginNumber: UInt = 5
|
||||||
|
|
||||||
/// Incorrect pass code attempts count
|
public var shouldResetWhenGoBackground: Bool = true
|
||||||
public let maxAttemptsNumber: Int
|
|
||||||
|
|
||||||
/// Clear input progress when application goes to background
|
private init() {}
|
||||||
public let shouldResetWhenGoBackground: Bool
|
|
||||||
|
|
||||||
public init(passCodeLength: Int = 4, maxAttemptsNumber: Int = 5, shouldResetWhenGoBackground: Bool = true) {
|
init?(passCodeCharactersNumber: UInt) {
|
||||||
self.passCodeLength = passCodeLength
|
guard passCodeCharactersNumber > 0 else {
|
||||||
self.maxAttemptsNumber = maxAttemptsNumber
|
assertionFailure("passCodeCharactersNumber must be greater then 0")
|
||||||
self.shouldResetWhenGoBackground = shouldResetWhenGoBackground
|
return nil
|
||||||
|
}
|
||||||
|
self.passCodeCharactersNumber = passCodeCharactersNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns configuration with default values
|
|
||||||
public static var defaultConfiguration: PassCodeConfiguration {
|
public static var defaultConfiguration: PassCodeConfiguration {
|
||||||
PassCodeConfiguration()
|
return PassCodeConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,16 +20,8 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
/// Enum that describes current state of online validation.
|
public enum PassCodeError: Error {
|
||||||
///
|
case codesNotMatch
|
||||||
/// - initial: Nothing did happen.
|
case wrongCode
|
||||||
/// - processing: Processing validation.
|
case tooManyAttempts
|
||||||
/// - valid: Got a valid result.
|
|
||||||
/// - invalid: Got an invalid result.
|
|
||||||
public enum OnlineValidationState {
|
|
||||||
|
|
||||||
case initial
|
|
||||||
case processing
|
|
||||||
case valid
|
|
||||||
case invalid(error: Error?)
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,24 @@
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
|
|
||||||
public extension PassCodeHolderProtocol {
|
extension PassCodeHolderProtocol {
|
||||||
|
|
||||||
var passCodeHolderCreate: PassCodeHolderCreate? {
|
public var passCodeHolderCreate: PassCodeHolderCreate? {
|
||||||
self as? PassCodeHolderCreate
|
return self as? PassCodeHolderCreate
|
||||||
}
|
}
|
||||||
|
|
||||||
var passCodeHolderEnter: PassCodeHolderEnter? {
|
public var passCodeHolderEnter: PassCodeHolderEnter? {
|
||||||
self as? PassCodeHolderEnter
|
return self as? PassCodeHolderEnter
|
||||||
}
|
}
|
||||||
|
|
||||||
var passCodeHolderChange: PassCodeHolderChange? {
|
public var passCodeHolderChange: PassCodeHolderChange? {
|
||||||
self as? PassCodeHolderChange
|
return self as? PassCodeHolderChange
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds information about pass codes during pass code creation process
|
|
||||||
public class PassCodeHolderCreate: PassCodeHolderProtocol {
|
public class PassCodeHolderCreate: PassCodeHolderProtocol {
|
||||||
|
|
||||||
public let type: PassCodeOperationType = .create
|
public let type: PassCodeControllerType = .create
|
||||||
|
|
||||||
private var firstPassCode: String?
|
private var firstPassCode: String?
|
||||||
private var secondPassCode: String?
|
private var secondPassCode: String?
|
||||||
|
|
@ -55,7 +54,7 @@ public class PassCodeHolderCreate: PassCodeHolderProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
public var shouldValidate: Bool {
|
public var shouldValidate: Bool {
|
||||||
firstPassCode != nil && secondPassCode != nil
|
return firstPassCode != nil && secondPassCode != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public var passCode: String? {
|
public var passCode: String? {
|
||||||
|
|
@ -70,10 +69,8 @@ public class PassCodeHolderCreate: PassCodeHolderProtocol {
|
||||||
switch enterStep {
|
switch enterStep {
|
||||||
case .enter:
|
case .enter:
|
||||||
firstPassCode = passCode
|
firstPassCode = passCode
|
||||||
|
|
||||||
case .repeatEnter:
|
case .repeatEnter:
|
||||||
secondPassCode = passCode
|
secondPassCode = passCode
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -83,24 +80,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
|
|
||||||
public class PassCodeHolderEnter: PassCodeHolderProtocol {
|
public class PassCodeHolderEnter: PassCodeHolderProtocol {
|
||||||
|
|
||||||
public let type: PassCodeOperationType = .enter
|
public let type: PassCodeControllerType = .enter
|
||||||
public let enterStep: PassCodeControllerState = .enter
|
public let enterStep: PassCodeControllerState = .enter
|
||||||
|
|
||||||
public var shouldValidate: Bool {
|
public var shouldValidate: Bool {
|
||||||
passCode != nil
|
return passCode != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public var passCode: String?
|
public var passCode: String?
|
||||||
|
|
@ -113,19 +110,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
|
|
||||||
public class PassCodeHolderChange: PassCodeHolderProtocol {
|
public class PassCodeHolderChange: PassCodeHolderProtocol {
|
||||||
|
|
||||||
public let type: PassCodeOperationType = .change
|
public let type: PassCodeControllerType = .change
|
||||||
|
|
||||||
private var oldPassCode: String?
|
private var oldPassCode: String?
|
||||||
private var newFirstPassCode: String?
|
private var newFirstPassCode: String?
|
||||||
|
|
@ -153,15 +150,12 @@ public class PassCodeHolderChange: PassCodeHolderProtocol {
|
||||||
|
|
||||||
public var passCode: String? {
|
public var passCode: String? {
|
||||||
switch (oldPassCode, newFirstPassCode, newSecondPassCode) {
|
switch (oldPassCode, newFirstPassCode, newSecondPassCode) {
|
||||||
case let (oldPassCode?, nil, nil):
|
case (let oldPassCode?, nil, nil):
|
||||||
return oldPassCode
|
return oldPassCode
|
||||||
|
|
||||||
case (_, _?, nil):
|
case (_, _?, nil):
|
||||||
return nil
|
return nil
|
||||||
|
case (_, let newFirstPassCode?, let newSecondPassCode?) where newFirstPassCode == newSecondPassCode:
|
||||||
case let (_, newFirstPassCode?, newSecondPassCode?) where newFirstPassCode == newSecondPassCode:
|
|
||||||
return newFirstPassCode
|
return newFirstPassCode
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +175,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,4 +187,5 @@ public class PassCodeHolderChange: PassCodeHolderProtocol {
|
||||||
newSecondPassCode = nil
|
newSecondPassCode = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,49 +20,34 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
/// Holds information about enter type (create, change, etc), step
|
|
||||||
/// Also describes interface to manipulate with entered pass code
|
|
||||||
public protocol PassCodeHolderProtocol {
|
public protocol PassCodeHolderProtocol {
|
||||||
|
|
||||||
/// Type of operation with pass code
|
var type: PassCodeControllerType { get }
|
||||||
var type: PassCodeOperationType { get }
|
|
||||||
/// Operation step
|
|
||||||
var enterStep: PassCodeControllerState { get }
|
var enterStep: PassCodeControllerState { get }
|
||||||
|
|
||||||
/// Add pass code for current step
|
|
||||||
func add(passCode: String)
|
func add(passCode: String)
|
||||||
/// Reset all progress
|
|
||||||
func reset()
|
func reset()
|
||||||
|
|
||||||
/// Should been pass code validated
|
|
||||||
var shouldValidate: Bool { get }
|
var shouldValidate: Bool { get }
|
||||||
/// Current pass code
|
|
||||||
var passCode: String? { get }
|
var passCode: String? { get }
|
||||||
|
|
||||||
/// Returns passCode or error if pass code is invalid
|
|
||||||
func validate() -> PassCodeValidationResult
|
func validate() -> PassCodeValidationResult
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PassCodeHolderBuilder {
|
public class PassCodeHolderBuilder {
|
||||||
|
|
||||||
private init() {}
|
private init() {}
|
||||||
|
|
||||||
/**
|
public static func build(with type: PassCodeControllerType) -> PassCodeHolderProtocol {
|
||||||
Creates holder by type (create, change, etc)
|
|
||||||
|
|
||||||
- parameter type: type of pass code controller
|
|
||||||
- returns: pass code information holder, specific by type
|
|
||||||
*/
|
|
||||||
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,41 +20,36 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
/// Result of pass code validation
|
|
||||||
public enum PassCodeValidationResult {
|
public enum PassCodeValidationResult {
|
||||||
|
|
||||||
case valid(String)
|
case valid(String)
|
||||||
case invalid(PassCodeError?)
|
case inValid(PassCodeError?)
|
||||||
}
|
|
||||||
|
|
||||||
public extension PassCodeValidationResult {
|
public var isValid: Bool {
|
||||||
var isValid: Bool {
|
|
||||||
switch self {
|
switch self {
|
||||||
case .valid:
|
case .valid:
|
||||||
return true
|
return true
|
||||||
|
default:
|
||||||
case .invalid:
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var passCode: String? {
|
public 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var error: PassCodeError? {
|
public 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,20 +25,17 @@ import RxSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
import LeadKit
|
import LeadKit
|
||||||
|
|
||||||
/// Describes pin image
|
|
||||||
public enum PinImageType {
|
public enum PinImageType {
|
||||||
case entered
|
case entered
|
||||||
case clear
|
case clear
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pass code operation type
|
public enum PassCodeControllerType {
|
||||||
public enum PassCodeOperationType {
|
|
||||||
case create
|
case create
|
||||||
case enter
|
case enter
|
||||||
case change
|
case change
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pass code operation state
|
|
||||||
public enum PassCodeControllerState {
|
public enum PassCodeControllerState {
|
||||||
case enter
|
case enter
|
||||||
case repeatEnter
|
case repeatEnter
|
||||||
|
|
@ -46,21 +43,19 @@ public enum PassCodeControllerState {
|
||||||
case newEnter
|
case newEnter
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Base view controller that operates with pass code
|
open class BasePassCodeViewController: UIViewController {
|
||||||
open class BasePassCodeViewController: UIViewController, LegacyConfigurableController {
|
|
||||||
|
|
||||||
public var viewModel: BasePassCodeViewModel! // swiftlint:disable:this implicitly_unwrapped_optional
|
public var viewModel: BasePassCodeViewModel!
|
||||||
|
|
||||||
// MARK: - IBOutlets
|
// MARK: - IBOutlets
|
||||||
|
|
||||||
@IBOutlet private weak var titleLabel: UILabel?
|
@IBOutlet public weak var titleLabel: UILabel?
|
||||||
@IBOutlet private weak var errorLabel: UILabel?
|
@IBOutlet public weak var errorLabel: UILabel?
|
||||||
@IBOutlet private weak var dotStackView: UIStackView!
|
@IBOutlet public weak var dotStackView: UIStackView!
|
||||||
|
|
||||||
public let disposeBag = DisposeBag()
|
public let disposeBag = DisposeBag()
|
||||||
private var delayedErrorDescriptions: Disposable?
|
|
||||||
|
|
||||||
private lazy var fakeTextField: UITextField = {
|
fileprivate lazy var fakeTextField: UITextField = {
|
||||||
let fakeTextField = UITextField()
|
let fakeTextField = UITextField()
|
||||||
fakeTextField.isSecureTextEntry = true
|
fakeTextField.isSecureTextEntry = true
|
||||||
fakeTextField.keyboardType = .numberPad
|
fakeTextField.keyboardType = .numberPad
|
||||||
|
|
@ -77,20 +72,9 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
|
||||||
|
|
||||||
initialLoadView()
|
initialLoadView()
|
||||||
initialDotNumberConfiguration()
|
initialDotNumberConfiguration()
|
||||||
|
enebleKeyboard()
|
||||||
configureBackgroundNotifications()
|
configureBackgroundNotifications()
|
||||||
showBiometricsRequestIfNeeded()
|
showTouchIdIfNeeded(with: touchIdHint)
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -100,17 +84,21 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationCenter.default.rx.notification(UIApplication.willResignActiveNotification)
|
NotificationCenter.default.rx.notification(.UIApplicationWillResignActive)
|
||||||
.subscribe(onNext: { [weak self] _ in
|
.subscribe(onNext: { [weak self] _ in
|
||||||
self?.resetUI()
|
self?.resetUI()
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.addDisposableTo(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.passCodeLength {
|
for _ in 0..<viewModel.passCodeConfiguration.passCodeCharactersNumber {
|
||||||
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)
|
||||||
|
|
@ -121,10 +109,10 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
|
||||||
resetDotsUI()
|
resetDotsUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func resetDotsUI() {
|
fileprivate func resetDotsUI() {
|
||||||
fakeTextField.text = nil
|
fakeTextField.text = nil
|
||||||
dotStackView.arrangedSubviews
|
dotStackView.arrangedSubviews
|
||||||
.compactMap { $0 as? UIImageView }
|
.flatMap { $0 as? UIImageView }
|
||||||
.forEach { $0.image = self.imageFor(type: .clear) }
|
.forEach { $0.image = self.imageFor(type: .clear) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,11 +125,11 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
|
||||||
imageView.image = imageFor(type: state)
|
imageView.image = imageFor(type: state)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setStates(for passCodeText: String) {
|
fileprivate func setStates(for passCodeText: String) {
|
||||||
var statesArray: [PinImageType] = []
|
var statesArray: [PinImageType] = []
|
||||||
|
|
||||||
for characterIndex in 0..<viewModel.passCodeConfiguration.passCodeLength {
|
for characterIndex in 0..<viewModel.passCodeConfiguration.passCodeCharactersNumber {
|
||||||
let state: PinImageType = Int(characterIndex) <= passCodeText.count - 1 ? .entered : .clear
|
let state: PinImageType = Int(characterIndex) <= passCodeText.characters.count - 1 ? .entered : .clear
|
||||||
statesArray.append(state)
|
statesArray.append(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,110 +138,69 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func resetUI() {
|
fileprivate func showTouchIdIfNeeded(with description: String) {
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - HAVE TO OVERRIDE
|
// MARK: - HAVE TO OVERRIDE
|
||||||
|
|
||||||
/// 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
|
// override to change Images
|
||||||
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
|
|
||||||
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)")
|
||||||
return UIImage()
|
return UIImage()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Override to change error description
|
// override to change error text
|
||||||
open func errorDescription(for error: PassCodeError) -> [PassCodeDelayedDescription] {
|
open func errorDescription(for error: PassCodeError) -> String {
|
||||||
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) -> NSAttributedString {
|
open func actionTitle(for passCodeControllerState: PassCodeControllerState) -> String {
|
||||||
assertionFailure("You should override this method: actionTitle(for passCodeControllerState: PassCodeControllerState)")
|
assertionFailure("You should override this method: actionTitle(for passCodeControllerState: PassCodeControllerState)")
|
||||||
return NSAttributedString(string: "")
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Functions that you can override to customize your controller
|
// MARK: - Functions that can you can override to castomise your controller
|
||||||
|
|
||||||
/// Call to show error
|
|
||||||
open func showError(for error: PassCodeError) {
|
open func showError(for error: PassCodeError) {
|
||||||
let descriptionsObservables = errorDescription(for: error)
|
errorLabel?.text = 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
|
|
||||||
open func hideError() {
|
open func hideError() {
|
||||||
errorLabel?.isHidden = true
|
errorLabel?.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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?.attributedText = actionTitle(for: passCodeControllerState)
|
titleLabel?.text = actionTitle(for: passCodeControllerState)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public functions
|
}
|
||||||
|
|
||||||
/// 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()
|
||||||
|
|
@ -261,13 +208,10 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
|
||||||
self?.setStates(for: text ?? "")
|
self?.setStates(for: text ?? "")
|
||||||
self?.hideError()
|
self?.hideError()
|
||||||
})
|
})
|
||||||
.delay(.milliseconds(100)) // time to draw dots
|
.drive(viewModel.passCodeText)
|
||||||
.drive(onNext: { [weak self] text in
|
.addDisposableTo(disposeBag)
|
||||||
self?.viewModel.setPassCodeText(text)
|
|
||||||
})
|
|
||||||
.disposed(by: disposeBag)
|
|
||||||
|
|
||||||
viewModel.validationResultDriver
|
viewModel.validationResult
|
||||||
.drive(onNext: { [weak self] validationResult in
|
.drive(onNext: { [weak self] validationResult in
|
||||||
guard let validationResult = validationResult else {
|
guard let validationResult = validationResult else {
|
||||||
return
|
return
|
||||||
|
|
@ -275,29 +219,28 @@ open class BasePassCodeViewController: UIViewController, LegacyConfigurableContr
|
||||||
|
|
||||||
if validationResult.isValid {
|
if validationResult.isValid {
|
||||||
self?.hideError()
|
self?.hideError()
|
||||||
} else if let passCodeError = validationResult.error {
|
} else if let pasCodeError = validationResult.error {
|
||||||
self?.showError(for: passCodeError)
|
self?.showError(for: pasCodeError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.addDisposableTo(disposeBag)
|
||||||
|
|
||||||
viewModel.passCodeControllerStateDriver
|
viewModel.passCodeControllerState
|
||||||
.drive(onNext: { [weak self] controllerState in
|
.drive(onNext: { [weak self] controllerState in
|
||||||
self?.configureUI(for: controllerState)
|
self?.configureUI(for: controllerState)
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.addDisposableTo(disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
open func addViews() {}
|
open func addViews() {}
|
||||||
|
|
||||||
open func configureAppearance() {}
|
open func setAppearance() {}
|
||||||
|
|
||||||
open func configureBarButtons() {}
|
open func configureBarButtons() {}
|
||||||
|
|
||||||
open func localize() {}
|
open func localize() {}
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - UITextFieldDelegate
|
}
|
||||||
|
|
||||||
extension BasePassCodeViewController: UITextFieldDelegate {
|
extension BasePassCodeViewController: UITextFieldDelegate {
|
||||||
|
|
||||||
|
|
@ -308,4 +251,5 @@ extension BasePassCodeViewController: UITextFieldDelegate {
|
||||||
let invalid = CharacterSet(charactersIn: "0123456789").inverted
|
let invalid = CharacterSet(charactersIn: "0123456789").inverted
|
||||||
return string.rangeOfCharacter(from: invalid, options: [], range: string.startIndex..<string.endIndex) == nil
|
return string.rangeOfCharacter(from: invalid, options: [], range: string.startIndex..<string.endIndex) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
public enum PassCodeAuthType {
|
||||||
|
case passCode(String)
|
||||||
|
case touchId
|
||||||
|
}
|
||||||
|
|
||||||
|
open class BasePassCodeViewModel: BaseViewModel {
|
||||||
|
|
||||||
|
public let controllerType: PassCodeControllerType
|
||||||
|
|
||||||
|
public let disposeBag = DisposeBag()
|
||||||
|
|
||||||
|
public let touchIdService: TouchIDService?
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addDisposableTo(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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addDisposableTo(disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func reset() {
|
||||||
|
passCodeText.value = nil
|
||||||
|
validationResultHolder.value = nil
|
||||||
|
passCodeControllerStateHolder.value = controllerType == .change ? .oldEnter : .enter
|
||||||
|
attemptsNumber = 0
|
||||||
|
passCodeHolder.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - HAVE TO OVERRIDE
|
||||||
|
|
||||||
|
open func isEnteredPassCodeValid(_ passCode: String) -> Bool {
|
||||||
|
assertionFailure("You should override this method: isEnteredPassCodeValid(_ passCode: String)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
open func authSucceed(_ type: PassCodeAuthType) {
|
||||||
|
assertionFailure("You should override this method: authSucceed(_ type: PassCodeAuthType)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Functions that can you can override to use TouchId
|
||||||
|
|
||||||
|
open var isTouchIdEnabled: Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,26 +20,32 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
/// Describes error, which may occur during pass code entering
|
import Foundation
|
||||||
public enum PassCodeError: Error {
|
|
||||||
/// Different codes
|
|
||||||
case codesNotMatch
|
|
||||||
|
|
||||||
/// Value is remaining attemps
|
public enum ApiError: Error {
|
||||||
case wrongCode(attemptsRemaining: Int)
|
|
||||||
|
case error(code: Int, message: String)
|
||||||
|
case none
|
||||||
|
|
||||||
/// Attempts limit reached (for create, change or enter)
|
|
||||||
case tooManyAttempts(type: PassCodeOperationType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension PassCodeError {
|
extension ApiError: LocalizedError {
|
||||||
var isTooManyAttempts: Bool {
|
|
||||||
switch self {
|
|
||||||
case .tooManyAttempts:
|
|
||||||
return true
|
|
||||||
|
|
||||||
default:
|
public init(apiResponse: ApiResponseProtocol) {
|
||||||
return false
|
if apiResponse.errorCode != 0, let message = apiResponse.errorMessage {
|
||||||
|
self = ApiError.error(code: apiResponse.errorCode, message: message)
|
||||||
|
} else {
|
||||||
|
self = ApiError.none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var errorDescription: String? {
|
||||||
|
switch self {
|
||||||
|
case .error(_, let message):
|
||||||
|
return message
|
||||||
|
case .none:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,16 +20,18 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
public protocol ApiErrorProtocol: RawRepresentable {}
|
||||||
|
|
||||||
/// Describes attributed string and time interval that message should be displayed after
|
extension Error {
|
||||||
public struct PassCodeDelayedDescription {
|
|
||||||
|
|
||||||
let delay: TimeInterval
|
public func isApiError<T: ApiErrorProtocol>(_ apiErrorType: T) -> Bool where T.RawValue == Int {
|
||||||
let description: NSAttributedString
|
if let error = self as? ApiError,
|
||||||
|
case let .error(code: code, message: _) = error,
|
||||||
public init(description: NSAttributedString, delay: TimeInterval = 0) {
|
code == apiErrorType.rawValue {
|
||||||
self.description = description
|
return true
|
||||||
self.delay = delay
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,12 +22,8 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Prototol with two fields that describes result of online validation.
|
public enum ConnectionError: LocalizedError {
|
||||||
public protocol OnlineValidateable {
|
|
||||||
|
|
||||||
/// Contains true if online validation did passed.
|
case noConnection
|
||||||
var isValid: Bool { get }
|
|
||||||
|
|
||||||
/// May contain an error if validation did failed.
|
|
||||||
var error: Error? { get }
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
public typealias VoidBlock = () -> Void
|
||||||
|
|
||||||
|
public extension Observable {
|
||||||
|
|
||||||
|
public func handleConnectionErrors() -> Observable<Observable.E> {
|
||||||
|
return observeOn(CurrentThreadScheduler.instance)
|
||||||
|
|
||||||
|
// handle no internet connection
|
||||||
|
.do(onError: { error in
|
||||||
|
if let urlError = error as? URLError,
|
||||||
|
urlError.code == .notConnectedToInternet ||
|
||||||
|
urlError.code == .timedOut {
|
||||||
|
DDLogError("Error: No Connection")
|
||||||
|
throw ConnectionError.noConnection
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// handle unacceptable http status code like "500 Internal Server Error" and others
|
||||||
|
.do(onError: { error in
|
||||||
|
if let afError = error as? AFError,
|
||||||
|
case let .responseValidationFailed(reason: reason) = afError,
|
||||||
|
case let .unacceptableStatusCode(code: statusCode) = reason {
|
||||||
|
DDLogError("Error: Unacceptable HTTP Status Code - \(statusCode)")
|
||||||
|
throw ConnectionError.noConnection
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public func retryWithinErrors(_ errorTypes: [Error.Type] = [ConnectionError.self],
|
||||||
|
retryLimit: Int = DefaultNetworkService.retryLimit)
|
||||||
|
-> Observable<Observable.E> {
|
||||||
|
|
||||||
|
return observeOn(CurrentThreadScheduler.instance)
|
||||||
|
.retryWhen { errors -> Observable<Observable.E> in
|
||||||
|
return errors.flatMapWithIndex { e, a -> Observable<Observable.E> in
|
||||||
|
let canRetry = errorTypes.contains { type(of: e) == $0 }
|
||||||
|
|
||||||
|
return (canRetry && a < retryLimit - 1) ? self : .error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public func changeLoadingBehaviour(isLoading: PublishSubject<Bool>) -> Observable<Observable.E> {
|
||||||
|
return observeOn(CurrentThreadScheduler.instance)
|
||||||
|
.do(onNext: { _ in
|
||||||
|
isLoading.onNext(false)
|
||||||
|
}, onError: { _ in
|
||||||
|
isLoading.onNext(false)
|
||||||
|
}, onSubscribe: { _ in
|
||||||
|
isLoading.onNext(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,12 +22,12 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
public extension UIBarButtonItem {
|
extension UIBarButtonItem {
|
||||||
|
|
||||||
/// 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,30 +22,29 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
private enum Keys {
|
fileprivate enum Keys {
|
||||||
static let sessionId = "sessionId"
|
static let sessionId = "sessionId"
|
||||||
static let userLogin = "userLogin"
|
static let userLogin = "userLogin"
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension UserDefaults {
|
public extension UserDefaults {
|
||||||
|
|
||||||
/// Default place to store session id
|
public var sessionId: String? {
|
||||||
var sessionId: String? {
|
|
||||||
get {
|
get {
|
||||||
string(forKey: Keys.sessionId)
|
return string(forKey: Keys.sessionId)
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
set(newValue, forKey: Keys.sessionId)
|
set(newValue, forKey: Keys.sessionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default place to store userLogin
|
public var userLogin: String? {
|
||||||
var userLogin: String? {
|
|
||||||
get {
|
get {
|
||||||
string(forKey: Keys.userLogin)
|
return string(forKey: Keys.userLogin)
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
set(newValue, forKey: Keys.userLogin)
|
set(newValue, forKey: Keys.userLogin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>FMWK</string>
|
<string>FMWK</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0</string>
|
<string>0.0.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,25 +21,13 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import KeychainAccess
|
import KeychainAccess
|
||||||
import CryptoSwift
|
import CocoaLumberjack
|
||||||
|
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
|
|
||||||
open class BasePassCodeService {
|
open class BasePassCodeService {
|
||||||
|
|
||||||
/// 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() {
|
||||||
|
|
@ -52,47 +40,69 @@ open class BasePassCodeService {
|
||||||
|
|
||||||
// MARK: - Private stuff
|
// MARK: - Private stuff
|
||||||
|
|
||||||
private lazy var keychain = Keychain(service: BasePassCodeService.keychainServiceString).synchronizable(false)
|
fileprivate lazy var keychain: Keychain = {
|
||||||
|
return Keychain(service: keychainService)
|
||||||
|
.synchronizable(false)
|
||||||
|
}()
|
||||||
|
|
||||||
private var passCodeHash: String? {
|
fileprivate var passCodeHash: String? {
|
||||||
keychain[Keys.passCodeHash]
|
return 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"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension BasePassCodeService {
|
extension BasePassCodeService {
|
||||||
|
|
||||||
/// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Possibility to authenticate via biometrics. TouchID or FaceID
|
public var isTouchIdEnabled: Bool {
|
||||||
var isBiometricsAuthorizationEnabled: Bool {
|
|
||||||
get {
|
get {
|
||||||
keychain[Keys.isBiometricsEnabled] == Values.biometricsEnabled
|
return keychain[Keys.isTouchIdEnabled] == Values.touchIdEnabled
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
keychain[Keys.isBiometricsEnabled] = newValue ? Values.biometricsEnabled : nil
|
keychain[Keys.isTouchIdEnabled] = newValue ? Values.touchIdEnabled : nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Saves new pass code
|
public func save(passCode: String?) {
|
||||||
func save(passCode: String?) {
|
|
||||||
if let passCode = passCode {
|
if let passCode = passCode {
|
||||||
keychain[Keys.passCodeHash] = passCode.sha256()
|
keychain[Keys.passCodeHash] = sha256(passCode)
|
||||||
} else {
|
} else {
|
||||||
keychain[Keys.passCodeHash] = nil
|
keychain[Keys.passCodeHash] = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
public func reset() {
|
||||||
func reset() {
|
|
||||||
save(passCode: nil)
|
save(passCode: nil)
|
||||||
isBiometricsAuthorizationEnabled = false
|
isTouchIdEnabled = 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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Touch Instinct
|
// Copyright (c) 2017 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,14 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import RxSwift
|
import RxSwift
|
||||||
|
import LeadKit
|
||||||
|
|
||||||
/// Represents service that store basic user information
|
|
||||||
open class BaseUserService {
|
open class BaseUserService {
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
// Can be overrided
|
// Can be overrided
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns user login
|
|
||||||
open var userLogin: String {
|
open var userLogin: String {
|
||||||
guard let defaultsLogin = UserDefaults.standard.userLogin else {
|
guard let defaultsLogin = UserDefaults.standard.userLogin else {
|
||||||
assertionFailure("userLogin is nil. Use isLoggedIn before read userLogin")
|
assertionFailure("userLogin is nil. Use isLoggedIn before read userLogin")
|
||||||
|
|
@ -39,7 +38,6 @@ open class BaseUserService {
|
||||||
return defaultsLogin
|
return defaultsLogin
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns session id
|
|
||||||
open var sessionId: String {
|
open var sessionId: String {
|
||||||
guard let defaultsSessionId = UserDefaults.standard.sessionId else {
|
guard let defaultsSessionId = UserDefaults.standard.sessionId else {
|
||||||
assertionFailure("sessionId is nil. Use isLoggedIn before read sessionId")
|
assertionFailure("sessionId is nil. Use isLoggedIn before read sessionId")
|
||||||
|
|
@ -48,14 +46,14 @@ open class BaseUserService {
|
||||||
return defaultsSessionId
|
return defaultsSessionId
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates if user is logged in
|
|
||||||
open var isLoggedIn: Bool {
|
open var isLoggedIn: Bool {
|
||||||
UserDefaults.standard.sessionId != nil
|
return UserDefaults.standard.sessionId != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset user information
|
|
||||||
open class func clearData() {
|
open class func clearData() {
|
||||||
UserDefaults.standard.sessionId = nil
|
UserDefaults.standard.sessionId = nil
|
||||||
UserDefaults.standard.userLogin = nil
|
UserDefaults.standard.userLogin = nil
|
||||||
|
UserDefaults.standard.synchronize()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
open class ApiNetworkService: DefaultNetworkService {
|
||||||
|
|
||||||
|
open func request<T: ImmutableMappable>(with parameters: ApiRequestParameters) -> Observable<T> {
|
||||||
|
let apiResponseRequest = rxRequest(with: parameters) as Observable<(response: HTTPURLResponse, model: ApiResponse)>
|
||||||
|
|
||||||
|
return apiResponseRequest
|
||||||
|
.handleConnectionErrors()
|
||||||
|
.map {
|
||||||
|
if $0.model.errorCode == 0 {
|
||||||
|
return try T(JSON: try cast($0.model.result) as [String: Any])
|
||||||
|
} else {
|
||||||
|
throw ApiError(apiResponse: $0.model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open func requestForResult(with parameters: ApiRequestParameters) -> Observable<Bool> {
|
||||||
|
let apiResponseRequest = rxRequest(with: parameters) as Observable<(response: HTTPURLResponse, model: ApiResponse)>
|
||||||
|
|
||||||
|
return apiResponseRequest
|
||||||
|
.handleConnectionErrors()
|
||||||
|
.map {
|
||||||
|
if $0.model.errorCode == 0,
|
||||||
|
let result = $0.model.result as? Bool {
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
throw ApiError(apiResponse: $0.model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
//
|
||||||
|
// 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 ObjectMapper
|
||||||
|
import RxSwift
|
||||||
|
|
||||||
|
open class DefaultNetworkService: NetworkService {
|
||||||
|
|
||||||
|
static let retryLimit = 3
|
||||||
|
|
||||||
|
open class var baseUrl: String {
|
||||||
|
fatalError("You should override this var: baseUrl")
|
||||||
|
}
|
||||||
|
|
||||||
|
open class var defaultTimeoutInterval: TimeInterval {
|
||||||
|
return 20.0
|
||||||
|
}
|
||||||
|
|
||||||
|
public override init(sessionManager: SessionManager) {
|
||||||
|
super.init(sessionManager: sessionManager)
|
||||||
|
|
||||||
|
bindActivityIndicator()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Default Values
|
||||||
|
|
||||||
|
open class var serverTrustPolicies: [String: ServerTrustPolicy] {
|
||||||
|
return [
|
||||||
|
baseUrl: .disableEvaluation
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
open class var configuration: URLSessionConfiguration {
|
||||||
|
let configuration = URLSessionConfiguration.default
|
||||||
|
configuration.timeoutIntervalForRequest = defaultTimeoutInterval
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
open class var sessionManager: SessionManager {
|
||||||
|
let sessionManager = SessionManager(configuration: configuration,
|
||||||
|
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
|
||||||
|
return sessionManager
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
public class TouchIDService {
|
||||||
|
|
||||||
|
private lazy var laContext: LAContext = {
|
||||||
|
return LAContext()
|
||||||
|
}()
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
public var canAuthenticateByTouchId: Bool {
|
||||||
|
return laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func authenticateByTouchId(description: String, authHandler: @escaping TouchIDServiceAuthHandler) {
|
||||||
|
laContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
|
||||||
|
localizedReason: description) { success, _ in
|
||||||
|
|
||||||
|
authHandler(success)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
source "https://github.com/CocoaPods/Specs.git"
|
||||||
|
source "https://github.com/TouchInstinct/Podspecs.git"
|
||||||
|
|
||||||
|
platform :ios, '9.0'
|
||||||
|
|
||||||
|
target 'LeadKitAdditions' do
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
pod 'LeadKit', :git => 'https://github.com/TouchInstinct/LeadKit.git', :branch => 'fix/sharedApplication', :commit => 'fd0eb18b8a6680ff16bbb1668d1ae0d29f29fad7'
|
||||||
|
pod 'KeychainAccess'
|
||||||
|
pod 'IDZSwiftCommonCrypto'
|
||||||
|
end
|
||||||
|
|
||||||
|
# If you have slow HDD
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = "true"
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
PODS:
|
||||||
|
- Alamofire (4.4.0)
|
||||||
|
- CocoaLumberjack/Default (3.1.0)
|
||||||
|
- CocoaLumberjack/Swift (3.1.0):
|
||||||
|
- CocoaLumberjack/Default
|
||||||
|
- IDZSwiftCommonCrypto (0.9.1)
|
||||||
|
- KeychainAccess (3.0.2)
|
||||||
|
- LeadKit (0.4.6):
|
||||||
|
- CocoaLumberjack/Swift (~> 3.1.0)
|
||||||
|
- ObjectMapper (~> 2.1)
|
||||||
|
- RxAlamofire (= 3.0.0)
|
||||||
|
- RxCocoa (= 3.2.0)
|
||||||
|
- RxSwift (= 3.2.0)
|
||||||
|
- Toast-Swift (~> 2.0.0)
|
||||||
|
- ObjectMapper (2.2.5)
|
||||||
|
- RxAlamofire (3.0.0):
|
||||||
|
- RxAlamofire/Core (= 3.0.0)
|
||||||
|
- RxAlamofire/Core (3.0.0):
|
||||||
|
- Alamofire (~> 4.0)
|
||||||
|
- RxSwift (~> 3.0)
|
||||||
|
- RxCocoa (3.2.0):
|
||||||
|
- RxSwift (~> 3.1)
|
||||||
|
- RxSwift (3.2.0)
|
||||||
|
- Toast-Swift (2.0.0)
|
||||||
|
|
||||||
|
DEPENDENCIES:
|
||||||
|
- IDZSwiftCommonCrypto
|
||||||
|
- KeychainAccess
|
||||||
|
- LeadKit (from `https://github.com/TouchInstinct/LeadKit.git`, commit `fd0eb18b8a6680ff16bbb1668d1ae0d29f29fad7`, branch `fix/sharedApplication`)
|
||||||
|
|
||||||
|
EXTERNAL SOURCES:
|
||||||
|
LeadKit:
|
||||||
|
:branch: fix/sharedApplication
|
||||||
|
:commit: fd0eb18b8a6680ff16bbb1668d1ae0d29f29fad7
|
||||||
|
:git: https://github.com/TouchInstinct/LeadKit.git
|
||||||
|
|
||||||
|
CHECKOUT OPTIONS:
|
||||||
|
LeadKit:
|
||||||
|
:commit: fd0eb18b8a6680ff16bbb1668d1ae0d29f29fad7
|
||||||
|
:git: https://github.com/TouchInstinct/LeadKit.git
|
||||||
|
|
||||||
|
SPEC CHECKSUMS:
|
||||||
|
Alamofire: dc44b1600b800eb63da6a19039a0083d62a6a62d
|
||||||
|
CocoaLumberjack: 8311463ddf9ee86a06ef92a071dd656c89244500
|
||||||
|
IDZSwiftCommonCrypto: c44fe5c0219a219846b56b4c148615dd06e58591
|
||||||
|
KeychainAccess: a986406022dfc7c634c691ad3bec670cc6a32002
|
||||||
|
LeadKit: d688a8bef79de7bbd83d553da3cb6c5292d48f2d
|
||||||
|
ObjectMapper: fb30f71e08470d1e5a20b199fafe1246281db898
|
||||||
|
RxAlamofire: 0b1fa48f545fffe7f7a28af2086bcaa3b5946cc9
|
||||||
|
RxCocoa: ccdf43101a70407097a29082f648ba1676075b30
|
||||||
|
RxSwift: 46574f70d416b7923c237195939cc488a7fbf3a0
|
||||||
|
Toast-Swift: 5b2f8f720f7e78e48511f693df1f9c9a6e38a25a
|
||||||
|
|
||||||
|
PODFILE CHECKSUM: 8e8ba1566ac9d3fe5b93325ab2faa9c76da42cd5
|
||||||
|
|
||||||
|
COCOAPODS: 1.2.1
|
||||||
22
Podfile
22
Podfile
|
|
@ -1,22 +0,0 @@
|
||||||
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
81
Podfile.lock
|
|
@ -1,81 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
||||||
//
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,235 +0,0 @@
|
||||||
//
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import Foundation
|
|
||||||
import Alamofire
|
|
||||||
|
|
||||||
/// Extension for network Error classification
|
|
||||||
public extension Error {
|
|
||||||
|
|
||||||
/// Returns: true if error is connection error
|
|
||||||
var isConnectionError: Bool {
|
|
||||||
guard let urlError = self as? URLError else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return urlError.code == .notConnectedToInternet || urlError.code == .timedOut
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns: true if server json response is not valid
|
|
||||||
var isResponseSerializationError: Bool {
|
|
||||||
(self as? AFError)?.isResponseSerializationError ?? false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
<?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>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>FMWK</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>0.3.13</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string></string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
|
|
||||||
public protocol LegacyConfigurableController: InitializableView {
|
|
||||||
|
|
||||||
associatedtype ViewModelT
|
|
||||||
|
|
||||||
var viewModel: ViewModelT! { get } // swiftlint:disable:this implicitly_unwrapped_optional
|
|
||||||
|
|
||||||
func configureBarButtons()
|
|
||||||
|
|
||||||
func initialLoadView()
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension LegacyConfigurableController where Self: UIViewController {
|
|
||||||
|
|
||||||
func initializeView() {
|
|
||||||
assertionFailure("Use \(String(describing: initialLoadView)) for UIViewController instead!")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Method that should be called in viewDidLoad method of UIViewController.
|
|
||||||
func initialLoadView() {
|
|
||||||
addViews()
|
|
||||||
configureLayout()
|
|
||||||
configureAppearance()
|
|
||||||
configureBarButtons()
|
|
||||||
localize()
|
|
||||||
bindViews()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
//
|
|
||||||
// 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() {}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
import SwiftValidator
|
|
||||||
import RxCocoa
|
|
||||||
import RxSwift
|
|
||||||
|
|
||||||
private enum ValidationServiceStateReactType {
|
|
||||||
case none
|
|
||||||
case all
|
|
||||||
case each
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ValidationServiceState {
|
|
||||||
case initial
|
|
||||||
case valid
|
|
||||||
case invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension ValidationServiceState {
|
|
||||||
|
|
||||||
var isValid: Bool {
|
|
||||||
self == .valid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class ValidationService {
|
|
||||||
|
|
||||||
private var disposeBag = DisposeBag()
|
|
||||||
|
|
||||||
private(set) var validationItems: [ValidationItem] = []
|
|
||||||
|
|
||||||
private let stateHolder = BehaviorRelay<ValidationServiceState>(value: .initial)
|
|
||||||
public var state: ValidationServiceState {
|
|
||||||
stateHolder.value
|
|
||||||
}
|
|
||||||
public var stateObservable: Observable<ValidationServiceState> {
|
|
||||||
stateHolder.asObservable()
|
|
||||||
}
|
|
||||||
|
|
||||||
private var validationStateReactType: ValidationServiceStateReactType = .none
|
|
||||||
|
|
||||||
public init() {
|
|
||||||
// just to be accessible
|
|
||||||
}
|
|
||||||
|
|
||||||
public func register(item: ValidationItem) {
|
|
||||||
register(items: [item])
|
|
||||||
}
|
|
||||||
|
|
||||||
public func register(items: [ValidationItem]) {
|
|
||||||
validationItems += items
|
|
||||||
bindItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func unregisterAll() {
|
|
||||||
validationItems.removeAll()
|
|
||||||
bindItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func unregister(item: ValidationItem) {
|
|
||||||
unregister(items: [item])
|
|
||||||
}
|
|
||||||
|
|
||||||
public func unregister(items: [ValidationItem]) {
|
|
||||||
items.forEach { item in
|
|
||||||
if let removeIndex = validationItems.firstIndex(where: { $0 === item }) {
|
|
||||||
validationItems.remove(at: removeIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bindItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func validate() -> Bool {
|
|
||||||
validationStateReactType = .all
|
|
||||||
let isValid = validationItems.allSatisfy { $0.manualValidate() }
|
|
||||||
validationStateReactType = .each
|
|
||||||
|
|
||||||
return isValid
|
|
||||||
}
|
|
||||||
|
|
||||||
private func bindItems() {
|
|
||||||
disposeBag = DisposeBag()
|
|
||||||
|
|
||||||
let allValidationStateObservables = validationItems.map { $0.validationStateObservable }
|
|
||||||
|
|
||||||
let zipStates = Observable
|
|
||||||
.zip(allValidationStateObservables) { $0 }
|
|
||||||
.filter { [weak self] _ in
|
|
||||||
self?.validationStateReactType == .all
|
|
||||||
}
|
|
||||||
|
|
||||||
let combineLatestStates = Observable
|
|
||||||
.combineLatest(allValidationStateObservables) { $0 }
|
|
||||||
.filter { [weak self] _ in
|
|
||||||
self?.validationStateReactType == .each
|
|
||||||
}
|
|
||||||
|
|
||||||
let stateObservables = [zipStates, combineLatestStates]
|
|
||||||
|
|
||||||
stateObservables.forEach { observable in
|
|
||||||
observable
|
|
||||||
.map { states -> Bool in
|
|
||||||
states.allSatisfy { $0.isValid }
|
|
||||||
}
|
|
||||||
.map { $0 ? ValidationServiceState.valid : .invalid }
|
|
||||||
.bind(to: stateHolder)
|
|
||||||
.disposed(by: disposeBag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 2799169e2103c034b0e1be221860a23292766f36
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
import LeadKit
|
|
||||||
import LeadKitAdditions
|
|
||||||
import PlaygroundSupport
|
|
||||||
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<playground version='5.0' target-platform='ios' executeOnSourceChanges='false'>
|
|
||||||
<timeline fileName='timeline.xctimeline'/>
|
|
||||||
</playground>
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
readonly CONFIG_PATH=${PROJECT_DIR}/build-scripts/xcode/.swiftlint.yml
|
|
||||||
|
|
||||||
readonly SWIFTLINT_VERSION=0.45.1
|
|
||||||
readonly SWIFTLINT_PORTABLE_FILENAME=portable_swiftlint.zip
|
|
||||||
|
|
||||||
readonly SWIFTLINT_PORTABLE_URL=https://github.com/realm/SwiftLint/releases/download/${SWIFTLINT_VERSION}/${SWIFTLINT_PORTABLE_FILENAME}
|
|
||||||
|
|
||||||
. build-scripts/xcode/aux_scripts/download_file.sh ${SWIFTLINT_PORTABLE_FILENAME} ${SWIFTLINT_PORTABLE_URL} Downloads --remove-cached
|
|
||||||
|
|
||||||
cd Downloads && unzip -o ${SWIFTLINT_PORTABLE_FILENAME}
|
|
||||||
|
|
||||||
${PROJECT_DIR}/Downloads/swiftlint autocorrect --path ${PROJECT_DIR}/Sources --config ${CONFIG_PATH} && ${PROJECT_DIR}/Downloads/swiftlint --path ${PROJECT_DIR}/Sources --config ${CONFIG_PATH}
|
|
||||||
Loading…
Reference in New Issue