From 5fd05fd061042c0eec6a590b1aec94f4a5d6624a Mon Sep 17 00:00:00 2001 From: Alexey Gerasimov Date: Tue, 25 Apr 2017 14:59:28 +0300 Subject: [PATCH 1/9] Structure fixed --- .../project.pbxproj | 80 +++++++++---------- LeadKitAdditions/Podfile.lock | 2 +- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj index 16b5d2e..8536952 100644 --- a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj +++ b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj @@ -9,14 +9,14 @@ /* 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, ); }; }; - CAE698F11E968BA6000394B0 /* BaseDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE698EF1E968BA6000394B0 /* BaseDateFormatter.swift */; }; - CAE698F61E968E3D000394B0 /* BaseUserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE698F41E968E3D000394B0 /* BaseUserService.swift */; }; - CAE698FB1E968F78000394B0 /* UserDefaults+UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE698F91E968F78000394B0 /* UserDefaults+UserService.swift */; }; - CAE698FF1E969352000394B0 /* ApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE698FD1E969352000394B0 /* ApiResponse.swift */; }; - CAE699041E9693F4000394B0 /* ConnectionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE699021E9693F4000394B0 /* ConnectionError.swift */; }; - CAE699081E96956D000394B0 /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE699061E96956D000394B0 /* ApiError.swift */; }; - CAE6990E1E969C66000394B0 /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE6990C1E969C66000394B0 /* Observable+Extensions.swift */; }; - CAE699121E969D2B000394B0 /* DefaultNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE699101E969D2B000394B0 /* DefaultNetworkService.swift */; }; + 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 */; }; + EF05EDC21EAF706200CAE7B6 /* DefaultNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -26,14 +26,14 @@ 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 = ""; }; CAE698E71E968820000394B0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - CAE698EF1E968BA6000394B0 /* BaseDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BaseDateFormatter.swift; path = Classes/BaseDateFormatter.swift; sourceTree = ""; }; - CAE698F41E968E3D000394B0 /* BaseUserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BaseUserService.swift; path = Services/BaseUserService.swift; sourceTree = ""; }; - CAE698F91E968F78000394B0 /* UserDefaults+UserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UserDefaults+UserService.swift"; path = "Extensions/UserDefaults+UserService.swift"; sourceTree = ""; }; - CAE698FD1E969352000394B0 /* ApiResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ApiResponse.swift; path = Classes/ApiResponse.swift; sourceTree = ""; }; - CAE699021E9693F4000394B0 /* ConnectionError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConnectionError.swift; path = Enums/ConnectionError.swift; sourceTree = ""; }; - CAE699061E96956D000394B0 /* ApiError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ApiError.swift; path = Enums/ApiError.swift; sourceTree = ""; }; - CAE6990C1E969C66000394B0 /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Observable+Extensions.swift"; path = "Extensions/Observable+Extensions.swift"; sourceTree = ""; }; - CAE699101E969D2B000394B0 /* DefaultNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DefaultNetworkService.swift; path = Classes/DefaultNetworkService.swift; sourceTree = ""; }; + EF05EDB31EAF703A00CAE7B6 /* BaseUserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseUserService.swift; sourceTree = ""; }; + EF05EDB51EAF704800CAE7B6 /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Extensions.swift"; sourceTree = ""; }; + EF05EDB61EAF704800CAE7B6 /* UserDefaults+UserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+UserService.swift"; sourceTree = ""; }; + EF05EDB91EAF705500CAE7B6 /* ApiError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiError.swift; sourceTree = ""; }; + EF05EDBA1EAF705500CAE7B6 /* ConnectionError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionError.swift; sourceTree = ""; }; + EF05EDBD1EAF706200CAE7B6 /* ApiResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiResponse.swift; sourceTree = ""; }; + EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDateFormatter.swift; sourceTree = ""; }; + EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkService.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -77,12 +77,12 @@ CAE698E51E968820000394B0 /* LeadKitAdditions */ = { isa = PBXGroup; children = ( + CAE698EE1E968B72000394B0 /* Classes */, CAE699011E9693DE000394B0 /* Enums */, CAE698F81E968F56000394B0 /* Extensions */, - CAE698F31E968E28000394B0 /* Services */, - CAE698EE1E968B72000394B0 /* Classes */, - CAE698E61E968820000394B0 /* LeadKitAdditions.h */, CAE698E71E968820000394B0 /* Info.plist */, + CAE698E61E968820000394B0 /* LeadKitAdditions.h */, + CAE698F31E968E28000394B0 /* Services */, ); path = LeadKitAdditions; sourceTree = ""; @@ -90,37 +90,37 @@ CAE698EE1E968B72000394B0 /* Classes */ = { isa = PBXGroup; children = ( - CAE698EF1E968BA6000394B0 /* BaseDateFormatter.swift */, - CAE698FD1E969352000394B0 /* ApiResponse.swift */, - CAE699101E969D2B000394B0 /* DefaultNetworkService.swift */, + EF05EDBD1EAF706200CAE7B6 /* ApiResponse.swift */, + EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */, + EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */, ); - name = Classes; + path = Classes; sourceTree = ""; }; CAE698F31E968E28000394B0 /* Services */ = { isa = PBXGroup; children = ( - CAE698F41E968E3D000394B0 /* BaseUserService.swift */, + EF05EDB31EAF703A00CAE7B6 /* BaseUserService.swift */, ); - name = Services; + path = Services; sourceTree = ""; }; CAE698F81E968F56000394B0 /* Extensions */ = { isa = PBXGroup; children = ( - CAE698F91E968F78000394B0 /* UserDefaults+UserService.swift */, - CAE6990C1E969C66000394B0 /* Observable+Extensions.swift */, + EF05EDB51EAF704800CAE7B6 /* Observable+Extensions.swift */, + EF05EDB61EAF704800CAE7B6 /* UserDefaults+UserService.swift */, ); - name = Extensions; + path = Extensions; sourceTree = ""; }; CAE699011E9693DE000394B0 /* Enums */ = { isa = PBXGroup; children = ( - CAE699021E9693F4000394B0 /* ConnectionError.swift */, - CAE699061E96956D000394B0 /* ApiError.swift */, + EF05EDB91EAF705500CAE7B6 /* ApiError.swift */, + EF05EDBA1EAF705500CAE7B6 /* ConnectionError.swift */, ); - name = Enums; + path = Enums; sourceTree = ""; }; F8A65FEC7C0EB4B93746E50F /* Pods */ = { @@ -267,7 +267,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../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"; + 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 */ @@ -277,14 +277,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - CAE698FB1E968F78000394B0 /* UserDefaults+UserService.swift in Sources */, - CAE698FF1E969352000394B0 /* ApiResponse.swift in Sources */, - CAE698F11E968BA6000394B0 /* BaseDateFormatter.swift in Sources */, - CAE699041E9693F4000394B0 /* ConnectionError.swift in Sources */, - CAE6990E1E969C66000394B0 /* Observable+Extensions.swift in Sources */, - CAE698F61E968E3D000394B0 /* BaseUserService.swift in Sources */, - CAE699121E969D2B000394B0 /* DefaultNetworkService.swift in Sources */, - CAE699081E96956D000394B0 /* ApiError.swift in Sources */, + EF05EDB81EAF704800CAE7B6 /* UserDefaults+UserService.swift in Sources */, + EF05EDC21EAF706200CAE7B6 /* DefaultNetworkService.swift in Sources */, + EF05EDBB1EAF705500CAE7B6 /* ApiError.swift in Sources */, + EF05EDB71EAF704800CAE7B6 /* Observable+Extensions.swift in Sources */, + EF05EDC01EAF706200CAE7B6 /* ApiResponse.swift in Sources */, + EF05EDBC1EAF705500CAE7B6 /* ConnectionError.swift in Sources */, + EF05EDB41EAF703A00CAE7B6 /* BaseUserService.swift in Sources */, + EF05EDC11EAF706200CAE7B6 /* BaseDateFormatter.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/LeadKitAdditions/Podfile.lock b/LeadKitAdditions/Podfile.lock index c0e2948..dcb0d7d 100644 --- a/LeadKitAdditions/Podfile.lock +++ b/LeadKitAdditions/Podfile.lock @@ -50,4 +50,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: ce4fe179e6470b751617d19baacbce25502a7002 -COCOAPODS: 1.2.0 +COCOAPODS: 1.2.1 From 6f1ec15d4858bfe7841e113345ae2dc777a077f6 Mon Sep 17 00:00:00 2001 From: Alexey Gerasimov Date: Tue, 25 Apr 2017 15:00:29 +0300 Subject: [PATCH 2/9] TouchID service added --- .../project.pbxproj | 4 ++ .../Services/TouchIDService.swift | 45 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 LeadKitAdditions/LeadKitAdditions/Services/TouchIDService.swift diff --git a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj index 8536952..8fc15cd 100644 --- a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj +++ b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ EF05EDC01EAF706200CAE7B6 /* ApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBD1EAF706200CAE7B6 /* ApiResponse.swift */; }; EF05EDC11EAF706200CAE7B6 /* BaseDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */; }; EF05EDC21EAF706200CAE7B6 /* DefaultNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */; }; + EF05EDC61EAF70EB00CAE7B6 /* TouchIDService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -34,6 +35,7 @@ EF05EDBD1EAF706200CAE7B6 /* ApiResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiResponse.swift; sourceTree = ""; }; EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDateFormatter.swift; sourceTree = ""; }; EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkService.swift; sourceTree = ""; }; + EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchIDService.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -101,6 +103,7 @@ isa = PBXGroup; children = ( EF05EDB31EAF703A00CAE7B6 /* BaseUserService.swift */, + EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */, ); path = Services; sourceTree = ""; @@ -278,6 +281,7 @@ buildActionMask = 2147483647; files = ( EF05EDB81EAF704800CAE7B6 /* UserDefaults+UserService.swift in Sources */, + EF05EDC61EAF70EB00CAE7B6 /* TouchIDService.swift in Sources */, EF05EDC21EAF706200CAE7B6 /* DefaultNetworkService.swift in Sources */, EF05EDBB1EAF705500CAE7B6 /* ApiError.swift in Sources */, EF05EDB71EAF704800CAE7B6 /* Observable+Extensions.swift in Sources */, diff --git a/LeadKitAdditions/LeadKitAdditions/Services/TouchIDService.swift b/LeadKitAdditions/LeadKitAdditions/Services/TouchIDService.swift new file mode 100644 index 0000000..6eaa96e --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Services/TouchIDService.swift @@ -0,0 +1,45 @@ +// +// 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 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) + } + } + +} From 6efc671aea231b83b672e4d2fd72da7db0bca847 Mon Sep 17 00:00:00 2001 From: Alexey Gerasimov Date: Tue, 25 Apr 2017 17:59:41 +0300 Subject: [PATCH 3/9] BasePassCodeService added --- .../project.pbxproj | 4 + .../Services/BasePassCodeService.swift | 99 +++++++++++++++++++ LeadKitAdditions/Podfile | 1 + LeadKitAdditions/Podfile.lock | 5 +- 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 LeadKitAdditions/LeadKitAdditions/Services/BasePassCodeService.swift diff --git a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj index 8fc15cd..c0835e2 100644 --- a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj +++ b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ EF05EDC11EAF706200CAE7B6 /* BaseDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */; }; EF05EDC21EAF706200CAE7B6 /* DefaultNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */; }; EF05EDC61EAF70EB00CAE7B6 /* TouchIDService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */; }; + EF05EDC81EAF91D500CAE7B6 /* BasePassCodeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -36,6 +37,7 @@ EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDateFormatter.swift; sourceTree = ""; }; EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkService.swift; sourceTree = ""; }; EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchIDService.swift; sourceTree = ""; }; + EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeService.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -102,6 +104,7 @@ CAE698F31E968E28000394B0 /* Services */ = { isa = PBXGroup; children = ( + EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */, EF05EDB31EAF703A00CAE7B6 /* BaseUserService.swift */, EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */, ); @@ -288,6 +291,7 @@ EF05EDC01EAF706200CAE7B6 /* ApiResponse.swift in Sources */, EF05EDBC1EAF705500CAE7B6 /* ConnectionError.swift in Sources */, EF05EDB41EAF703A00CAE7B6 /* BaseUserService.swift in Sources */, + EF05EDC81EAF91D500CAE7B6 /* BasePassCodeService.swift in Sources */, EF05EDC11EAF706200CAE7B6 /* BaseDateFormatter.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/LeadKitAdditions/LeadKitAdditions/Services/BasePassCodeService.swift b/LeadKitAdditions/LeadKitAdditions/Services/BasePassCodeService.swift new file mode 100644 index 0000000..6bec7f9 --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Services/BasePassCodeService.swift @@ -0,0 +1,99 @@ +// +// 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 KeychainAccess +import CocoaLumberjack + +open class BasePassCodeService { + + open class var keychainService: String { + return Bundle.main.bundleIdentifier ?? "" + } + + public init() { + let isInitialLoad = UserDefaults.standard.bool(forKey: Keys.isInitialLoad) + if isInitialLoad { + UserDefaults.standard.set(false, forKey: Keys.isInitialLoad) + reset() + } + } + + // MARK: - Private stuff + + fileprivate lazy var keychain: Keychain = { + return Keychain(service: keychainService) + .synchronizable(false) + }() + + fileprivate var passCodeHash: String? { + return keychain[Keys.passCodeHash] + } + + fileprivate enum Keys { + static let passCodeHash = "passCodeHash" + static let isTouchIdEnabled = "isTouchIdEnabled" + static let isInitialLoad = "isInitialLoad" + } + + fileprivate enum Values { + static let touchIdEnabled = "touchIdEnabled" + } + +} + +extension BasePassCodeService { + + public var isPassCodeSaved: Bool { + return keychain[Keys.passCodeHash] != nil + } + + public var isTouchIdEnabled: Bool { + get { + return keychain[Keys.isTouchIdEnabled] == Values.touchIdEnabled + } + set { + keychain[Keys.isTouchIdEnabled] = newValue ? Values.touchIdEnabled : nil + } + } + + public func save(passCode: String?) { + keychain[Keys.passCodeHash] = passCode?.hashMD5 + } + + public func check(passCode: String) -> Bool { + return passCode.hashMD5 == passCodeHash + } + + public func reset() { + save(passCode: nil) + isTouchIdEnabled = false + } + +} + +private extension String { + + var hashMD5: String? { + return self + } + +} diff --git a/LeadKitAdditions/Podfile b/LeadKitAdditions/Podfile index 2629275..05844c4 100644 --- a/LeadKitAdditions/Podfile +++ b/LeadKitAdditions/Podfile @@ -8,6 +8,7 @@ target 'LeadKitAdditions' do pod 'LeadKit', :git => 'https://github.com/TouchInstinct/LeadKit.git', :branch => 'fix/sharedApplication', :commit => 'fd0eb18b8a6680ff16bbb1668d1ae0d29f29fad7' pod 'TableKit' + pod 'KeychainAccess' end diff --git a/LeadKitAdditions/Podfile.lock b/LeadKitAdditions/Podfile.lock index dcb0d7d..343610a 100644 --- a/LeadKitAdditions/Podfile.lock +++ b/LeadKitAdditions/Podfile.lock @@ -3,6 +3,7 @@ PODS: - CocoaLumberjack/Default (3.1.0) - CocoaLumberjack/Swift (3.1.0): - CocoaLumberjack/Default + - KeychainAccess (3.0.2) - LeadKit (0.4.6): - CocoaLumberjack/Swift (~> 3.1.0) - ObjectMapper (~> 2.1) @@ -23,6 +24,7 @@ PODS: - Toast-Swift (2.0.0) DEPENDENCIES: + - KeychainAccess - LeadKit (from `https://github.com/TouchInstinct/LeadKit.git`, commit `fd0eb18b8a6680ff16bbb1668d1ae0d29f29fad7`, branch `fix/sharedApplication`) - TableKit @@ -40,6 +42,7 @@ CHECKOUT OPTIONS: SPEC CHECKSUMS: Alamofire: dc44b1600b800eb63da6a19039a0083d62a6a62d CocoaLumberjack: 8311463ddf9ee86a06ef92a071dd656c89244500 + KeychainAccess: a986406022dfc7c634c691ad3bec670cc6a32002 LeadKit: d688a8bef79de7bbd83d553da3cb6c5292d48f2d ObjectMapper: fb30f71e08470d1e5a20b199fafe1246281db898 RxAlamofire: 0b1fa48f545fffe7f7a28af2086bcaa3b5946cc9 @@ -48,6 +51,6 @@ SPEC CHECKSUMS: TableKit: 02e041b443f75fa3e9f1ee6024d4b256305bd904 Toast-Swift: 5b2f8f720f7e78e48511f693df1f9c9a6e38a25a -PODFILE CHECKSUM: ce4fe179e6470b751617d19baacbce25502a7002 +PODFILE CHECKSUM: f6bfca600b479c264f39b85593af2629edff3515 COCOAPODS: 1.2.1 From f5ea2895cdaa045f5ee443040e0ff3238c2bf1d9 Mon Sep 17 00:00:00 2001 From: Alexey Gerasimov Date: Tue, 25 Apr 2017 18:18:09 +0300 Subject: [PATCH 4/9] CommonCrypto framework added --- LeadKitAdditions/CommonCrypto/CommonCrypto.h | 19 +++ .../CommonCrypto/CommonCrypto.xcconfig | 10 ++ LeadKitAdditions/CommonCrypto/Info.plist | 24 ++++ .../CommonCrypto/iphoneos.modulemap | 4 + .../CommonCrypto/iphonesimulator.modulemap | 4 + .../project.pbxproj | 129 ++++++++++++++++++ .../Services/BasePassCodeService.swift | 29 +++- 7 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 LeadKitAdditions/CommonCrypto/CommonCrypto.h create mode 100644 LeadKitAdditions/CommonCrypto/CommonCrypto.xcconfig create mode 100644 LeadKitAdditions/CommonCrypto/Info.plist create mode 100755 LeadKitAdditions/CommonCrypto/iphoneos.modulemap create mode 100755 LeadKitAdditions/CommonCrypto/iphonesimulator.modulemap diff --git a/LeadKitAdditions/CommonCrypto/CommonCrypto.h b/LeadKitAdditions/CommonCrypto/CommonCrypto.h new file mode 100644 index 0000000..def2316 --- /dev/null +++ b/LeadKitAdditions/CommonCrypto/CommonCrypto.h @@ -0,0 +1,19 @@ +// +// CommonCrypto.h +// CommonCrypto +// +// Created by Alexey Gerasimov on 25/04/2017. +// Copyright © 2017 TouchInstinct. All rights reserved. +// + +#import + +//! Project version number for CommonCrypto. +FOUNDATION_EXPORT double CommonCryptoVersionNumber; + +//! Project version string for CommonCrypto. +FOUNDATION_EXPORT const unsigned char CommonCryptoVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/LeadKitAdditions/CommonCrypto/CommonCrypto.xcconfig b/LeadKitAdditions/CommonCrypto/CommonCrypto.xcconfig new file mode 100644 index 0000000..080c4d8 --- /dev/null +++ b/LeadKitAdditions/CommonCrypto/CommonCrypto.xcconfig @@ -0,0 +1,10 @@ +// +// CommonCrypto.xcconfig +// Chat +// +// Created by Alexey Gerasimov on 17/08/16. +// Copyright © 2016 Touch Instinct. All rights reserved. +// + +MODULEMAP_FILE[sdk=iphoneos*] = $(SRCROOT)/CommonCrypto/iphoneos.modulemap +MODULEMAP_FILE[sdk=iphonesimulator*] = $(SRCROOT)/CommonCrypto/iphonesimulator.modulemap \ No newline at end of file diff --git a/LeadKitAdditions/CommonCrypto/Info.plist b/LeadKitAdditions/CommonCrypto/Info.plist new file mode 100644 index 0000000..fbe1e6b --- /dev/null +++ b/LeadKitAdditions/CommonCrypto/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/LeadKitAdditions/CommonCrypto/iphoneos.modulemap b/LeadKitAdditions/CommonCrypto/iphoneos.modulemap new file mode 100755 index 0000000..125b772 --- /dev/null +++ b/LeadKitAdditions/CommonCrypto/iphoneos.modulemap @@ -0,0 +1,4 @@ +module CommonCrypto [system] { + header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/CommonCrypto/CommonCrypto.h" + export * +} diff --git a/LeadKitAdditions/CommonCrypto/iphonesimulator.modulemap b/LeadKitAdditions/CommonCrypto/iphonesimulator.modulemap new file mode 100755 index 0000000..835d686 --- /dev/null +++ b/LeadKitAdditions/CommonCrypto/iphonesimulator.modulemap @@ -0,0 +1,4 @@ +module CommonCrypto [system] { + header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/CommonCrypto/CommonCrypto.h" + export * +} diff --git a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj index c0835e2..e6bbb41 100644 --- a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj +++ b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj @@ -19,6 +19,8 @@ EF05EDC21EAF706200CAE7B6 /* DefaultNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */; }; EF05EDC61EAF70EB00CAE7B6 /* TouchIDService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */; }; EF05EDC81EAF91D500CAE7B6 /* BasePassCodeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */; }; + EF05EDD21EAF9CF100CAE7B6 /* CommonCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = EF05EDD01EAF9CF100CAE7B6 /* CommonCrypto.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EF05EDDA1EAF9D4A00CAE7B6 /* CommonCrypto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF05EDCE1EAF9CF100CAE7B6 /* CommonCrypto.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -38,6 +40,12 @@ EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkService.swift; sourceTree = ""; }; EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchIDService.swift; sourceTree = ""; }; EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeService.swift; sourceTree = ""; }; + EF05EDCE1EAF9CF100CAE7B6 /* CommonCrypto.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CommonCrypto.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EF05EDD01EAF9CF100CAE7B6 /* CommonCrypto.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CommonCrypto.h; sourceTree = ""; }; + EF05EDD11EAF9CF100CAE7B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + EF05EDD61EAF9D2900CAE7B6 /* CommonCrypto.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = CommonCrypto.xcconfig; sourceTree = ""; }; + EF05EDD71EAF9D2900CAE7B6 /* iphoneos.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = iphoneos.modulemap; sourceTree = ""; }; + EF05EDD81EAF9D2900CAE7B6 /* iphonesimulator.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = iphonesimulator.modulemap; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -45,10 +53,18 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + EF05EDDA1EAF9D4A00CAE7B6 /* CommonCrypto.framework in Frameworks */, 7A94B4A5956B82BE1CEBA873 /* Pods_LeadKitAdditions.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; + EF05EDCA1EAF9CF100CAE7B6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -64,6 +80,7 @@ isa = PBXGroup; children = ( CAE698E51E968820000394B0 /* LeadKitAdditions */, + EF05EDCF1EAF9CF100CAE7B6 /* CommonCrypto */, CAE698E41E968820000394B0 /* Products */, F8A65FEC7C0EB4B93746E50F /* Pods */, A3117951840B8B7D2E7A8A80 /* Frameworks */, @@ -74,6 +91,7 @@ isa = PBXGroup; children = ( CAE698E31E968820000394B0 /* LeadKitAdditions.framework */, + EF05EDCE1EAF9CF100CAE7B6 /* CommonCrypto.framework */, ); name = Products; sourceTree = ""; @@ -129,6 +147,18 @@ path = Enums; sourceTree = ""; }; + EF05EDCF1EAF9CF100CAE7B6 /* CommonCrypto */ = { + isa = PBXGroup; + children = ( + EF05EDD01EAF9CF100CAE7B6 /* CommonCrypto.h */, + EF05EDD61EAF9D2900CAE7B6 /* CommonCrypto.xcconfig */, + EF05EDD11EAF9CF100CAE7B6 /* Info.plist */, + EF05EDD71EAF9D2900CAE7B6 /* iphoneos.modulemap */, + EF05EDD81EAF9D2900CAE7B6 /* iphonesimulator.modulemap */, + ); + path = CommonCrypto; + sourceTree = ""; + }; F8A65FEC7C0EB4B93746E50F /* Pods */ = { isa = PBXGroup; children = ( @@ -149,6 +179,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EF05EDCB1EAF9CF100CAE7B6 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + EF05EDD21EAF9CF100CAE7B6 /* CommonCrypto.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -174,6 +212,24 @@ productReference = CAE698E31E968820000394B0 /* LeadKitAdditions.framework */; productType = "com.apple.product-type.framework"; }; + EF05EDCD1EAF9CF100CAE7B6 /* CommonCrypto */ = { + isa = PBXNativeTarget; + buildConfigurationList = EF05EDD31EAF9CF100CAE7B6 /* Build configuration list for PBXNativeTarget "CommonCrypto" */; + buildPhases = ( + EF05EDC91EAF9CF100CAE7B6 /* Sources */, + EF05EDCA1EAF9CF100CAE7B6 /* Frameworks */, + EF05EDCB1EAF9CF100CAE7B6 /* Headers */, + EF05EDCC1EAF9CF100CAE7B6 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CommonCrypto; + productName = CommonCrypto; + productReference = EF05EDCE1EAF9CF100CAE7B6 /* CommonCrypto.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -188,6 +244,10 @@ LastSwiftMigration = 0830; ProvisioningStyle = Manual; }; + EF05EDCD1EAF9CF100CAE7B6 = { + CreatedOnToolsVersion = 8.3.1; + ProvisioningStyle = Manual; + }; }; }; buildConfigurationList = CAE698DD1E968820000394B0 /* Build configuration list for PBXProject "LeadKitAdditions" */; @@ -203,6 +263,7 @@ projectRoot = ""; targets = ( CAE698E21E968820000394B0 /* LeadKitAdditions */, + EF05EDCD1EAF9CF100CAE7B6 /* CommonCrypto */, ); }; /* End PBXProject section */ @@ -215,6 +276,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EF05EDCC1EAF9CF100CAE7B6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -296,6 +364,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EF05EDC91EAF9CF100CAE7B6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -449,6 +524,52 @@ }; name = Release; }; + EF05EDD41EAF9CF100CAE7B6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = CommonCrypto/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + "MODULEMAP_FILE[sdk=iphoneos*]" = /Users/alexeygerasimov/Documents/dev/LeadKitAdditions/LeadKitAdditions/CommonCrypto/iphoneos.modulemap; + "MODULEMAP_FILE[sdk=iphonesimulator*]" = /Users/alexeygerasimov/Documents/dev/LeadKitAdditions/LeadKitAdditions/CommonCrypto/iphonesimulator.modulemap; + PRODUCT_BUNDLE_IDENTIFIER = com.touchin.CommonCrypto; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + EF05EDD51EAF9CF100CAE7B6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = CommonCrypto/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + "MODULEMAP_FILE[sdk=iphoneos*]" = /Users/alexeygerasimov/Documents/dev/LeadKitAdditions/LeadKitAdditions/CommonCrypto/iphoneos.modulemap; + "MODULEMAP_FILE[sdk=iphonesimulator*]" = /Users/alexeygerasimov/Documents/dev/LeadKitAdditions/LeadKitAdditions/CommonCrypto/iphonesimulator.modulemap; + PRODUCT_BUNDLE_IDENTIFIER = com.touchin.CommonCrypto; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -470,6 +591,14 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + EF05EDD31EAF9CF100CAE7B6 /* Build configuration list for PBXNativeTarget "CommonCrypto" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EF05EDD41EAF9CF100CAE7B6 /* Debug */, + EF05EDD51EAF9CF100CAE7B6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; /* End XCConfigurationList section */ }; rootObject = CAE698DA1E968820000394B0 /* Project object */; diff --git a/LeadKitAdditions/LeadKitAdditions/Services/BasePassCodeService.swift b/LeadKitAdditions/LeadKitAdditions/Services/BasePassCodeService.swift index 6bec7f9..f80b71d 100644 --- a/LeadKitAdditions/LeadKitAdditions/Services/BasePassCodeService.swift +++ b/LeadKitAdditions/LeadKitAdditions/Services/BasePassCodeService.swift @@ -22,6 +22,7 @@ import KeychainAccess import CocoaLumberjack +import CommonCrypto open class BasePassCodeService { @@ -76,11 +77,15 @@ extension BasePassCodeService { } public func save(passCode: String?) { - keychain[Keys.passCodeHash] = passCode?.hashMD5 + if let passCode = passCode { + keychain[Keys.passCodeHash] = sha256(passCode) + } else { + keychain[Keys.passCodeHash] = nil + } } public func check(passCode: String) -> Bool { - return passCode.hashMD5 == passCodeHash + return sha256(passCode) == passCodeHash } public func reset() { @@ -90,10 +95,24 @@ extension BasePassCodeService { } -private extension String { +private extension BasePassCodeService { - var hashMD5: String? { - return self + func sha256(_ str: String) -> String? { + guard let data = str.data(using: String.Encoding.utf8), let shaData = sha256(data) else { + return nil + } + + let rc = shaData.base64EncodedString(options: []) + return rc + } + + func sha256(_ data: Data) -> Data? { + guard let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH)) else { + return nil + } + + CC_SHA256((data as NSData).bytes, CC_LONG(data.count), res.mutableBytes.assumingMemoryBound(to: UInt8.self)) + return res as Data } } From fabd2c30398891c3bf7cdae54c5dcb79fe6330ff Mon Sep 17 00:00:00 2001 From: Alexey Gerasimov Date: Tue, 25 Apr 2017 20:03:44 +0300 Subject: [PATCH 5/9] PassCode controller added --- LeadKitAdditions.podspec | 2 +- .../project.pbxproj | 68 +++++ .../Model/PassCodeConfiguration.swift | 45 ++++ .../PassCode/Model/PassCodeError.swift | 27 ++ .../PassCode/Model/PassCodeHolder.swift | 116 +++++++++ .../Model/PassCodeHolderProtocol.swift | 56 ++++ .../Model/PassCodeValidationResult.swift | 55 ++++ .../View/BasePassCodeViewController.swift | 239 ++++++++++++++++++ .../ViewModel/BasePassCodeViewModel.swift | 172 +++++++++++++ 9 files changed, 779 insertions(+), 1 deletion(-) create mode 100644 LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeConfiguration.swift create mode 100644 LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeError.swift create mode 100644 LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeHolder.swift create mode 100644 LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeHolderProtocol.swift create mode 100644 LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeValidationResult.swift create mode 100644 LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/View/BasePassCodeViewController.swift create mode 100644 LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift diff --git a/LeadKitAdditions.podspec b/LeadKitAdditions.podspec index a882fff..b9c36ee 100644 --- a/LeadKitAdditions.podspec +++ b/LeadKitAdditions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKitAdditions" - s.version = "0.0.3" + s.version = "0.0.4" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/NikAshanin/LeadKitAdditions" s.license = "Apache License, Version 2.0" diff --git a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj index e6bbb41..b0dcbd6 100644 --- a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj +++ b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj @@ -21,6 +21,13 @@ EF05EDC81EAF91D500CAE7B6 /* BasePassCodeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */; }; EF05EDD21EAF9CF100CAE7B6 /* CommonCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = EF05EDD01EAF9CF100CAE7B6 /* CommonCrypto.h */; settings = {ATTRIBUTES = (Public, ); }; }; EF05EDDA1EAF9D4A00CAE7B6 /* CommonCrypto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF05EDCE1EAF9CF100CAE7B6 /* CommonCrypto.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 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 */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -46,6 +53,13 @@ EF05EDD61EAF9D2900CAE7B6 /* CommonCrypto.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = CommonCrypto.xcconfig; sourceTree = ""; }; EF05EDD71EAF9D2900CAE7B6 /* iphoneos.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = iphoneos.modulemap; sourceTree = ""; }; EF05EDD81EAF9D2900CAE7B6 /* iphonesimulator.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = iphonesimulator.modulemap; sourceTree = ""; }; + EF05EDE01EAFA74200CAE7B6 /* BasePassCodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeViewController.swift; sourceTree = ""; }; + EF05EDE21EAFA7A600CAE7B6 /* BasePassCodeViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeViewModel.swift; sourceTree = ""; }; + EF05EDE41EAFA80D00CAE7B6 /* PassCodeConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeConfiguration.swift; sourceTree = ""; }; + EF05EDE61EAFA87300CAE7B6 /* PassCodeValidationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeValidationResult.swift; sourceTree = ""; }; + EF05EDE81EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolderProtocol.swift; sourceTree = ""; }; + EF05EDEA1EAFA8E600CAE7B6 /* PassCodeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeError.swift; sourceTree = ""; }; + EF05EDEC1EAFA96D00CAE7B6 /* PassCodeHolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolder.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -99,6 +113,7 @@ CAE698E51E968820000394B0 /* LeadKitAdditions */ = { isa = PBXGroup; children = ( + EF05EDDB1EAFA6FA00CAE7B6 /* Controllers */, CAE698EE1E968B72000394B0 /* Classes */, CAE699011E9693DE000394B0 /* Enums */, CAE698F81E968F56000394B0 /* Extensions */, @@ -159,6 +174,52 @@ path = CommonCrypto; sourceTree = ""; }; + EF05EDDB1EAFA6FA00CAE7B6 /* Controllers */ = { + isa = PBXGroup; + children = ( + EF05EDDC1EAFA72600CAE7B6 /* PassCode */, + ); + path = Controllers; + sourceTree = ""; + }; + EF05EDDC1EAFA72600CAE7B6 /* PassCode */ = { + isa = PBXGroup; + children = ( + EF05EDDD1EAFA72600CAE7B6 /* Model */, + EF05EDDE1EAFA72600CAE7B6 /* View */, + EF05EDDF1EAFA72600CAE7B6 /* ViewModel */, + ); + path = PassCode; + sourceTree = ""; + }; + EF05EDDD1EAFA72600CAE7B6 /* Model */ = { + isa = PBXGroup; + children = ( + EF05EDE41EAFA80D00CAE7B6 /* PassCodeConfiguration.swift */, + EF05EDEA1EAFA8E600CAE7B6 /* PassCodeError.swift */, + EF05EDEC1EAFA96D00CAE7B6 /* PassCodeHolder.swift */, + EF05EDE81EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift */, + EF05EDE61EAFA87300CAE7B6 /* PassCodeValidationResult.swift */, + ); + path = Model; + sourceTree = ""; + }; + EF05EDDE1EAFA72600CAE7B6 /* View */ = { + isa = PBXGroup; + children = ( + EF05EDE01EAFA74200CAE7B6 /* BasePassCodeViewController.swift */, + ); + path = View; + sourceTree = ""; + }; + EF05EDDF1EAFA72600CAE7B6 /* ViewModel */ = { + isa = PBXGroup; + children = ( + EF05EDE21EAFA7A600CAE7B6 /* BasePassCodeViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; F8A65FEC7C0EB4B93746E50F /* Pods */ = { isa = PBXGroup; children = ( @@ -352,15 +413,22 @@ buildActionMask = 2147483647; files = ( EF05EDB81EAF704800CAE7B6 /* UserDefaults+UserService.swift in Sources */, + EF05EDE11EAFA74200CAE7B6 /* BasePassCodeViewController.swift in Sources */, EF05EDC61EAF70EB00CAE7B6 /* TouchIDService.swift in Sources */, + EF05EDE31EAFA7A600CAE7B6 /* BasePassCodeViewModel.swift in Sources */, EF05EDC21EAF706200CAE7B6 /* DefaultNetworkService.swift in Sources */, EF05EDBB1EAF705500CAE7B6 /* ApiError.swift in Sources */, + EF05EDE91EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift in Sources */, + EF05EDED1EAFA96D00CAE7B6 /* PassCodeHolder.swift in Sources */, EF05EDB71EAF704800CAE7B6 /* Observable+Extensions.swift in Sources */, EF05EDC01EAF706200CAE7B6 /* ApiResponse.swift in Sources */, EF05EDBC1EAF705500CAE7B6 /* ConnectionError.swift in Sources */, + EF05EDEB1EAFA8E600CAE7B6 /* PassCodeError.swift in Sources */, EF05EDB41EAF703A00CAE7B6 /* BaseUserService.swift in Sources */, + EF05EDE51EAFA80D00CAE7B6 /* PassCodeConfiguration.swift in Sources */, EF05EDC81EAF91D500CAE7B6 /* BasePassCodeService.swift in Sources */, EF05EDC11EAF706200CAE7B6 /* BaseDateFormatter.swift in Sources */, + EF05EDE71EAFA87300CAE7B6 /* PassCodeValidationResult.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeConfiguration.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeConfiguration.swift new file mode 100644 index 0000000..8682581 --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeConfiguration.swift @@ -0,0 +1,45 @@ +// +// 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. +// + +public struct PassCodeConfiguration { + + public var passCodeCharactersNumber: UInt = 4 + public var maxAttemptsLoginNumber: UInt = 5 + + public var shouldResetWhenGoBackground: Bool = true + + private init() {} + + init?(passCodeCharactersNumber: UInt) { + guard passCodeCharactersNumber > 0 else { + assertionFailure("passCodeCharactersNumber must be greater then 0") + return nil + } + self.passCodeCharactersNumber = passCodeCharactersNumber + } + + public static var defaultConfiguration: PassCodeConfiguration { + let passCodeConfiguration = PassCodeConfiguration() + return passCodeConfiguration + } + +} diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeError.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeError.swift new file mode 100644 index 0000000..7001eb3 --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeError.swift @@ -0,0 +1,27 @@ +// +// 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. +// + +public enum PassCodeError: Error { + case codesNotMatch + case wrongCode + case tooMuchAttempts +} diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeHolder.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeHolder.swift new file mode 100644 index 0000000..47ba14a --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeHolder.swift @@ -0,0 +1,116 @@ +// +// 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 RxCocoa + +extension PassCodeHolderProtocol { + + public var passCodeHolderCreate: PassCodeHolderCreate? { + return self as? PassCodeHolderCreate + } + + public var passCodeHolderEnter: PassCodeHolderEnter? { + return self as? PassCodeHolderEnter + } + +} + +public class PassCodeHolderCreate: PassCodeHolderProtocol { + + public let type: PassCodeControllerType = .create + + private var firstPassCode: String? + private var secondPassCode: String? + + public var enterStep: PassCodeEnterStep { + if firstPassCode == nil { + return .first + } else { + return .second + } + } + + public var shouldValidate: Bool { + return firstPassCode != nil && secondPassCode != nil + } + + public var passCode: String? { + guard let firstPassCode = firstPassCode, let secondPassCode = secondPassCode, firstPassCode == secondPassCode else { + return nil + } + + return firstPassCode + } + + public func add(passCode: String) { + switch enterStep { + case .first: + firstPassCode = passCode + case .second: + secondPassCode = passCode + } + } + + public func validate() -> PassCodeValidationResult { + if let passCode = passCode { + return .valid(passCode) + } else { + return .inValid(.codesNotMatch) + } + } + + public func reset() { + firstPassCode = nil + secondPassCode = nil + } + +} + +public class PassCodeHolderEnter: PassCodeHolderProtocol { + + public let type: PassCodeControllerType = .enter + public let enterStep: PassCodeEnterStep = .first + + public var shouldValidate: Bool { + return passCode != nil + } + + public var passCode: String? + + public func add(passCode: String) { + self.passCode = passCode + } + + public func validate() -> PassCodeValidationResult { + if let passCode = passCode { + return .valid(passCode) + } else { + return .inValid(nil) + } + } + + public func reset() { + passCode = nil + } + +} diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeHolderProtocol.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeHolderProtocol.swift new file mode 100644 index 0000000..8296679 --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeHolderProtocol.swift @@ -0,0 +1,56 @@ +// +// 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. +// + +public enum PassCodeEnterStep { + case first + case second +} + +public protocol PassCodeHolderProtocol { + + var type: PassCodeControllerType { get } + var enterStep: PassCodeEnterStep { get } + + func add(passCode: String) + func reset() + + var shouldValidate: Bool { get } + var passCode: String? { get } + + func validate() -> PassCodeValidationResult + +} + +public class PassCodeHolderBuilder { + + private init() {} + + public static func build(with type: PassCodeControllerType) -> PassCodeHolderProtocol { + switch type { + case .create: + return PassCodeHolderCreate() + case .enter: + return PassCodeHolderEnter() + } + } + +} diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeValidationResult.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeValidationResult.swift new file mode 100644 index 0000000..33bdd9b --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeValidationResult.swift @@ -0,0 +1,55 @@ +// +// 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. +// + +public enum PassCodeValidationResult { + + case valid(String) + case inValid(PassCodeError?) + + public var isValid: Bool { + switch self { + case .valid: + return true + default: + return false + } + } + + public var passCode: String? { + switch self { + case let .valid(passCode): + return passCode + default: + return nil + } + } + + public var error: PassCodeError? { + switch self { + case let .inValid(error): + return error + default: + return nil + } + } + +} diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/View/BasePassCodeViewController.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/View/BasePassCodeViewController.swift new file mode 100644 index 0000000..3b2c40e --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/View/BasePassCodeViewController.swift @@ -0,0 +1,239 @@ +// +// 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 UIKit +import RxSwift +import RxCocoa +import LeadKit + +public enum PinImageType { + case entered + case clear +} + +public enum PassCodeControllerType { + case create + case enter +} + +public enum PassCodeControllerState { + case enter + case repeatEnter +} + +open class BasePassCodeViewController: UIViewController { + + public var viewModel: BasePassCodeViewModel! + + // MARK: - IBOutlets + + @IBOutlet public weak var titleLabel: UILabel? + @IBOutlet public weak var errorLabel: UILabel? + @IBOutlet public weak var dotStackView: UIStackView! + + let disposeBag = DisposeBag() + + fileprivate lazy var fakeTextField: UITextField = { + let fakeTextField = UITextField() + fakeTextField.isSecureTextEntry = true + fakeTextField.keyboardType = .numberPad + fakeTextField.isHidden = true + self.view.addSubview(fakeTextField) + return fakeTextField + }() + + // MARK: - Life circle + + override open func viewDidLoad() { + super.viewDidLoad() + + initialLoadView() + initialDotNumberConfiguration() + enebleKeyboard() + configureBackgroundNotifications() + showTouchIdIfNeeded(with: touchIdHint) + } + + // MARK: - Private functions + + private func configureBackgroundNotifications() { + guard viewModel.passCodeConfiguration.shouldResetWhenGoBackground else { + return + } + + NotificationCenter.default.rx.notification(.UIApplicationWillResignActive) + .subscribe(onNext: { [weak self] _ in + self?.resetUI() + }) + .addDisposableTo(disposeBag) + } + + private func enebleKeyboard() { + fakeTextField.becomeFirstResponder() + } + + private func initialDotNumberConfiguration() { + dotStackView.arrangedSubviews.forEach { dotStackView.removeArrangedSubview($0) } + + for _ in 0.. index, + let imageView = dotStackView.arrangedSubviews[index] as? UIImageView else { + return + } + + imageView.image = imageFor(type: state) + } + + fileprivate func setStates(for passCodeText: String) { + var statesArray: [PinImageType] = [] + + for characterIndex in 0.. UIImage { + assertionFailure("Don't use it directly. Override it!") + return UIImage() + } + + // override to change error text + func errorDescription(for error: PassCodeError) -> String { + assertionFailure("Don't use it directly. Override it!") + return "" + } + + // override to change action title text + func actionTitle(for passCodeControllerState: PassCodeControllerState) -> String { + assertionFailure("Don't use it directly. Override it!") + return "" + } + + // MARK: - Functions that can you can override to castomise your controller + + open func showError(for error: PassCodeError) { + errorLabel?.text = errorDescription(for: error) + errorLabel?.isHidden = false + } + + open func hideError() { + errorLabel?.isHidden = true + } + + // override to change UI for state + open func configureUI(for passCodeControllerState: PassCodeControllerState) { + resetDotsUI() + titleLabel?.text = actionTitle(for: passCodeControllerState) + } + +} + +// We need to implement all functions of ConfigurableController protocol to give ability to override them. +extension BasePassCodeViewController: ConfigurableController { + + open func bindViews() { + fakeTextField.rx.text.asDriver() + .do(onNext: { [weak self] text in + self?.setStates(for: text ?? "") + self?.hideError() + }) + .drive(viewModel.passCodeText) + .addDisposableTo(disposeBag) + + viewModel.validationResult + .drive(onNext: { [weak self] validationResult in + guard let validationResult = validationResult else { + return + } + + if validationResult.isValid { + self?.hideError() + } else if let pasCodeError = validationResult.error { + self?.showError(for: pasCodeError) + } + }) + .addDisposableTo(disposeBag) + + viewModel.passCodeControllerState + .drive(onNext: { [weak self] controllerState in + self?.configureUI(for: controllerState) + }) + .addDisposableTo(disposeBag) + } + + open func addViews() {} + + open func setAppearance() {} + + open func configureBarButtons() {} + + open func localize() {} + +} diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift new file mode 100644 index 0000000..8b26e4b --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift @@ -0,0 +1,172 @@ +// +// 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(nil) + var validationResult: Driver { + return validationResultHolder.asDriver() + } + + fileprivate let passCodeControllerStateHolder = Variable(.enter) + public var passCodeControllerState: Driver { + return passCodeControllerStateHolder.asDriver() + } + + public let passCodeText = Variable(nil) + + fileprivate var attemptsNumber = 0 + + fileprivate lazy var passCodeHolder: PassCodeHolderProtocol = { + return PassCodeHolderBuilder.build(with: self.controllerType) + }() + + 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 + if validationResult?.isValid ?? false, let passCode = validationResult?.passCode { + self?.authSucceed(.passCode(passCode)) + } else { + self?.passCodeControllerStateHolder.value = .enter + } + }) + .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("Don't use it directly. Override it!") + return false + } + + open func authSucceed(_ type: PassCodeAuthType) { + assertionFailure("Don't use it directly. Override it!") + } + + // MARK: - Functions that can you can override to use TouchId + + open var isTouchIdEnabled: Bool { + return false + } + + open func activateTouchIdForUser() { + assertionFailure("Don't use it directly. Override it!") + } + +} + +extension BasePassCodeViewModel { + + fileprivate func set(passCode: String) { + passCodeHolder.add(passCode: passCode) + validateIfNeeded() + + if shouldUpdateControllerState { + switch passCodeHolder.enterStep { + case .first: + passCodeControllerStateHolder.value = .enter + case .second: + passCodeControllerStateHolder.value = .repeatEnter + } + } + } + + private var shouldUpdateControllerState: Bool { + return !passCodeHolder.shouldValidate || + !(validationResultHolder.value?.isValid ?? true) || + validationResultHolder.value?.error == .tooMuchAttempts + } + + private func validateIfNeeded() { + guard passCodeHolder.shouldValidate else { + return + } + + var validationResult = passCodeHolder.validate() + + if passCodeHolder.type == .enter { + 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(.tooMuchAttempts) + } + } + + if !validationResult.isValid { + passCodeHolder.reset() + } + + validationResultHolder.value = validationResult + } + +} From 108d996c996b51d42cadbab04a7a041e7054cb32 Mon Sep 17 00:00:00 2001 From: Alexey Gerasimov Date: Wed, 26 Apr 2017 16:08:37 +0300 Subject: [PATCH 6/9] Simple network service added --- .../project.pbxproj | 4 ++ .../Classes/DefaultNetworkService.swift | 24 ++++--- .../Classes/SimpleNetworkService.swift | 70 +++++++++++++++++++ 3 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 LeadKitAdditions/LeadKitAdditions/Classes/SimpleNetworkService.swift diff --git a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj index b0dcbd6..9204b0a 100644 --- a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj +++ b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 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 */; }; + EF05EDF41EB0D05400CAE7B6 /* SimpleNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDF31EB0D05400CAE7B6 /* SimpleNetworkService.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -60,6 +61,7 @@ EF05EDE81EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolderProtocol.swift; sourceTree = ""; }; EF05EDEA1EAFA8E600CAE7B6 /* PassCodeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeError.swift; sourceTree = ""; }; EF05EDEC1EAFA96D00CAE7B6 /* PassCodeHolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolder.swift; sourceTree = ""; }; + EF05EDF31EB0D05400CAE7B6 /* SimpleNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleNetworkService.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -130,6 +132,7 @@ EF05EDBD1EAF706200CAE7B6 /* ApiResponse.swift */, EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */, EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */, + EF05EDF31EB0D05400CAE7B6 /* SimpleNetworkService.swift */, ); path = Classes; sourceTree = ""; @@ -413,6 +416,7 @@ buildActionMask = 2147483647; files = ( EF05EDB81EAF704800CAE7B6 /* UserDefaults+UserService.swift in Sources */, + EF05EDF41EB0D05400CAE7B6 /* SimpleNetworkService.swift in Sources */, EF05EDE11EAFA74200CAE7B6 /* BasePassCodeViewController.swift in Sources */, EF05EDC61EAF70EB00CAE7B6 /* TouchIDService.swift in Sources */, EF05EDE31EAFA7A600CAE7B6 /* BasePassCodeViewModel.swift in Sources */, diff --git a/LeadKitAdditions/LeadKitAdditions/Classes/DefaultNetworkService.swift b/LeadKitAdditions/LeadKitAdditions/Classes/DefaultNetworkService.swift index 3b5b057..4ff5f9f 100644 --- a/LeadKitAdditions/LeadKitAdditions/Classes/DefaultNetworkService.swift +++ b/LeadKitAdditions/LeadKitAdditions/Classes/DefaultNetworkService.swift @@ -28,14 +28,16 @@ import RxSwift import RxCocoa import RxAlamofire -public let defaultTimeoutInterval = 20.0 - open class DefaultNetworkService: NetworkService { static let retryLimit = 3 - open class func baseUrl() -> String { - fatalError("base url should be overrided") + open class var baseUrl: String { + fatalError("You should override this var") + } + + open class var defaultTimeoutInterval: TimeInterval { + fatalError("You should override this var") } public override init(sessionManager: SessionManager) { @@ -46,22 +48,22 @@ open class DefaultNetworkService: NetworkService { // MARK: - Default Values - open class func serverTrustPolicies() -> [String: ServerTrustPolicy] { + open class var serverTrustPolicies: [String: ServerTrustPolicy] { return [ - DefaultNetworkService.baseUrl(): .disableEvaluation + DefaultNetworkService.baseUrl: .disableEvaluation ] } - open class func configuration() -> URLSessionConfiguration { + open class var configuration: URLSessionConfiguration { let configuration = URLSessionConfiguration.default configuration.timeoutIntervalForRequest = defaultTimeoutInterval return configuration } - open class func sessionManager() -> SessionManager { - let sessionManager = SessionManager(configuration: configuration(), - serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies())) + open class var sessionManager: SessionManager { + let sessionManager = SessionManager(configuration: configuration, + serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)) return sessionManager } @@ -71,7 +73,7 @@ extension ApiRequestParameters { init(url: String, parameters: [String: Any] = [:]) { - self.init(url: DefaultNetworkService.baseUrl() + url, + self.init(url: DefaultNetworkService.baseUrl + url, method: .post, parameters: parameters, encoding: JSONEncoding.default, diff --git a/LeadKitAdditions/LeadKitAdditions/Classes/SimpleNetworkService.swift b/LeadKitAdditions/LeadKitAdditions/Classes/SimpleNetworkService.swift new file mode 100644 index 0000000..2ed8156 --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Classes/SimpleNetworkService.swift @@ -0,0 +1,70 @@ +// +// 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 +import RxCocoa +import RxAlamofire + +open class SimpleNetworkService: DefaultNetworkService { + + // Singleton + static let shared = SimpleNetworkService() + + public convenience init() { + self.init(sessionManager: SimpleNetworkService.sessionManager) + } + + // MARK: - Public methods + + open func request(with parameters: ApiRequestParameters) -> Observable { + 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 { + 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) + } + } + } + +} From f64de731a8f2cd0bc8e3bada894c58d195537c1c Mon Sep 17 00:00:00 2001 From: Alexey Gerasimov Date: Wed, 26 Apr 2017 16:13:35 +0300 Subject: [PATCH 7/9] Fixed: pullRequest comments --- LeadKitAdditions/CommonCrypto/CommonCrypto.h | 22 +++++++++++++---- .../CommonCrypto/CommonCrypto.xcconfig | 24 +++++++++++++++---- .../Model/PassCodeConfiguration.swift | 3 +-- .../PassCode/Model/PassCodeError.swift | 2 +- .../ViewModel/BasePassCodeViewModel.swift | 4 ++-- 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/LeadKitAdditions/CommonCrypto/CommonCrypto.h b/LeadKitAdditions/CommonCrypto/CommonCrypto.h index def2316..94b806a 100644 --- a/LeadKitAdditions/CommonCrypto/CommonCrypto.h +++ b/LeadKitAdditions/CommonCrypto/CommonCrypto.h @@ -1,9 +1,23 @@ // -// CommonCrypto.h -// CommonCrypto +// Copyright (c) 2017 Touch Instinct // -// Created by Alexey Gerasimov on 25/04/2017. -// Copyright © 2017 TouchInstinct. All rights reserved. +// 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 diff --git a/LeadKitAdditions/CommonCrypto/CommonCrypto.xcconfig b/LeadKitAdditions/CommonCrypto/CommonCrypto.xcconfig index 080c4d8..995f1ce 100644 --- a/LeadKitAdditions/CommonCrypto/CommonCrypto.xcconfig +++ b/LeadKitAdditions/CommonCrypto/CommonCrypto.xcconfig @@ -1,10 +1,24 @@ // -// CommonCrypto.xcconfig -// Chat +// Copyright (c) 2017 Touch Instinct // -// Created by Alexey Gerasimov on 17/08/16. -// Copyright © 2016 Touch Instinct. All rights reserved. +// 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. // MODULEMAP_FILE[sdk=iphoneos*] = $(SRCROOT)/CommonCrypto/iphoneos.modulemap -MODULEMAP_FILE[sdk=iphonesimulator*] = $(SRCROOT)/CommonCrypto/iphonesimulator.modulemap \ No newline at end of file +MODULEMAP_FILE[sdk=iphonesimulator*] = $(SRCROOT)/CommonCrypto/iphonesimulator.modulemap diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeConfiguration.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeConfiguration.swift index 8682581..2635d02 100644 --- a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeConfiguration.swift +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeConfiguration.swift @@ -38,8 +38,7 @@ public struct PassCodeConfiguration { } public static var defaultConfiguration: PassCodeConfiguration { - let passCodeConfiguration = PassCodeConfiguration() - return passCodeConfiguration + return PassCodeConfiguration() } } diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeError.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeError.swift index 7001eb3..6fbcc8a 100644 --- a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeError.swift +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/Model/PassCodeError.swift @@ -23,5 +23,5 @@ public enum PassCodeError: Error { case codesNotMatch case wrongCode - case tooMuchAttempts + case tooManyAttempts } diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift index 8b26e4b..b476547 100644 --- a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift @@ -139,7 +139,7 @@ extension BasePassCodeViewModel { private var shouldUpdateControllerState: Bool { return !passCodeHolder.shouldValidate || !(validationResultHolder.value?.isValid ?? true) || - validationResultHolder.value?.error == .tooMuchAttempts + validationResultHolder.value?.error == .tooManyAttempts } private func validateIfNeeded() { @@ -158,7 +158,7 @@ extension BasePassCodeViewModel { if (!validationResult.isValid && attemptsNumber == Int(passCodeConfiguration.maxAttemptsLoginNumber)) || attemptsNumber > Int(passCodeConfiguration.maxAttemptsLoginNumber) { - validationResult = .inValid(.tooMuchAttempts) + validationResult = .inValid(.tooManyAttempts) } } From 1f6a443742c7fe90fcc50f160fe56de6b1bcd40d Mon Sep 17 00:00:00 2001 From: Alexey Gerasimov Date: Wed, 26 Apr 2017 16:27:28 +0300 Subject: [PATCH 8/9] BarButtonLoading service added --- .../project.pbxproj | 32 +++++-- .../UIBarButtonItem+Extensions.swift | 33 +++++++ .../Services/BarButtonLoadingService.swift | 87 +++++++++++++++++++ .../Network}/DefaultNetworkService.swift | 0 .../Network}/SimpleNetworkService.swift | 0 5 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 LeadKitAdditions/LeadKitAdditions/Extensions/UIBarButtonItem+Extensions.swift create mode 100644 LeadKitAdditions/LeadKitAdditions/Services/BarButtonLoadingService.swift rename LeadKitAdditions/LeadKitAdditions/{Classes => Services/Network}/DefaultNetworkService.swift (100%) rename LeadKitAdditions/LeadKitAdditions/{Classes => Services/Network}/SimpleNetworkService.swift (100%) diff --git a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj index 9204b0a..c244b1a 100644 --- a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj +++ b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ 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 */; }; - EF05EDC21EAF706200CAE7B6 /* DefaultNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */; }; EF05EDC61EAF70EB00CAE7B6 /* TouchIDService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */; }; EF05EDC81EAF91D500CAE7B6 /* BasePassCodeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */; }; EF05EDD21EAF9CF100CAE7B6 /* CommonCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = EF05EDD01EAF9CF100CAE7B6 /* CommonCrypto.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -28,7 +27,10 @@ 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 */; }; - EF05EDF41EB0D05400CAE7B6 /* SimpleNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDF31EB0D05400CAE7B6 /* SimpleNetworkService.swift */; }; + EF05EDF61EB0D4C300CAE7B6 /* BarButtonLoadingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDF51EB0D4C300CAE7B6 /* BarButtonLoadingService.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 /* SimpleNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF05EDFB1EB0D77400CAE7B6 /* SimpleNetworkService.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -45,7 +47,6 @@ EF05EDBA1EAF705500CAE7B6 /* ConnectionError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionError.swift; sourceTree = ""; }; EF05EDBD1EAF706200CAE7B6 /* ApiResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiResponse.swift; sourceTree = ""; }; EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDateFormatter.swift; sourceTree = ""; }; - EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkService.swift; sourceTree = ""; }; EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchIDService.swift; sourceTree = ""; }; EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeService.swift; sourceTree = ""; }; EF05EDCE1EAF9CF100CAE7B6 /* CommonCrypto.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CommonCrypto.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -61,7 +62,10 @@ EF05EDE81EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolderProtocol.swift; sourceTree = ""; }; EF05EDEA1EAFA8E600CAE7B6 /* PassCodeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeError.swift; sourceTree = ""; }; EF05EDEC1EAFA96D00CAE7B6 /* PassCodeHolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassCodeHolder.swift; sourceTree = ""; }; - EF05EDF31EB0D05400CAE7B6 /* SimpleNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleNetworkService.swift; sourceTree = ""; }; + EF05EDF51EB0D4C300CAE7B6 /* BarButtonLoadingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarButtonLoadingService.swift; sourceTree = ""; }; + EF05EDF71EB0D5A600CAE7B6 /* UIBarButtonItem+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Extensions.swift"; sourceTree = ""; }; + EF05EDFA1EB0D77400CAE7B6 /* DefaultNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkService.swift; sourceTree = ""; }; + EF05EDFB1EB0D77400CAE7B6 /* SimpleNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleNetworkService.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -131,8 +135,6 @@ children = ( EF05EDBD1EAF706200CAE7B6 /* ApiResponse.swift */, EF05EDBE1EAF706200CAE7B6 /* BaseDateFormatter.swift */, - EF05EDBF1EAF706200CAE7B6 /* DefaultNetworkService.swift */, - EF05EDF31EB0D05400CAE7B6 /* SimpleNetworkService.swift */, ); path = Classes; sourceTree = ""; @@ -140,6 +142,8 @@ CAE698F31E968E28000394B0 /* Services */ = { isa = PBXGroup; children = ( + EF05EDF91EB0D75A00CAE7B6 /* Network */, + EF05EDF51EB0D4C300CAE7B6 /* BarButtonLoadingService.swift */, EF05EDC71EAF91D500CAE7B6 /* BasePassCodeService.swift */, EF05EDB31EAF703A00CAE7B6 /* BaseUserService.swift */, EF05EDC51EAF70EB00CAE7B6 /* TouchIDService.swift */, @@ -151,6 +155,7 @@ isa = PBXGroup; children = ( EF05EDB51EAF704800CAE7B6 /* Observable+Extensions.swift */, + EF05EDF71EB0D5A600CAE7B6 /* UIBarButtonItem+Extensions.swift */, EF05EDB61EAF704800CAE7B6 /* UserDefaults+UserService.swift */, ); path = Extensions; @@ -223,6 +228,15 @@ path = ViewModel; sourceTree = ""; }; + EF05EDF91EB0D75A00CAE7B6 /* Network */ = { + isa = PBXGroup; + children = ( + EF05EDFA1EB0D77400CAE7B6 /* DefaultNetworkService.swift */, + EF05EDFB1EB0D77400CAE7B6 /* SimpleNetworkService.swift */, + ); + path = Network; + sourceTree = ""; + }; F8A65FEC7C0EB4B93746E50F /* Pods */ = { isa = PBXGroup; children = ( @@ -416,20 +430,22 @@ buildActionMask = 2147483647; files = ( EF05EDB81EAF704800CAE7B6 /* UserDefaults+UserService.swift in Sources */, - EF05EDF41EB0D05400CAE7B6 /* SimpleNetworkService.swift in Sources */, EF05EDE11EAFA74200CAE7B6 /* BasePassCodeViewController.swift in Sources */, EF05EDC61EAF70EB00CAE7B6 /* TouchIDService.swift in Sources */, EF05EDE31EAFA7A600CAE7B6 /* BasePassCodeViewModel.swift in Sources */, - EF05EDC21EAF706200CAE7B6 /* DefaultNetworkService.swift in Sources */, EF05EDBB1EAF705500CAE7B6 /* ApiError.swift in Sources */, EF05EDE91EAFA8A000CAE7B6 /* PassCodeHolderProtocol.swift in Sources */, + EF05EDFD1EB0D77400CAE7B6 /* SimpleNetworkService.swift in Sources */, + EF05EDF81EB0D5A600CAE7B6 /* UIBarButtonItem+Extensions.swift in Sources */, EF05EDED1EAFA96D00CAE7B6 /* PassCodeHolder.swift in Sources */, + EF05EDF61EB0D4C300CAE7B6 /* BarButtonLoadingService.swift in Sources */, EF05EDB71EAF704800CAE7B6 /* Observable+Extensions.swift in Sources */, EF05EDC01EAF706200CAE7B6 /* ApiResponse.swift in Sources */, EF05EDBC1EAF705500CAE7B6 /* ConnectionError.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 */, diff --git a/LeadKitAdditions/LeadKitAdditions/Extensions/UIBarButtonItem+Extensions.swift b/LeadKitAdditions/LeadKitAdditions/Extensions/UIBarButtonItem+Extensions.swift new file mode 100644 index 0000000..51c2d56 --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Extensions/UIBarButtonItem+Extensions.swift @@ -0,0 +1,33 @@ +// +// 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 UIKit + +extension UIBarButtonItem { + + public static var activityIndicator: (barButton: UIBarButtonItem, activityIndicator: UIActivityIndicatorView) { + let indicatorView = UIActivityIndicatorView(activityIndicatorStyle: .white) + let indicatorBar = UIBarButtonItem(customView: indicatorView) + return (indicatorBar, indicatorView) + } + +} diff --git a/LeadKitAdditions/LeadKitAdditions/Services/BarButtonLoadingService.swift b/LeadKitAdditions/LeadKitAdditions/Services/BarButtonLoadingService.swift new file mode 100644 index 0000000..832cdad --- /dev/null +++ b/LeadKitAdditions/LeadKitAdditions/Services/BarButtonLoadingService.swift @@ -0,0 +1,87 @@ +// +// 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 UIKit +import RxSwift +import RxCocoa + +public enum BarButtonLoadingServiceSide { + case left + case right +} + +public class BarButtonLoadingService { + + fileprivate weak var navigationItem: UINavigationItem? + fileprivate var savedBarButton: UIBarButtonItem? + private let side: BarButtonLoadingServiceSide + + private var barButtonItem: UIBarButtonItem? { + get { + switch side { + case .left: + return navigationItem?.leftBarButtonItem + case .right: + return navigationItem?.rightBarButtonItem + } + } + set { + switch side { + case .left: + navigationItem?.leftBarButtonItem = newValue + case .right: + navigationItem?.rightBarButtonItem = newValue + } + } + } + + public init(navigationItem: UINavigationItem, side: BarButtonLoadingServiceSide) { + self.navigationItem = navigationItem + self.side = side + } + + fileprivate func setBarButton(waiting: Bool = false) { + if waiting { + savedBarButton = barButtonItem + + let activityIndicatorItem = UIBarButtonItem.activityIndicator + barButtonItem = activityIndicatorItem.barButton + activityIndicatorItem.activityIndicator.startAnimating() + } else { + barButtonItem = savedBarButton + } + } + +} + +extension Observable { + + public func changeLoadingUI(using loadingService: BarButtonLoadingService) -> Observable { + return observeOn(MainScheduler.instance) + .do(onSubscribe: { + loadingService.setBarButton(waiting: true) + }, onDispose: { + loadingService.setBarButton(waiting: false) + }) + } + +} diff --git a/LeadKitAdditions/LeadKitAdditions/Classes/DefaultNetworkService.swift b/LeadKitAdditions/LeadKitAdditions/Services/Network/DefaultNetworkService.swift similarity index 100% rename from LeadKitAdditions/LeadKitAdditions/Classes/DefaultNetworkService.swift rename to LeadKitAdditions/LeadKitAdditions/Services/Network/DefaultNetworkService.swift diff --git a/LeadKitAdditions/LeadKitAdditions/Classes/SimpleNetworkService.swift b/LeadKitAdditions/LeadKitAdditions/Services/Network/SimpleNetworkService.swift similarity index 100% rename from LeadKitAdditions/LeadKitAdditions/Classes/SimpleNetworkService.swift rename to LeadKitAdditions/LeadKitAdditions/Services/Network/SimpleNetworkService.swift From 5e308d5f0f3f3e15294da8398b4d3992834fe3f9 Mon Sep 17 00:00:00 2001 From: Alexey Gerasimov Date: Wed, 26 Apr 2017 16:44:10 +0300 Subject: [PATCH 9/9] Must-overrides now have hints --- .../PassCode/View/BasePassCodeViewController.swift | 8 ++++---- .../PassCode/ViewModel/BasePassCodeViewModel.swift | 6 +++--- .../Services/Network/DefaultNetworkService.swift | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/View/BasePassCodeViewController.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/View/BasePassCodeViewController.swift index 3b2c40e..e88e3a0 100644 --- a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/View/BasePassCodeViewController.swift +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/View/BasePassCodeViewController.swift @@ -154,25 +154,25 @@ open class BasePassCodeViewController: UIViewController { // MARK: - HAVE TO OVERRIDE open var touchIdHint: String { - assertionFailure("You should override this var") + assertionFailure("You should override this var: touchIdHint") return "" } // override to change Images func imageFor(type: PinImageType) -> UIImage { - assertionFailure("Don't use it directly. Override it!") + assertionFailure("You should override this method: imageFor(type: PinImageType)") return UIImage() } // override to change error text func errorDescription(for error: PassCodeError) -> String { - assertionFailure("Don't use it directly. Override it!") + assertionFailure("You should override this method: errorDescription(for error: PassCodeError)") return "" } // override to change action title text func actionTitle(for passCodeControllerState: PassCodeControllerState) -> String { - assertionFailure("Don't use it directly. Override it!") + assertionFailure("You should override this method: actionTitle(for passCodeControllerState: PassCodeControllerState)") return "" } diff --git a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift index b476547..333e65d 100644 --- a/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift +++ b/LeadKitAdditions/LeadKitAdditions/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift @@ -100,12 +100,12 @@ open class BasePassCodeViewModel: BaseViewModel { // MARK: - HAVE TO OVERRIDE open func isEnteredPassCodeValid(_ passCode: String) -> Bool { - assertionFailure("Don't use it directly. Override it!") + assertionFailure("You should override this method: isEnteredPassCodeValid(_ passCode: String)") return false } open func authSucceed(_ type: PassCodeAuthType) { - assertionFailure("Don't use it directly. Override it!") + assertionFailure("You should override this method: authSucceed(_ type: PassCodeAuthType)") } // MARK: - Functions that can you can override to use TouchId @@ -115,7 +115,7 @@ open class BasePassCodeViewModel: BaseViewModel { } open func activateTouchIdForUser() { - assertionFailure("Don't use it directly. Override it!") + assertionFailure("You should override this method: activateTouchIdForUser()") } } diff --git a/LeadKitAdditions/LeadKitAdditions/Services/Network/DefaultNetworkService.swift b/LeadKitAdditions/LeadKitAdditions/Services/Network/DefaultNetworkService.swift index 4ff5f9f..67f50b2 100644 --- a/LeadKitAdditions/LeadKitAdditions/Services/Network/DefaultNetworkService.swift +++ b/LeadKitAdditions/LeadKitAdditions/Services/Network/DefaultNetworkService.swift @@ -33,11 +33,11 @@ open class DefaultNetworkService: NetworkService { static let retryLimit = 3 open class var baseUrl: String { - fatalError("You should override this var") + fatalError("You should override this var: baseUrl") } open class var defaultTimeoutInterval: TimeInterval { - fatalError("You should override this var") + fatalError("You should override this var: defaultTimeoutInterval") } public override init(sessionManager: SessionManager) {