Compare commits

...

194 Commits

Author SHA1 Message Date
Ivan Smolin 4c4db7e5a5
Merge pull request #66 from TouchInstinct/feature/xcode_13_1
update libraries to build with Xcode 13.1; bump version
2022-01-26 20:26:33 +03:00
Ivan Smolin b6fe2474f5 update libraries to build with Xcode 13.1; bump version 2022-01-26 18:14:05 +03:00
Nikita 91d4eb2dd9
Merge pull request #65 from TouchInstinct/feature/podspecs_update
Updated podspecs
2020-09-03 12:06:09 +03:00
krasich74 42afb31d73 Updated podspecs 2020-09-03 10:32:40 +03:00
Nikita e22c6813cb
Merge pull request #64 from TouchInstinct/feature/podspecs_update
Updated podspecs
2020-09-02 10:55:04 +03:00
krasich74 0e99ac3ccc updated podspecs 2020-09-01 17:28:13 +03:00
Ivan Smolin de0b3b9da7
Merge pull request #63 from TouchInstinct/carthage__without_binary
Carthage  without binary
2020-01-23 12:11:08 +03:00
Ivan Smolin 6daa551dd3 update changelog 2020-01-22 20:07:27 +03:00
Ivan Smolin ffffc847f7 update LeadKit version 2020-01-22 20:06:33 +03:00
Ivan Smolin 4904449b85 remove Romefile 2020-01-22 19:04:59 +03:00
Ivan Smolin 131d3c7b78 fix swiftlint 2020-01-22 14:12:35 +03:00
Ivan Smolin 4fd075cbc8 add PinLayout 2020-01-16 12:49:17 +03:00
Ivan Smolin b91144f0ff update .gitignore 2020-01-15 22:03:05 +03:00
Ivan Smolin 8320179e01 fix linter warnings 2020-01-15 22:01:54 +03:00
Ivan Smolin cb8a37f9c9 add Romefile 2020-01-15 21:13:13 +03:00
Ivan Smolin 0f0a1861f1 add forgotten KeychainAccess 2020-01-15 21:13:07 +03:00
Ivan Smolin fc168d6d24 update dependencies, pin swiftlint version 2020-01-14 13:35:16 +03:00
Ivan Smolin 4006a3570f carthage without binaries 2020-01-13 19:07:53 +03:00
Pavel Lukandiy c3be916033
Merge pull request #62 from TouchInstinct/feature/pin_layout_removal
Feature/pin layout removal
2019-04-25 13:22:53 +03:00
Pavel Lukandiy beecb2955b Podspec update 2019-04-17 16:34:21 +03:00
Pavel Lukandiy 817be88e76 Podspec update 2019-04-17 15:32:48 +03:00
Pavel Lukandiy 98719044b0 Version update 2019-04-15 15:32:03 +03:00
Pavel Lukandiy 246bd21260 Removed pin cell 2019-04-15 15:29:12 +03:00
Ivan Smolin a63bfe6e7f
Merge pull request #61 from TouchInstinct/swift_5
migrate to swift 5
2019-03-28 12:46:42 +03:00
Ivan Smolin 61ae9439d3 migrate to swift 5 2019-03-28 12:41:09 +03:00
Ivan Smolin ee4c9cd6d9
Merge pull request #60 from TouchInstinct/feature/carthage
Feature/carthage
2019-02-11 14:35:47 +03:00
Ivan Smolin 0c2acda3c6 update swiftlint lint rules, podspec and changelog 2019-02-11 14:13:24 +03:00
Ivan Smolin 3d17f139c3 fix swiftlint script 2019-02-08 18:56:27 +03:00
Ivan Smolin 3830e59b69 add shared scheme 2019-02-08 18:52:46 +03:00
Ivan Smolin 498c0b970e remove extension target. move to carthage 2019-02-08 18:48:14 +03:00
Ivan Zinovyev b634eb0162
Merge pull request #59 from TouchInstinct/fix_changelog
Fix readme
2019-01-25 16:33:56 +03:00
Ivan Zinovyev 3d6a4d39f3 Fix readme 2019-01-25 16:30:21 +03:00
Ivan Zinovyev e42fece1c6
Merge pull request #58 from TouchInstinct/feature/biometry_type
Add BiometryType for BiometricsService
2019-01-25 15:49:24 +03:00
Ivan Zinovyev 7ff1786ab3 Add BiometryType for BiometricsService 2019-01-25 15:28:04 +03:00
SoriUR 4843f6b60d
Merge pull request #57 from SoriUR/feature/BiometricsService
Feature/biometrics service
2019-01-21 14:46:46 +03:00
Iurii 611046e649 increment podspec version 2019-01-21 06:19:01 +03:00
Iurii 70f0d35cdb update changelog 2019-01-21 06:16:55 +03:00
Iurii a4ff6a667a recreate the context 2019-01-21 05:44:07 +03:00
Iurii fb86b0ffe9 Add a comment 2019-01-21 05:37:05 +03:00
Iurii a60d211a15 Add a functions to service 2019-01-21 05:33:17 +03:00
Alexey Gerasimov 554a890fce
Merge pull request #56 from TouchInstinct/fix/validator
Fix/validator
2018-12-03 12:53:57 +03:00
scoreyou d42aeec894 Extensions target clear 2018-12-03 12:51:16 +03:00
scoreyou 3cdabcadd7 New validator fork linked 2018-12-03 12:42:55 +03:00
Alexey Gerasimov bbef78ccd7
Merge pull request #55 from TouchInstinct/fix/passCodeCount
TooManyAttempts logic fixed
2018-10-24 13:32:16 +03:00
Alexey Gerasimov 94269b86e5 TooManyAttempts logic fixed 2018-10-24 13:29:36 +03:00
Alexey Gerasimov 9b5c16b7da
Merge pull request #54 from TouchInstinct/feature/passCodeCount
Feature/pass code count
2018-10-22 18:33:43 +03:00
Alexey Gerasimov 94aaeba4ad Version update 2018-10-22 16:38:03 +03:00
Alexey Gerasimov d8285db06f Forgotten public 2018-10-22 16:28:40 +03:00
Alexey Gerasimov aec1b23df7 Too many attempts logic updated 2018-10-22 16:24:55 +03:00
Alexey Gerasimov 77309f0b77 Make showBiometricsRequestIfNeeded method public 2018-10-22 15:48:03 +03:00
Alexey Gerasimov 5c4ba04e2c LegacyConfigurableController PassCode fix 2018-10-22 15:44:47 +03:00
Alexey Gerasimov c8a6000b25 PassCodeControllerType renamed 2018-10-22 15:44:17 +03:00
iON1k 36d8602e8f
Merge pull request #53 from TouchInstinct/feature/swift4_2_wip
Swift 4.2
2018-10-15 17:03:16 +03:00
Anton Popkov 4f9658ecbf LeadKit updating 2018-10-15 16:05:47 +03:00
Anton Popkov 050f4439d5 Podspec update 2018-10-15 14:36:53 +03:00
Anton Popkov 6c39765788 LeadKit version update 2018-10-15 14:36:40 +03:00
Anton Popkov ca8581bb9a Pods update 2018-10-15 13:05:08 +03:00
Ivan Smolin 3486f990c1 migration to swift 4.2 and LeadKit 0.8.13 2018-10-15 12:36:33 +03:00
Aliona 9399fcb511
Merge pull request #52 from TouchInstinct/add_touch_id_recognition
Add isTouchId to BiometricsService
2018-07-25 16:18:54 +03:00
Aliona af4ca742c0
Update BiometricsService.swift 2018-07-25 16:04:52 +03:00
Aliona c27a031dcc Move isFaceIdSupported to Leadkit 2018-07-25 16:01:07 +03:00
Aliona b6c8200015
Merge pull request #51 from TouchInstinct/biometrics_service_upgrade
Add public init to biometricsService
2018-07-24 18:24:04 +03:00
Aliona c11573e493 Add public init to biometricsService 2018-07-24 18:22:14 +03:00
Andrey Ovsyannikov f5349272af
Merge pull request #50 from TouchInstinct/fix/pass_code
BasePassCodeViewController
2018-07-18 18:42:43 +03:00
Madhas 1570ce331d add methods to BasePassCodeViewController to make fakeTextField become and resign first responder 2018-07-18 18:32:35 +03:00
Aliona 1f8f0dc893
Merge pull request #48 from TouchInstinct/exclude_file
Exclude stuff
2018-06-26 15:30:23 +03:00
Aliona d43746777a
Update LeadKitAdditions.podspec 2018-06-26 00:22:29 +03:00
Aliona 1d54d44670
Update CHANGELOG.md 2018-06-25 22:07:05 +03:00
Aliona 4b87230329
Add version 2018-06-25 22:02:56 +03:00
Aliona 0cf938faad Merge remote-tracking branch 'origin/exclude_file' into exclude_file 2018-06-25 20:32:37 +03:00
Aliona 23f784019b Add cells 2018-06-25 20:32:28 +03:00
Aliona 48a31c4847
Update LeadKitAdditions.podspec 2018-06-25 20:27:53 +03:00
Aliona c35c768faa Revert "Exclude stuff"
This reverts commit 0dd82d15de.
2018-06-25 19:57:54 +03:00
Aliona f3d41ac0f9 Add separator cell 2018-06-25 19:57:29 +03:00
Aliona a6514267e6 Remove version 2018-06-25 18:08:07 +03:00
Aliona 0dd82d15de Exclude stuff 2018-06-25 17:20:23 +03:00
Andrey Ovsyannikov 061cc82741
Merge pull request #47 from TouchInstinct/test
Codable blast zone
2018-05-28 14:56:47 +03:00
Madhas ab19e3cc91 changelog updated 2018-05-28 14:22:20 +03:00
Madhas 3c9ec27f42 podspec updated 2018-05-28 14:11:50 +03:00
Madhas 29ce2bc74b remove network and object mapper stuff 2018-05-25 17:59:41 +03:00
Madhas 6f7c4f27e1 buid-scripts submodule added 2018-05-25 17:52:50 +03:00
Alexey Gerasimov 9b6357a170 Try to remove version dependency 2018-05-25 16:40:58 +03:00
Pavel 24839fdc25
Merge pull request #46 from TouchInstinct/feature/domain_state_property
Feature/domain state property
2018-05-24 12:27:43 +03:00
Pavel Lukandiy 7789dc9be1 Version up. Podspec update 2018-05-23 20:07:27 +03:00
Pavel Lukandiy b7d751baeb Description added 2018-05-23 19:56:51 +03:00
Pavel Lukandiy 3c3f28f766 Removed public init 2018-05-23 19:44:27 +03:00
Pavel Lukandiy 412f7ccd00 Initialization and description 2018-05-23 18:52:23 +03:00
Pavel Lukandiy dd9a9d6d34 Added domain state property 2018-05-23 18:15:16 +03:00
Ivan Smolin 128fa2826a
Merge pull request #45 from TouchInstinct/feature/pass_code_changes
Pass code changes and dependencies update
2018-04-23 11:06:35 +03:00
Ivan Smolin 90324c49e2 - Update: validationResult of BasePassCodeViewModel renamed to validationResultDriver and becomes public.
- Remove: MaskFieldTextProxy.
- Remove: InputMask dependency
- Replace: IDZSwiftCommonCrypto with CryptoSwift
2018-04-20 19:06:07 +03:00
Ivan Smolin a4fcd2026c
Merge pull request #44 from TouchInstinct/fix/build
fix build with new LeadKit
2018-04-11 19:51:43 +03:00
Ivan Smolin fa6684830e fix build with new LeadKit 2018-04-11 19:46:07 +03:00
Alexey Gerasimov 5937727493
Merge pull request #43 from TouchInstinct/fix/networkService
DefaultNetworkService refactor
2018-04-06 14:21:26 +03:00
Alexey Gerasimov 6d51575c38 DefaultNetworkService refactor 2018-04-06 14:19:09 +03:00
Alexey Gerasimov c9697df89e
Merge pull request #42 from TouchInstinct/feature/biometrics
Feature/biometrics
2018-04-05 13:38:05 +03:00
Alexey Gerasimov 4bd3bc7b5f Changelog and version 2018-04-05 13:25:43 +03:00
Alexey Gerasimov 52aa86c123 Merge branch 'master' into feature/biometrics 2018-04-05 13:24:05 +03:00
Alexey Gerasimov 5494db6ae9 Biometrics start and end notifications added 2018-04-05 13:22:57 +03:00
Ivan Smolin cf38f0905f
Merge pull request #41 from TouchInstinct/fix/pods
update podfile.lock and podspec
2018-04-05 13:09:26 +03:00
Ivan Smolin 7e22545762 update podfile.lock and podspec 2018-04-05 13:06:29 +03:00
Ivan Smolin 03123ae15b
Merge pull request #40 from TouchInstinct/feature/pin_layout_cells
Add LabelTableViewCell
2018-04-05 11:40:35 +03:00
Ivan Smolin 671000c217 Add PinLayout` dependency.
Add PinLayoutTableViewCell, SeparatorTableViewCell and LabelTableViewCell powered by PinLayout.
Add LabelCellViewModel default view model for label cell.
Add Playground to project.
2018-04-02 20:39:07 +03:00
Ivan Smolin dc16eb63b9
Merge pull request #39 from TouchInstinct/feature/xcode_migration
Feature/xcode migration
2018-04-02 16:11:19 +03:00
Ivan Smolin 7229194481 update to LeadKit 0.7.4 2018-04-02 15:58:51 +03:00
Ivan Smolin 2ff40285c6 Xcode 9.3 migration 2018-04-02 15:13:22 +03:00
Alexey Gerasimov c17742cd8b
Merge pull request #38 from TouchInstinct/fix/passCode
Delayed messages implemented
2018-03-30 17:23:56 +03:00
Alexey Gerasimov 4ef7dd0e19 Delayed messages implemented 2018-03-30 17:13:11 +03:00
Alexey Gerasimov a1483eeee9
Merge pull request #37 from TouchInstinct/fix/highlight
Fix/highlight
2018-03-30 14:47:57 +03:00
Alexey Gerasimov 63d748a46e Version incremented 2018-03-30 14:44:36 +03:00
Alexey Gerasimov eeeac04989 Try delay 2018-03-30 14:39:18 +03:00
Alexey Gerasimov ec8966a1fb Pin input throttled 2018-03-30 14:31:20 +03:00
Ivan Smolin 6adb4a1838
Merge pull request #36 from TouchInstinct/feature/text_field_view_model
base implementation of text cell view model
2018-03-27 20:53:31 +03:00
Ivan Smolin 0fbf19574c update pods and changelog 2018-03-27 20:06:20 +03:00
Ivan Smolin 7316f8f6a7 code review note 2018-03-27 18:13:04 +03:00
Ivan Smolin 46933ff42d code review note 2018-03-27 17:49:53 +03:00
Ivan Smolin dc0343993a linter 2018-03-27 11:38:09 +03:00
Ivan Smolin 468adfd5be DefaultNetworkService deprecation 2018-03-27 11:33:19 +03:00
Ivan Smolin 4988dd65d9 base implementation of text cell view model. remove old implementation of same stuff 2018-03-26 23:41:30 +03:00
Igor Kislyuk 17561204c3
Merge pull request #35 from TouchInstinct/fix/general
Fix/general
2018-03-26 20:27:49 +03:00
Igor Kislyuk 11df63f2f4 Update 2018-03-26 20:22:45 +03:00
Igor Kislyuk 15ccb075b4 Update 2018-03-26 20:21:37 +03:00
Igor Kislyuk d53b4b84f6 Private outlet fix 2018-03-26 18:49:42 +03:00
Igor Kislyuk 3a104b1566 Merge branch 'master' into fix/general 2018-03-26 18:37:32 +03:00
Ivan Smolin cb84b0b68b
Merge pull request #34 from TouchInstinct/feature/linters_update
Feature/linters update
2018-03-26 18:36:31 +03:00
Ivan Smolin 09dadff106 change project structure for linter 2018-03-26 17:58:55 +03:00
Ivan Smolin 26ed7cdd6f remove code-quality, add build-scripts. fix warnings 2018-03-26 17:48:22 +03:00
Igor Kislyuk 7bc61d062c
Merge pull request #33 from TouchInstinct/fix/pincode
Update for Swift 4. Refactor
2018-03-26 16:50:01 +03:00
Igor Kislyuk 1b26dd25bd Update podspec 2018-03-26 16:49:37 +03:00
Igor Kislyuk d91a81b225 Remove empty lines 2018-03-26 16:48:05 +03:00
Igor Kislyuk e602c1db59 Update logic 2018-03-26 14:11:46 +03:00
Igor Kislyuk 5d9a456d1d Code review notes 2018-03-26 13:33:04 +03:00
Igor Kislyuk 9baa92a303 Add attempt index 2018-03-23 20:00:46 +03:00
Igor Kislyuk 8bba538e4a Renamification & configuration 2018-03-23 17:40:19 +03:00
Igor Kislyuk f612914307 Rename TouchID to biometrics 2018-03-23 16:37:03 +03:00
Igor Kislyuk 27f42e55a1 Rename to biometrics 2018-03-23 15:52:34 +03:00
Igor Kislyuk 7b6e73e789 Update name & refactor error 2018-03-23 15:21:12 +03:00
Igor Kislyuk ce2c69e854 Passcode refactoring & improvements 2018-03-23 12:48:36 +03:00
Igor Kislyuk 4861749415 Update configuration 2018-03-22 19:49:53 +03:00
Igor Kislyuk 8a01633e03 Update for swift 4 2018-03-22 19:12:43 +03:00
Pavel 126a68aa81
Merge pull request #32 from TouchInstinct/fix/default_api_response
Updated typical api response
2017-12-07 19:30:34 +03:00
Pavel Lukandiy bbf3cd6974 Updated typical api response 2017-12-07 19:12:29 +03:00
Ivan Smolin c3252b13a4
Merge pull request #30 from TouchInstinct/validation_update
update access modifiers of ValidationService
2017-12-04 13:49:08 +03:00
Ivan Smolin 5c6423d901 update access modifiers of ValidationService 2017-12-04 12:42:07 +03:00
Ivan Smolin 9208d41301
Merge pull request #29 from TouchInstinct/leadkit_update
Leadkit update
2017-12-01 18:32:15 +03:00
Ivan Smolin e5aff4e1e9 retry changes. add acceptable status codes to DefaultNetworkManager 2017-12-01 17:11:37 +03:00
Ivan Smolin 042fac89cc fix crash when NetworkService creates lazy in background thread (globalinit_*_func0) 2017-12-01 15:53:11 +03:00
Ivan Smolin 0619187530 update LeadKit version and replace ConnectionError with LeadKit.RequestError 2017-10-30 19:24:05 +03:00
Ivan Smolin a66a06200a Merge pull request #28 from TouchInstinct/new-swift
New swift
2017-10-23 14:54:49 +03:00
Ivan Smolin b6e5bdd70f final Swift 4 migration 2017-10-23 14:35:45 +03:00
Ivan Smolin 0fc7085002 continue update (SwiftValidator and InputMask in progress) 2017-10-20 11:24:34 +03:00
Ivan Smolin d2e2d1f28b Merge branch 'master' into new-swift
# Conflicts:
#	LeadKitAdditions.podspec
2017-10-19 18:15:29 +03:00
iON1k ca8ae59f9a Merge pull request #27 from TouchInstinct/feature/refactoring
Little refactoring
2017-10-12 21:49:22 +03:00
Anton Popkov fc216a52d2 Podspec update 2017-10-12 21:23:44 +03:00
Anton Popkov 40180f8bd8 Remove excess public modifiers from extensions 2017-10-12 21:22:10 +03:00
Igor 5de3001982 Update dependency 2017-09-25 11:17:32 +03:00
Igor a35960cce6 Update to 0.1 version & add md files 2017-09-25 10:51:58 +03:00
Igor f1553fb594 Update IDZSwiftCommonCrypto & bump LeadKit 2017-09-25 10:49:20 +03:00
Igor Kislyuk b0d8c48f8e Merge pull request #26 from TouchInstinct/hotfix
Hotfix
2017-08-25 12:50:20 +03:00
Igor 3afb8124be Hotfix 2017-08-25 12:48:09 +03:00
Igor Kislyuk 50905c9a06 Merge pull request #25 from TouchInstinct/feature/extensions
Add. Network extensions
2017-08-25 12:39:45 +03:00
Igor 6d51a8e0f5 Fix capitalization 2017-08-25 12:39:21 +03:00
Igor e858c8ad02 Add. Network extensions 2017-08-25 12:31:18 +03:00
Grigory 3baa5deff9 Merge pull request #24 from TouchInstinct/BuildFix
CellTextFieldToolBar added
2017-07-28 15:11:55 +03:00
Igor Kislyuk 73ba56f3c7 Fix. Resource name 2017-07-28 14:28:07 +03:00
Igor Kislyuk 05ead686fb Merge branch 'BuildFix' of github.com:TouchInstinct/LeadKitAdditions into BuildFix 2017-07-28 14:24:59 +03:00
Igor Kislyuk 638399eb2d Fix. Linting & rules 2017-07-28 14:24:38 +03:00
Grigory Ulanov da0f96a54d Merge branch 'BuildFix' of https://github.com/TouchInstinct/LeadKitAdditions into BuildFix 2017-07-28 07:53:04 +03:00
Grigory Ulanov f93066a1f4 pull request fix 2017-07-28 07:52:57 +03:00
Igor Kislyuk 9c04cbdc32 Add. Code quality 2017-07-28 05:02:10 +03:00
Igor Kislyuk 192c7b0fe5 Add. Missing pod for subspec & license 2017-07-28 04:42:45 +03:00
Grigory Ulanov 706c47d4de CellTextFieldToolBar added 2017-07-27 15:29:07 +03:00
Nikolai Ashanin b59eb5d363 Merge pull request #23 from TouchInstinct/fix/building
Fix. Building
2017-07-26 23:01:11 +03:00
Igor Kislyuk c2c5ec5536 Fix. Building 2017-07-26 21:02:31 +03:00
Nikolai Ashanin aac1a961ba Merge pull request #22 from TouchInstinct/fix/requirments
Fix. Dependencies
2017-07-26 19:01:51 +03:00
Igor Kislyuk 2958a53d41 Fix. Dependencies 2017-07-26 19:00:17 +03:00
Nikolai Ashanin 8be0d2a6a6 Merge pull request #20 from TouchInstinct/pinStateFix
pin fixed
2017-07-06 13:27:17 +03:00
Grigory c4b02fb055 pin fixed 2017-07-06 13:22:54 +03:00
Nikolai Ashanin c751e89c13 Merge pull request #18 from TouchInstinct/feature/formTableView
Feature/form table view
2017-07-03 20:44:35 +03:00
Grigory c79c740d79 pull request fix 2017-06-20 17:32:18 +03:00
Grigory 36200eaeb9 build fix 2017-06-08 16:15:36 +03:00
Grigory 43b1861347 formTableView 2017-06-08 14:18:52 +03:00
Nikolai Ashanin 3defbf0534 Merge pull request #17 from TouchInstinct/fix/synchronize
Remove synchronize
2017-06-06 15:02:28 +03:00
Igor Kislyuk 17a5460009 Fix. Remove synchronize 2017-06-06 14:26:33 +03:00
Nikolai Ashanin 12ca384a03 Merge pull request #15 from scoreyou/feature/newLeadKit
Feature/new lead kit
2017-06-06 12:51:52 +03:00
Alexey Gerasimov 6fd0a27e84 Merge branch 'master' into feature/newLeadKit
# Conflicts:
#	LeadKitAdditions.podspec
2017-06-06 11:59:15 +03:00
Alexey Gerasimov 95974fa313 Fixed: pullRequest comments 2017-06-06 11:58:05 +03:00
Alexey Gerasimov 596f25ccc0 Comments added 2017-05-24 19:09:58 +03:00
Alexey Gerasimov 98ea69217d One more fix 2017-05-23 20:52:25 +03:00
Alexey Gerasimov 204559e814 One more deployment target fix 2017-05-23 20:30:39 +03:00
Alexey Gerasimov 2e0b76f54e Deployment target fixed 2017-05-23 20:27:24 +03:00
Alexey Gerasimov e518c708d6 Fixed for ios extensions 2017-05-23 20:04:06 +03:00
Alexey Gerasimov 7fec47f57b Redundant compilation flag removed 2017-05-10 15:58:54 +03:00
Alexey Gerasimov 783fc7d371 Extensions fix 2017-05-05 17:07:48 +03:00
Alexey Gerasimov fe631a5a1f Podfile, podspec modified, new target added 2017-05-04 20:42:24 +03:00
58 changed files with 2411 additions and 1479 deletions

3
.gitignore vendored
View File

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

3
.gitmodules vendored Normal file
View File

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

1
.swift-version Normal file
View File

@ -0,0 +1 @@
5.0

144
CHANGELOG.md Normal file
View File

@ -0,0 +1,144 @@
# 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 Normal file
View File

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

12
Cartfile.resolved Normal file
View File

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

201
LICENSE Normal file
View File

@ -0,0 +1,201 @@
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.

View File

@ -1,15 +1,23 @@
Pod::Spec.new do |s|
s.name = "LeadKitAdditions"
s.version = "0.0.14"
s.version = "0.4.0"
s.summary = "iOS framework with a bunch of tools for rapid development"
s.homepage = "https://github.com/NikAshanin/LeadKitAdditions"
s.homepage = "https://github.com/TouchInstinct/LeadKitAdditions"
s.license = "Apache License, Version 2.0"
s.author = "Touch Instinct"
s.platform = :ios, "9.0"
s.source = { :git => "https://github.com/NikAshanin/LeadKitAdditions.git", :tag => s.version }
s.source_files = "LeadKitAdditions/LeadKitAdditions/**/*.swift"
s.platform = :ios, "10.0"
s.source = { :git => "https://github.com/TouchInstinct/LeadKitAdditions.git", :tag => s.version }
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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +0,0 @@
exclude:
- 'Pods'
- 'Carthage'
- 'RxAlamofire'

View File

@ -1,568 +0,0 @@
// !$*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 */;
}

View File

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

View File

@ -1,182 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import LeadKit
import RxSwift
import RxCocoa
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 = .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
}
}

View File

@ -1,92 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import Alamofire
import CocoaLumberjack
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)
})
}
}

View File

@ -1,59 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import LeadKit
import Alamofire
import ObjectMapper
import RxSwift
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)
}
}
}
}

View File

@ -1,67 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Alamofire
import LeadKit
import 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
}
}

View File

@ -1,47 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import LocalAuthentication
public typealias TouchIDServiceAuthHandler = (Bool) -> Void
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)
}
}
}

View File

@ -1,15 +0,0 @@
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"

View File

@ -1,56 +0,0 @@
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 Normal file
View File

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

81
Podfile.lock Normal file
View File

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

1
README.md Normal file
View File

@ -0,0 +1 @@
# LeadKitAdditions

View File

@ -0,0 +1,164 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import LeadKit
import RxSwift
import RxCocoa
import SwiftValidator
/// Base implementation of TextFieldViewEvents.
open class BaseTextFieldViewEvents: TextFieldViewEvents {
public let textChangedDriver: Driver<String?>
/// Memberwise initializer.
///
/// - Parameter textChangedDriver: Driver that emits text changes from a view.
public init(textChangedDriver: Driver<String?>) {
self.textChangedDriver = textChangedDriver
}
}
/// Base implementation of text field view model events.
open class BaseTextFieldViewModelEvents: TextFieldViewModelEvents {
public let setTextDriver: Driver<String?>
public let changeValidationStateDriver: Driver<ValidationItemState>
public let changeOnlineValidationStateDriver: Driver<OnlineValidationState>
/// Memberwise initializer.
///
/// - Parameters:
/// - setTextDriver: Driver that emit text that should be set inside a view.
/// - changeValidationStateDriver: Driver that emit validation state changes events.
/// - changeOnlineValidationStateDriver: Driver that emit online validation state changes events.
public init(setTextDriver: Driver<String?>,
changeValidationStateDriver: Driver<ValidationItemState>,
changeOnlineValidationStateDriver: Driver<OnlineValidationState>) {
self.setTextDriver = setTextDriver
self.changeValidationStateDriver = changeValidationStateDriver
self.changeOnlineValidationStateDriver = changeOnlineValidationStateDriver
}
}
public extension BaseTextFieldViewModelEvents {
/// Method that binds text driver to validation service via validation rules.
///
/// - Parameters:
/// - textDriver: Driver that emits text changes.
/// - rules: Rules to validate for.
/// - validationService: Validation service to register in.
/// - Returns: Driver that emit validation state changes.
static func offlineValidationDriver(with textDriver: Driver<String?>,
using rules: [Rule] = [RequiredRule()],
in validationService: ValidationService) -> Driver<ValidationItemState> {
let validationItem = ValidationItem(rules: rules, textDriver: textDriver)
validationService.register(item: validationItem)
let validationStateDriver = validationItem
.validationStateObservable
.asDriver(onErrorJustReturn: .initial)
return validationStateDriver
}
typealias OnlineValidationClosure = (String) -> Single<OnlineValidateable>
/// Method that binds text driver to validation chain (offline validation -> online validation)
/// and returns online validation state driver.
///
/// - Parameters:
/// - textDriver: Driver that emits text changes.
/// - offlineRules: Rules to validate before online validation will be requested.
/// - validationClosure: Closure that will be called for online validation request.
/// - Returns: Driver that emit online validation state changes.
static func onlineValidationDriver(with textDriver: Driver<String?>,
using offlineRules: [Rule] = [],
validationClosure: @escaping OnlineValidationClosure) -> Driver<OnlineValidationState> {
textDriver.flatMap { string -> Driver<OnlineValidationState> in
guard let nonEmptyString = string, !nonEmptyString.isEmpty else {
return .just(.initial)
}
let passedRules = offlineRules
.allSatisfy { $0.validate(nonEmptyString) }
guard passedRules else {
return .just(.initial)
}
let validationDriver = validationClosure(nonEmptyString).map { result -> OnlineValidationState in
if result.isValid {
return .valid
} else {
return .invalid(error: result.error)
}
}
.asDriver(onErrorJustReturn: .initial)
return Driver.merge([.just(.initial), .just(.processing), validationDriver])
}
}
/// Convenience initializer with offline and online validation.
///
/// - Parameters:
/// - binding: Data model field binding.
/// - rules: Rules to validate before online validation will be requested.
/// - validationService: Validation service to register in.
/// - onlineValidationClosure: Closure that will be called for online validation request.
/// - onlineValidationThrottle: Throttling duration for each text change.
convenience init<T>(binding: DataModelFieldBinding<T>,
rules: [Rule] = [RequiredRule()],
validationService: ValidationService,
onlineValidationClosure: OnlineValidationClosure? = nil,
onlineValidationThrottle: RxTimeInterval = .milliseconds(500)) {
let dataModelFieldDriver = binding.fieldDriver
let offlineValidationDriver = BaseTextFieldViewModelEvents.offlineValidationDriver(with: dataModelFieldDriver,
using: rules,
in: validationService)
let onlineValidationDriver: Driver<OnlineValidationState>
if let onlineValidationClosure = onlineValidationClosure {
let throttledTextDriver = dataModelFieldDriver.throttle(onlineValidationThrottle)
onlineValidationDriver = BaseTextFieldViewModelEvents
.onlineValidationDriver(with: throttledTextDriver,
using: rules,
validationClosure: onlineValidationClosure)
} else {
onlineValidationDriver = .just(.initial)
}
self.init(setTextDriver: dataModelFieldDriver,
changeValidationStateDriver: offlineValidationDriver,
changeOnlineValidationStateDriver: onlineValidationDriver)
}
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Touch Instinct
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
@ -24,11 +24,13 @@ import UIKit
import RxSwift
import RxCocoa
/// Side to which activity indicator applied
public enum LoadingBarButtonSide {
case left
case right
}
/// Workaround with navigationBarButton, that can change state (UI) into activity indicator
public class LoadingBarButton {
fileprivate weak var navigationItem: UINavigationItem?
@ -40,6 +42,7 @@ public class LoadingBarButton {
switch side {
case .left:
return navigationItem?.leftBarButtonItem
case .right:
return navigationItem?.rightBarButtonItem
}
@ -48,12 +51,20 @@ public class LoadingBarButton {
switch side {
case .left:
navigationItem?.leftBarButtonItem = newValue
case .right:
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) {
self.navigationItem = navigationItem
self.side = side
@ -62,25 +73,32 @@ public class LoadingBarButton {
fileprivate func setState(waiting: Bool = false) {
if waiting {
let activityIndicatorItem = UIBarButtonItem.activityIndicator
let activityIndicatorItem = UIBarButtonItem.activityIndicator
barButtonItem = activityIndicatorItem.barButton
activityIndicatorItem.activityIndicator.startAnimating()
} else {
barButtonItem = initialBarButton
}
}
}
extension Observable {
public extension Observable {
public func changeLoadingUI(using barButton: LoadingBarButton) -> Observable<Observable.E> {
return observeOn(MainScheduler.instance)
/**
Reactive extension for LoadingBarButton
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: {
barButton.setState(waiting: true)
}, onDispose: {
barButton.setState(waiting: false)
})
}
}

View File

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

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Touch Instinct
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
@ -20,18 +20,16 @@
// THE SOFTWARE.
//
public protocol ApiErrorProtocol: RawRepresentable {}
import Foundation
extension Error {
/// Describes attributed string and time interval that message should be displayed after
public struct PassCodeDelayedDescription {
public func isApiError<T: ApiErrorProtocol>(_ apiErrorType: T) -> Bool where T.RawValue == Int {
if let error = self as? ApiError,
case let .error(code: code, message: _) = error,
code == apiErrorType.rawValue {
return true
} else {
return false
}
let delay: TimeInterval
let description: NSAttributedString
public init(description: NSAttributedString, delay: TimeInterval = 0) {
self.description = description
self.delay = delay
}
}

View File

@ -0,0 +1,45 @@
//
// 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.
//
/// Describes error, which may occur during pass code entering
public enum PassCodeError: Error {
/// Different codes
case codesNotMatch
/// Value is remaining attemps
case wrongCode(attemptsRemaining: Int)
/// Attempts limit reached (for create, change or enter)
case tooManyAttempts(type: PassCodeOperationType)
}
public extension PassCodeError {
var isTooManyAttempts: Bool {
switch self {
case .tooManyAttempts:
return true
default:
return false
}
}
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Touch Instinct
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
@ -23,24 +23,25 @@
import RxSwift
import RxCocoa
extension PassCodeHolderProtocol {
public extension PassCodeHolderProtocol {
public var passCodeHolderCreate: PassCodeHolderCreate? {
return self as? PassCodeHolderCreate
var passCodeHolderCreate: PassCodeHolderCreate? {
self as? PassCodeHolderCreate
}
public var passCodeHolderEnter: PassCodeHolderEnter? {
return self as? PassCodeHolderEnter
var passCodeHolderEnter: PassCodeHolderEnter? {
self as? PassCodeHolderEnter
}
public var passCodeHolderChange: PassCodeHolderChange? {
return self as? PassCodeHolderChange
var passCodeHolderChange: PassCodeHolderChange? {
self as? PassCodeHolderChange
}
}
/// Holds information about pass codes during pass code creation process
public class PassCodeHolderCreate: PassCodeHolderProtocol {
public let type: PassCodeControllerType = .create
public let type: PassCodeOperationType = .create
private var firstPassCode: String?
private var secondPassCode: String?
@ -54,7 +55,7 @@ public class PassCodeHolderCreate: PassCodeHolderProtocol {
}
public var shouldValidate: Bool {
return firstPassCode != nil && secondPassCode != nil
firstPassCode != nil && secondPassCode != nil
}
public var passCode: String? {
@ -69,8 +70,10 @@ public class PassCodeHolderCreate: PassCodeHolderProtocol {
switch enterStep {
case .enter:
firstPassCode = passCode
case .repeatEnter:
secondPassCode = passCode
default:
break
}
@ -80,24 +83,24 @@ public class PassCodeHolderCreate: PassCodeHolderProtocol {
if let passCode = passCode {
return .valid(passCode)
} else {
return .inValid(.codesNotMatch)
return .invalid(.codesNotMatch)
}
}
public func reset() {
firstPassCode = nil
firstPassCode = nil
secondPassCode = nil
}
}
/// Holds information about pass code during pass code entering process
public class PassCodeHolderEnter: PassCodeHolderProtocol {
public let type: PassCodeControllerType = .enter
public let type: PassCodeOperationType = .enter
public let enterStep: PassCodeControllerState = .enter
public var shouldValidate: Bool {
return passCode != nil
passCode != nil
}
public var passCode: String?
@ -110,19 +113,19 @@ public class PassCodeHolderEnter: PassCodeHolderProtocol {
if let passCode = passCode {
return .valid(passCode)
} else {
return .inValid(nil)
return .invalid(nil)
}
}
public func reset() {
passCode = nil
}
}
/// Holds information about pass codes during pass code changing process
public class PassCodeHolderChange: PassCodeHolderProtocol {
public let type: PassCodeControllerType = .change
public let type: PassCodeOperationType = .change
private var oldPassCode: String?
private var newFirstPassCode: String?
@ -150,12 +153,15 @@ public class PassCodeHolderChange: PassCodeHolderProtocol {
public var passCode: String? {
switch (oldPassCode, newFirstPassCode, newSecondPassCode) {
case (let oldPassCode?, nil, nil):
case let (oldPassCode?, nil, nil):
return oldPassCode
case (_, _?, nil):
return nil
case (_, let newFirstPassCode?, let newSecondPassCode?) where newFirstPassCode == newSecondPassCode:
case let (_, newFirstPassCode?, newSecondPassCode?) where newFirstPassCode == newSecondPassCode:
return newFirstPassCode
default:
return nil
}
@ -175,7 +181,7 @@ public class PassCodeHolderChange: PassCodeHolderProtocol {
if let passCode = passCode {
return .valid(passCode)
} else {
return .inValid(enterStep == .newEnter ? nil : .codesNotMatch)
return .invalid(enterStep == .newEnter ? nil : .codesNotMatch)
}
}
@ -187,5 +193,4 @@ public class PassCodeHolderChange: PassCodeHolderProtocol {
newSecondPassCode = nil
}
}
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Touch Instinct
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
@ -20,34 +20,49 @@
// THE SOFTWARE.
//
/// Holds information about enter type (create, change, etc), step
/// Also describes interface to manipulate with entered pass code
public protocol PassCodeHolderProtocol {
var type: PassCodeControllerType { get }
/// Type of operation with pass code
var type: PassCodeOperationType { get }
/// Operation step
var enterStep: PassCodeControllerState { get }
/// Add pass code for current step
func add(passCode: String)
/// Reset all progress
func reset()
/// Should been pass code validated
var shouldValidate: Bool { get }
/// Current pass code
var passCode: String? { get }
/// Returns passCode or error if pass code is invalid
func validate() -> PassCodeValidationResult
}
public class PassCodeHolderBuilder {
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 {
case .create:
return PassCodeHolderCreate()
case .enter:
return PassCodeHolderEnter()
case .change:
return PassCodeHolderChange()
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,235 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
/// Describes types of authentication
public enum PassCodeAuthType {
case passCode(String)
case touchId
}
/// Base view model for passCodeViewController
open class BasePassCodeViewModel {
public let operationType: PassCodeOperationType
public let disposeBag = DisposeBag()
/// Service that can answer if user is authorized by biometrics
public let biometricsService = BiometricsService()
/// Contains configuration for pass code operations
public let passCodeConfiguration: PassCodeConfiguration
private let validationResultHolder = BehaviorRelay<PassCodeValidationResult?>(value: nil)
public var validationResultDriver: Driver<PassCodeValidationResult?> {
validationResultHolder.asDriver()
}
private let passCodeControllerStateVariable = BehaviorRelay<PassCodeControllerState>(value: .enter)
public var passCodeControllerStateDriver: Driver<PassCodeControllerState> {
passCodeControllerStateVariable.asDriver()
}
private let passCodeText = BehaviorRelay<String?>(value: nil)
private var attemptsNumber = 0
private lazy var passCodeHolder: PassCodeHolderProtocol = PassCodeHolderBuilder.build(with: self.operationType)
public init(operationType: PassCodeOperationType, passCodeConfiguration: PassCodeConfiguration) {
self.operationType = operationType
self.passCodeConfiguration = passCodeConfiguration
bindViewModel()
}
private func bindViewModel() {
passCodeText.asDriver()
.distinctUntilChanged { $0 == $1 }
.drive(onNext: { [weak self] passCode in
if let passCode = passCode,
passCode.count == Int(self?.passCodeConfiguration.passCodeLength ?? 0) {
self?.set(passCode: passCode)
}
})
.disposed(by: disposeBag)
validationResultHolder.asObservable()
.bind(to: validationResultBinder)
.disposed(by: disposeBag)
}
// MARK: - Public
public var passCodeTextValue: String? {
passCodeText.value
}
public func setPassCodeText(_ value: String?) {
passCodeText.accept(value)
}
public func reset() {
passCodeText.accept(nil)
validationResultHolder.accept(nil)
passCodeControllerStateVariable.accept(operationType == .change ? .oldEnter : .enter)
attemptsNumber = 0
passCodeHolder.reset()
}
public func authenticateUsingBiometrics(with description: String, fallback: String?, cancel: String?) {
biometricsAuthBegins()
biometricsService.authenticateWithBiometrics(with: description,
fallback: fallback,
cancel: cancel) { [weak self] success, error in
self?.biometricsAuthEnds()
if success {
self?.authSucceed(.touchId)
} else {
self?.authFailed(with: error)
}
}
}
// MARK: - HAVE TO OVERRIDE
/// Override to check if entered pass code is equal to stored
open func isEnteredPassCodeValid(_ passCode: String) -> Bool {
assertionFailure("You should override this method: isEnteredPassCodeValid(_ passCode: String)")
return false
}
/// Method is called after successful authentication
open func authSucceed(_ type: PassCodeAuthType) {
assertionFailure("You should override this method: authSucceed(_ type: PassCodeAuthType)")
}
/// Called when authentication failed
open func authFailed(with: Error?) {
assertionFailure("You should override this method: authFailed(with: Error)")
}
// MARK: - Biometrics
/// Posibility to use biometrics for authentication
open var isBiometricsEnabled: Bool {
false
}
/// Notify about activation for biometrics. Remember to save user choice
open func activateBiometricsForUser() {
assertionFailure("You should override this method: activateBiometricsForUser()")
}
/// Notify before system alert with biometrics
open func biometricsAuthBegins() {}
/// Notify after system alert with biometrics
open func biometricsAuthEnds() {}
}
private extension BasePassCodeViewModel {
var validationResultBinder: Binder<PassCodeValidationResult?> {
Binder(self) { model, validationResult in
let isValid = validationResult?.isValid ?? false
let passCode = validationResult?.passCode
if model.passCodeHolder.type == .change {
if isValid, model.passCodeHolder.enterStep == .repeatEnter, let passCode = passCode {
model.authSucceed(.passCode(passCode))
} else {
model.passCodeControllerStateVariable.accept(model.passCodeHolder.enterStep)
}
} else {
if isValid, let passCode = passCode {
model.authSucceed(.passCode(passCode))
} else {
model.passCodeControllerStateVariable.accept(model.passCodeHolder.enterStep)
}
}
}
}
}
extension BasePassCodeViewModel {
private func set(passCode: String) {
passCodeHolder.add(passCode: passCode)
validateIfNeeded()
if shouldUpdateControllerState {
passCodeControllerStateVariable.accept(passCodeHolder.enterStep)
}
}
private var shouldUpdateControllerState: Bool {
!passCodeHolder.shouldValidate ||
!(validationResultHolder.value?.isValid ?? true) ||
validationResultHolder.value?.error?.isTooManyAttempts ?? false
}
private func validateIfNeeded() {
guard passCodeHolder.shouldValidate else {
return
}
switch passCodeHolder.type {
case .create where passCodeHolder.enterStep == .repeatEnter:
attemptsNumber += 1
case .change where passCodeHolder.enterStep == .repeatEnter:
attemptsNumber += 1
case .enter:
attemptsNumber += 1
default:
break
}
var validationResult = passCodeHolder.validate()
// if entered (in .enter mode) code is invalid -> .wrongCode
if passCodeHolder.type == .enter,
let passCode = validationResult.passCode,
!isEnteredPassCodeValid(passCode) {
let remainingAttemptsCount = passCodeConfiguration.maxAttemptsNumber - attemptsNumber
validationResult = .invalid(.wrongCode(attemptsRemaining: remainingAttemptsCount))
}
// if entered code (in any mode) is mismatched too many times -> .tooManyAttempts
if (!validationResult.isValid && attemptsNumber == passCodeConfiguration.maxAttemptsNumber) ||
attemptsNumber > passCodeConfiguration.maxAttemptsNumber {
validationResult = .invalid(.tooManyAttempts(type: operationType))
}
if !validationResult.isValid {
passCodeHolder.reset()
}
validationResultHolder.accept(validationResult)
}
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Touch Instinct
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
@ -20,25 +20,19 @@
// THE SOFTWARE.
//
import ObjectMapper
import RxSwift
public class ApiResponse: ApiResponseProtocol, ImmutableMappable {
public extension BaseTextFieldViewEvents {
public let result: Any?
public let errorCode: Int
public let errorMessage: String?
/// Convenience init with UITextField as textChangedDriver.
///
/// - Parameter textField: UITextField to use for text events.
convenience init(textField: UITextField) {
let textChangedDriver = textField.rx
.text
.asDriver()
.distinctUntilChanged { $0 == $1 }
public required init(map: Map) throws {
result = try? map.value("result")
errorCode = try map.value("error_code")
errorMessage = try? map.value("error_message")
self.init(textChangedDriver: textChangedDriver)
}
}
public protocol ApiResponseProtocol: ImmutableMappable {
var errorCode: Int { get }
var errorMessage: String? { get }
}

View File

@ -0,0 +1,20 @@
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
}
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Touch Instinct
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
@ -22,12 +22,12 @@
import UIKit
extension UIBarButtonItem {
public extension UIBarButtonItem {
public static var activityIndicator: (barButton: UIBarButtonItem, activityIndicator: UIActivityIndicatorView) {
let indicatorView = UIActivityIndicatorView(activityIndicatorStyle: .white)
/// Creates activity indicator view and bar button item (based on activity indicator)
static var activityIndicator: (barButton: UIBarButtonItem, activityIndicator: UIActivityIndicatorView) {
let indicatorView = UIActivityIndicatorView(style: .white)
let indicatorBar = UIBarButtonItem(customView: indicatorView)
return (indicatorBar, indicatorView)
}
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Touch Instinct
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
@ -22,29 +22,30 @@
import Foundation
fileprivate enum Keys {
private enum Keys {
static let sessionId = "sessionId"
static let userLogin = "userLogin"
}
public extension UserDefaults {
public var sessionId: String? {
/// Default place to store session id
var sessionId: String? {
get {
return string(forKey: Keys.sessionId)
string(forKey: Keys.sessionId)
}
set {
set(newValue, forKey: Keys.sessionId)
}
}
public var userLogin: String? {
/// Default place to store userLogin
var userLogin: String? {
get {
return string(forKey: Keys.userLogin)
string(forKey: Keys.userLogin)
}
set {
set(newValue, forKey: Keys.userLogin)
}
}
}

View File

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

24
Sources/Info-iOS.plist Normal file
View File

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

View File

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

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Touch Instinct
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
@ -22,8 +22,12 @@
import Foundation
public enum ConnectionError: LocalizedError {
/// Prototol with two fields that describes result of online validation.
public protocol OnlineValidateable {
case noConnection
/// Contains true if online validation did passed.
var isValid: Bool { get }
/// May contain an error if validation did failed.
var error: Error? { get }
}

View File

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

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Touch Instinct
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
@ -21,14 +21,15 @@
//
import RxSwift
import LeadKit
/// Represents service that store basic user information
open class BaseUserService {
public init() {
// Can be overrided
}
/// Returns user login
open var userLogin: String {
guard let defaultsLogin = UserDefaults.standard.userLogin else {
assertionFailure("userLogin is nil. Use isLoggedIn before read userLogin")
@ -38,6 +39,7 @@ open class BaseUserService {
return defaultsLogin
}
/// Returns session id
open var sessionId: String {
guard let defaultsSessionId = UserDefaults.standard.sessionId else {
assertionFailure("sessionId is nil. Use isLoggedIn before read sessionId")
@ -46,14 +48,14 @@ open class BaseUserService {
return defaultsSessionId
}
/// Indicates if user is logged in
open var isLoggedIn: Bool {
return UserDefaults.standard.sessionId != nil
UserDefaults.standard.sessionId != nil
}
/// Reset user information
open class func clearData() {
UserDefaults.standard.sessionId = nil
UserDefaults.standard.userLogin = nil
UserDefaults.standard.synchronize()
}
}

View File

@ -0,0 +1,122 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import LocalAuthentication
public typealias BiometricsAuthHandler = (Bool, Error?) -> Void
/// Service that provide access to authentication via biometric
public final class BiometricsService {
public enum BiometryType {
case faceID
case touchID
case none
}
private lazy var laContext = LAContext()
/// If the user unlocks the device using biometrics within the specified time interval,
/// then authentication for the receiver succeeds automatically, without prompting the user for biometrics.
/// Works only after device unlock event, no other apps authentications counts.
public var allowableReuseDuration: TimeInterval? {
didSet {
guard let duration = allowableReuseDuration else {
return
}
if #available(iOS 9.0, *) {
laContext.touchIDAuthenticationAllowableReuseDuration = duration
}
}
}
/// Returns BiometryType supporting by device: TouchID, FaceID or none
public var biometryType: BiometryType {
let canEvaluate = canAuthenticateWithBiometrics
if #available(iOS 11.0, *) {
switch laContext.biometryType {
case .touchID:
return .touchID
case .faceID:
return .faceID
case .none:
return .none
@unknown default:
return .none
}
}
return canEvaluate ? .touchID : .none
}
/// Returns current domain state
public var evaluatedPolicyDomainState: Data? {
// We need to call canEvaluatePolicy function for evaluatedPolicyDomainState to be updated
_ = canAuthenticateWithBiometrics
return laContext.evaluatedPolicyDomainState
}
/// Indicates is it possible to authenticate on this device via any biometric.
/// Returns false if not enrolled or lockedout.
public var canAuthenticateWithBiometrics: Bool {
laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
}
/**
Initiates system biometrics authentication process.
Once evaluated, will return success until the context is deallocated.
Call "clear" to use a new context instance.
- parameters:
- description: prompt on the system alert
- fallback: alternative action button title on system alert
- cancel: cancel button title on the system alert
- authHandler: callback, with parameter, indicates if user authenticate successfuly
*/
public func authenticateWithBiometrics(with description: String,
fallback fallbackTitle: String?,
cancel cancelTitle: String?,
authHandler: @escaping BiometricsAuthHandler) {
if #available(iOS 10.0, *) {
laContext.localizedCancelTitle = cancelTitle
}
laContext.localizedFallbackTitle = fallbackTitle
laContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: description) { success, error in
authHandler(success, error)
}
}
/// Replace old instance of the context with the new one
public func clear() {
laContext = LAContext()
}
public init() {}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,109 @@
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
build-scripts Submodule

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

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

14
run_swiftlint.sh Normal file
View File

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