diff --git a/PasscodeLock.podspec b/PasscodeLock.podspec index 2df42467..739556fc 100755 --- a/PasscodeLock.podspec +++ b/PasscodeLock.podspec @@ -1,19 +1,21 @@ Pod::Spec.new do |s| s.name = 'PasscodeLock' -s.version = '1.0.3' +s.version = '1.1.2' s.license = { :type => "MIT", :file => 'LICENSE.txt' } s.summary = 'An iOS passcode lock with Touch ID authentication written in Swift.' -s.homepage = 'https://github.com/velikanov/SwiftPasscodeLock' -s.authors = { 'Yanko Dimitrov' => '', 'Chris Ziogas' => '', } -s.source = { :git => 'https://github.com/velikanov/SwiftPasscodeLock.git' } +s.homepage = 'https://github.com/oskarirauta/SwiftPasscodeLock' +s.authors = { 'Oskari Rauta' => '', 'Yanko Dimitrov' => '', 'Chris Ziogas' => '', } +s.source = { :git => 'https://github.com/oskarirauta/SwiftPasscodeLock.git' } -s.ios.deployment_target = '8.0' +s.ios.deployment_target = '9.0' +s.swift_version = '5.0' s.source_files = 'PasscodeLock/*.{h,swift}', 'PasscodeLock/*/*.{swift}' s.resources = [ 'PasscodeLock/Views/PasscodeLockView.xib', + 'PasscodeLock/Views/DarkPasscodeLockView.xib', 'PasscodeLock/en.lproj/*' ] diff --git a/PasscodeLock.xcodeproj/project.pbxproj b/PasscodeLock.xcodeproj/project.pbxproj index fe6032d9..9c2b8bca 100644 --- a/PasscodeLock.xcodeproj/project.pbxproj +++ b/PasscodeLock.xcodeproj/project.pbxproj @@ -3,10 +3,12 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ + 6E2B4C0E1EE72C68005D20D0 /* DarkPasscodeLockView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6E2B4C0D1EE72C68005D20D0 /* DarkPasscodeLockView.xib */; }; + 6E93C8EC1EE5FA5C00D4F2F3 /* EnterOptionalPasscodeState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E93C8EB1EE5FA5C00D4F2F3 /* EnterOptionalPasscodeState.swift */; }; C99EAF431B90B05700D61E1B /* PasscodeLock.h in Headers */ = {isa = PBXBuildFile; fileRef = C99EAF421B90B05700D61E1B /* PasscodeLock.h */; settings = {ATTRIBUTES = (Public, ); }; }; C99EAF4A1B90B05800D61E1B /* PasscodeLock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C99EAF3F1B90B05700D61E1B /* PasscodeLock.framework */; }; C9D3DF0B1B919CE4008561EB /* PasscodeLockView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C9D3DF0A1B919CE4008561EB /* PasscodeLockView.xib */; }; @@ -106,6 +108,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 6E2B4C0D1EE72C68005D20D0 /* DarkPasscodeLockView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DarkPasscodeLockView.xib; sourceTree = ""; }; + 6E93C8EB1EE5FA5C00D4F2F3 /* EnterOptionalPasscodeState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnterOptionalPasscodeState.swift; sourceTree = ""; }; C99EAF3F1B90B05700D61E1B /* PasscodeLock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PasscodeLock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C99EAF421B90B05700D61E1B /* PasscodeLock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PasscodeLock.h; sourceTree = ""; }; C99EAF441B90B05700D61E1B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -288,6 +292,7 @@ children = ( C9DC07E01B90BBFD007A4DD0 /* PasscodeLock.swift */, C9DC07F11B90C9DE007A4DD0 /* EnterPasscodeState.swift */, + 6E93C8EB1EE5FA5C00D4F2F3 /* EnterOptionalPasscodeState.swift */, C9DC07FE1B90D24A007A4DD0 /* SetPasscodeState.swift */, C9DC08011B90D2BA007A4DD0 /* ConfirmPasscodeState.swift */, C9DC08041B90D394007A4DD0 /* ChangePasscodeState.swift */, @@ -312,6 +317,7 @@ C9DC08111B90DE1B007A4DD0 /* PasscodeSignPlaceholderView.swift */, C9DC08131B90DE50007A4DD0 /* PasscodeSignButton.swift */, C9D3DF0A1B919CE4008561EB /* PasscodeLockView.xib */, + 6E2B4C0D1EE72C68005D20D0 /* DarkPasscodeLockView.xib */, ); path = Views; sourceTree = ""; @@ -452,15 +458,16 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0810; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "Yanko Dimitrov"; TargetAttributes = { C99EAF3E1B90B05700D61E1B = { CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0810; + LastSwiftMigration = 1020; }; C99EAF481B90B05700D61E1B = { CreatedOnToolsVersion = 7.0; + LastSwiftMigration = 1020; }; C9D3DF0F1B91AD11008561EB = { CreatedOnToolsVersion = 7.0; @@ -479,8 +486,8 @@ }; }; buildConfigurationList = C99EAF391B90B05700D61E1B /* Build configuration list for PBXProject "PasscodeLock" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + compatibilityVersion = "Xcode 10.0"; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -507,6 +514,7 @@ files = ( C9DC07FC1B90D0A3007A4DD0 /* PasscodeLock.strings in Resources */, C9D3DF0B1B919CE4008561EB /* PasscodeLockView.xib in Resources */, + 6E2B4C0E1EE72C68005D20D0 /* DarkPasscodeLockView.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -556,6 +564,7 @@ C9DC07F21B90C9DE007A4DD0 /* EnterPasscodeState.swift in Sources */, C9DC08141B90DE50007A4DD0 /* PasscodeSignButton.swift in Sources */, C9D3DF481B91F099008561EB /* PasscodeLockPresenter.swift in Sources */, + 6E93C8EC1EE5FA5C00D4F2F3 /* EnterOptionalPasscodeState.swift in Sources */, C9DC07D81B90B261007A4DD0 /* PasscodeLockStateType.swift in Sources */, C9DC08161B90DF4E007A4DD0 /* PasscodeLockViewController.swift in Sources */, C9DC07E11B90BBFD007A4DD0 /* PasscodeLock.swift in Sources */, @@ -681,14 +690,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -712,7 +729,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -731,14 +748,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -756,10 +781,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -778,12 +804,16 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = PasscodeLock/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yankodimitrov.PasscodeLock; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -798,11 +828,15 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = PasscodeLock/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yankodimitrov.PasscodeLock; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -811,11 +845,16 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; INFOPLIST_FILE = PasscodeLockTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yankodimitrov.PasscodeLockTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "PasscodeLockTests/PasscodeLockTests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -824,10 +863,15 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; INFOPLIST_FILE = PasscodeLockTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yankodimitrov.PasscodeLockTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "PasscodeLockTests/PasscodeLockTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -838,10 +882,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = PasscodeLockDemo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yankodimitrov.PasscodeLockDemo; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -853,10 +900,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = PasscodeLockDemo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yankodimitrov.PasscodeLockDemo; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -867,10 +917,14 @@ BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = PasscodeLockDemoTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yankodimitrov.PasscodeLockDemoTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PasscodeLockDemo.app/PasscodeLockDemo"; }; name = Debug; @@ -881,10 +935,14 @@ BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = PasscodeLockDemoTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yankodimitrov.PasscodeLockDemoTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PasscodeLockDemo.app/PasscodeLockDemo"; }; name = Release; @@ -894,10 +952,14 @@ buildSettings = { INFOPLIST_FILE = PasscodeLockDemoUITests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yankodimitrov.PasscodeLockDemoUITests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TEST_TARGET_NAME = PasscodeLockDemo; USES_XCTRUNNER = YES; }; @@ -908,10 +970,14 @@ buildSettings = { INFOPLIST_FILE = PasscodeLockDemoUITests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yankodimitrov.PasscodeLockDemoUITests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TEST_TARGET_NAME = PasscodeLockDemo; USES_XCTRUNNER = YES; }; diff --git a/PasscodeLock.xcodeproj/xcshareddata/xcschemes/PasscodeLock.xcscheme b/PasscodeLock.xcodeproj/xcshareddata/xcschemes/PasscodeLock.xcscheme index 79e7b235..230902f8 100644 --- a/PasscodeLock.xcodeproj/xcshareddata/xcschemes/PasscodeLock.xcscheme +++ b/PasscodeLock.xcodeproj/xcshareddata/xcschemes/PasscodeLock.xcscheme @@ -1,6 +1,6 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + 1.1.2 CFBundleSignature ???? CFBundleVersion diff --git a/PasscodeLock/PasscodeLock/EnterOptionalPasscodeState.swift b/PasscodeLock/PasscodeLock/EnterOptionalPasscodeState.swift new file mode 100644 index 00000000..a58ea272 --- /dev/null +++ b/PasscodeLock/PasscodeLock/EnterOptionalPasscodeState.swift @@ -0,0 +1,74 @@ +// +// EnterOptionalPasscodeState.swift +// PasscodeLock +// +// Created by Yanko Dimitrov on 8/28/15. +// Copyright © 2015 Yanko Dimitrov. All rights reserved. +// + +import Foundation + +struct EnterOptionalPasscodeState: PasscodeLockStateType { + + let title: String + let description: String + let isCancellableAction: Bool + var isTouchIDAllowed = true + + static let incorrectPasscodeAttemptsKey = "incorrectPasscodeAttempts" + static var incorrectPasscodeAttempts: Int { + get { + return UserDefaults.standard.integer(forKey: incorrectPasscodeAttemptsKey) + } + set { + UserDefaults.standard.set(newValue, forKey: incorrectPasscodeAttemptsKey) + } + } + + private var isNotificationSent = false + + init(allowCancellation: Bool = true) { + + isCancellableAction = allowCancellation + title = localizedStringFor("PasscodeLockEnterTitle", comment: "Enter passcode title") + description = localizedStringFor("PasscodeLockEnterDescription", comment: "Enter passcode description") + } + + mutating func acceptPasscode(_ passcode: [String], fromLock lock: PasscodeLockType) { + + guard let currentPasscode = lock.repository.passcode else { + return + } + + var incorrectPasscodeAttempts = EnterPasscodeState.incorrectPasscodeAttempts + if passcode == currentPasscode { + + lock.delegate?.passcodeLockDidSucceed(lock) + incorrectPasscodeAttempts = 0 + } else { + + incorrectPasscodeAttempts += 1 + + if incorrectPasscodeAttempts >= lock.configuration.maximumInccorectPasscodeAttempts { + + postNotification() + incorrectPasscodeAttempts = 0 + } + + lock.delegate?.passcodeLockDidFail(lock) + } + + EnterPasscodeState.incorrectPasscodeAttempts = incorrectPasscodeAttempts + } + + fileprivate mutating func postNotification() { + + guard !isNotificationSent else { return } + + let center = NotificationCenter.default + + center.post(name: Notification.Name(rawValue: PasscodeLockIncorrectPasscodeNotification), object: nil) + + isNotificationSent = true + } +} diff --git a/PasscodeLock/PasscodeLock/PasscodeLock.swift b/PasscodeLock/PasscodeLock/PasscodeLock.swift index 82b5fc3c..e2cbbc60 100644 --- a/PasscodeLock/PasscodeLock/PasscodeLock.swift +++ b/PasscodeLock/PasscodeLock/PasscodeLock.swift @@ -12,7 +12,7 @@ import LocalAuthentication open class PasscodeLock: PasscodeLockType { open weak var delegate: PasscodeLockTypeDelegate? - open let configuration: PasscodeLockConfigurationType + open var configuration: PasscodeLockConfigurationType open var repository: PasscodeRepositoryType { return configuration.repository @@ -44,7 +44,9 @@ open class PasscodeLock: PasscodeLockType { if passcode.count >= configuration.passcodeLength { - lockState.acceptPasscode(passcode, fromLock: self) + // handles "requires exclusive access" error at Swift 4 + var lockStateCopy = lockState + lockStateCopy.acceptPasscode(passcode, fromLock: self) passcode.removeAll(keepingCapacity: true) } } diff --git a/PasscodeLock/PasscodeLockPresenter.swift b/PasscodeLock/PasscodeLockPresenter.swift index 3904e3ef..c6a9c071 100644 --- a/PasscodeLock/PasscodeLockPresenter.swift +++ b/PasscodeLock/PasscodeLockPresenter.swift @@ -16,7 +16,7 @@ open class PasscodeLockPresenter { let window = UIWindow(frame: UIScreen.main.bounds) - window.windowLevel = 0 + window.windowLevel = UIWindow.Level(rawValue: 0) window.makeKeyAndVisible() return window @@ -25,12 +25,12 @@ open class PasscodeLockPresenter { fileprivate let passcodeConfiguration: PasscodeLockConfigurationType open var isPasscodePresented = false - open let passcodeLockVC: PasscodeLockViewController + open var passcodeLockVC: PasscodeLockViewController public init(mainWindow window: UIWindow?, configuration: PasscodeLockConfigurationType, viewController: PasscodeLockViewController) { mainWindow = window - mainWindow?.windowLevel = 1 + mainWindow?.windowLevel = UIWindow.Level(rawValue: 1) passcodeConfiguration = configuration passcodeLockVC = viewController @@ -64,7 +64,7 @@ open class PasscodeLockPresenter { guard !isPasscodePresented else { return } isPasscodePresented = true - passcodeLockWindow.windowLevel = 2 + passcodeLockWindow.windowLevel = UIWindow.Level(rawValue: 2) toggleKeyboardVisibility(hide: true) @@ -83,7 +83,7 @@ open class PasscodeLockPresenter { open func dismissPasscodeLock(animated: Bool = true) { isPasscodePresented = false - mainWindow?.windowLevel = 1 + mainWindow?.windowLevel = UIWindow.Level(rawValue: 1) mainWindow?.makeKeyAndVisible() if animated { @@ -99,14 +99,14 @@ open class PasscodeLockPresenter { }, completion: { [weak self] _ in - self?.passcodeLockWindow.windowLevel = 0 + self?.passcodeLockWindow.windowLevel = UIWindow.Level(rawValue: 0) self?.passcodeLockWindow.rootViewController = nil self?.passcodeLockWindow.alpha = 1 self?.toggleKeyboardVisibility(hide: false) } ) } else { - passcodeLockWindow.windowLevel = 0 + passcodeLockWindow.windowLevel = UIWindow.Level(rawValue: 0) passcodeLockWindow.rootViewController = nil toggleKeyboardVisibility(hide: false) } diff --git a/PasscodeLock/PasscodeLockViewController.swift b/PasscodeLock/PasscodeLockViewController.swift index a14a58fa..1fac1e37 100644 --- a/PasscodeLock/PasscodeLockViewController.swift +++ b/PasscodeLock/PasscodeLockViewController.swift @@ -12,6 +12,7 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat public enum LockState { case enterPasscode + case enterOptionalPasscode case setPasscode case changePasscode case removePasscode @@ -20,6 +21,7 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat switch self { case .enterPasscode: return EnterPasscodeState() + case .enterOptionalPasscode: return EnterOptionalPasscodeState() case .setPasscode: return SetPasscodeState() case .changePasscode: return ChangePasscodeState() case .removePasscode: return EnterPasscodeState(allowCancellation: true) @@ -37,14 +39,21 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat open var successCallback: ((_ lock: PasscodeLockType) -> Void)? open var dismissCompletionCallback: (()->Void)? + open var cancelCompletionCallback: (()->Void)? + open var wrongPasswordCallback: ((_ attemptNo: Int) -> Void)? + open var tooManyAttemptsCallback: ((_ attemptNo: Int)->Void)? open var animateOnDismiss: Bool open var notificationCenter: NotificationCenter? - internal let passcodeConfiguration: PasscodeLockConfigurationType - internal var passcodeLock: PasscodeLockType - internal var isPlaceholdersAnimationCompleted = true + open var overrideTitleText: String? = nil + open var overrideDescriptionText: String? = nil + + open var passcodeConfiguration: PasscodeLockConfigurationType + open var passcodeLock: PasscodeLockType + open var isPlaceholdersAnimationCompleted = true + open var extraCallbacks = false - fileprivate var shouldTryToAuthenticateWithBiometrics = true + open var shouldTryToAuthenticateWithBiometrics = true // MARK: - Initializers @@ -62,10 +71,20 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat passcodeLock.delegate = self notificationCenter = NotificationCenter.default } - - public convenience init(state: LockState, configuration: PasscodeLockConfigurationType, animateOnDismiss: Bool = true) { + + public convenience init(state: LockState, configuration: PasscodeLockConfigurationType, darkUI: Bool = false, animateOnDismiss: Bool = true) { + + if ( darkUI ) { + + self.init(state: state.getState(), configuration: configuration, animateOnDismiss: animateOnDismiss, nibName: "DarkPasscodeLockView", bundle: nil) + + } else { + + self.init(state: state.getState(), configuration: configuration, animateOnDismiss: animateOnDismiss) + } + + self.extraCallbacks = ( state == .enterPasscode || state == .enterOptionalPasscode ) - self.init(state: state.getState(), configuration: configuration, animateOnDismiss: animateOnDismiss) } public required init(coder aDecoder: NSCoder) { @@ -90,6 +109,7 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat open override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + EnterPasscodeState.incorrectPasscodeAttempts = 0 updatePasscodeView() } @@ -101,37 +121,44 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat authenticateWithBiometrics() } } + + open override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + updatePasscodeView() + } internal func updatePasscodeView() { - titleLabel?.text = passcodeLock.state.title - descriptionLabel?.text = passcodeLock.state.description + titleLabel?.text = ( overrideTitleText != nil ? overrideTitleText! : passcodeLock.state.title ) + descriptionLabel?.text = ( overrideDescriptionText != nil ? overrideDescriptionText! : passcodeLock.state.description ) cancelButton?.isHidden = !passcodeLock.state.isCancellableAction - touchIDButton?.isHidden = !passcodeLock.isTouchIDAllowed + touchIDButton?.isHidden = !passcodeLock.isTouchIDAllowed || passcodeLock.configuration.shouldDisableTouchIDButton } // MARK: - Events fileprivate func setupEvents() { - notificationCenter?.addObserver(self, selector: #selector(PasscodeLockViewController.appWillEnterForegroundHandler(_:)), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil) - notificationCenter?.addObserver(self, selector: #selector(PasscodeLockViewController.appDidEnterBackgroundHandler(_:)), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil) + notificationCenter?.addObserver(self, selector: #selector(PasscodeLockViewController.appWillEnterForegroundHandler(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) + notificationCenter?.addObserver(self, selector: #selector(PasscodeLockViewController.appDidEnterBackgroundHandler(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil) } fileprivate func clearEvents() { - notificationCenter?.removeObserver(self, name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil) - notificationCenter?.removeObserver(self, name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil) + notificationCenter?.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil) + notificationCenter?.removeObserver(self, name: UIApplication.didEnterBackgroundNotification, object: nil) } - open func appWillEnterForegroundHandler(_ notification: Notification) { + @objc open func appWillEnterForegroundHandler(_ notification: Notification) { + shouldTryToAuthenticateWithBiometrics = true + if passcodeConfiguration.shouldRequestTouchIDImmediately { authenticateWithBiometrics() } } - open func appDidEnterBackgroundHandler(_ notification: Notification) { + @objc open func appDidEnterBackgroundHandler(_ notification: Notification) { shouldTryToAuthenticateWithBiometrics = false } @@ -147,7 +174,10 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat @IBAction func cancelButtonTap(_ sender: UIButton) { - dismissPasscodeLock(passcodeLock) + self.dismissPasscodeLock(passcodeLock, completionHandler: { + [weak self] in + self?.cancelCompletionCallback?() + }) } @IBAction func deleteSignButtonTap(_ sender: UIButton) { @@ -175,11 +205,10 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat // if presented as modal if presentingViewController?.presentedViewController == self { - dismiss(animated: animateOnDismiss, completion: { [weak self] _ in - - self?.dismissCompletionCallback?() - + dismiss(animated: animateOnDismiss, completion: { + [weak self] in completionHandler?() + self?.dismissCompletionCallback?() }) return @@ -190,9 +219,9 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat navigationController?.popViewController(animated: animateOnDismiss) } - dismissCompletionCallback?() - completionHandler?() + + dismissCompletionCallback?() } // MARK: - Animations @@ -246,7 +275,8 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat deleteSignButton?.isEnabled = true animatePlaceholders(placeholders, toState: .inactive) - dismissPasscodeLock(lock, completionHandler: { [weak self] _ in + dismissPasscodeLock(lock, completionHandler: { + [weak self] in self?.successCallback?(lock) }) } @@ -254,6 +284,28 @@ open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegat open func passcodeLockDidFail(_ lock: PasscodeLockType) { animateWrongPassword() + + if ( self.extraCallbacks ) { + let attemptNo = EnterPasscodeState.incorrectPasscodeAttempts + 1 + let maxAttempts = lock.configuration.maximumInccorectPasscodeAttempts + let shouldDissmissOnTooManyAttempts = lock.configuration.shouldDismissOnTooManyAttempts && lock.state.isCancellableAction + + if (( maxAttempts >= 0) && ( attemptNo >= maxAttempts )) { + if ( shouldDissmissOnTooManyAttempts ) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.6, execute: { + + self.dismissPasscodeLock(lock, completionHandler: { + [weak self] in + self?.tooManyAttemptsCallback?(attemptNo) + }) + }) + } else { + self.tooManyAttemptsCallback?(attemptNo) + } + } else { + self.wrongPasswordCallback?(attemptNo) + } + } } open func passcodeLockDidChangeState(_ lock: PasscodeLockType) { diff --git a/PasscodeLock/Protocols/PasscodeLockConfigurationType.swift b/PasscodeLock/Protocols/PasscodeLockConfigurationType.swift index ec158e75..921df911 100644 --- a/PasscodeLock/Protocols/PasscodeLockConfigurationType.swift +++ b/PasscodeLock/Protocols/PasscodeLockConfigurationType.swift @@ -14,6 +14,20 @@ public protocol PasscodeLockConfigurationType { var passcodeLength: Int {get} var isTouchIDAllowed: Bool {get set} var shouldRequestTouchIDImmediately: Bool {get} + var shouldDisableTouchIDButton: Bool {get} var touchIdReason: String? {get set} var maximumInccorectPasscodeAttempts: Int {get} + var shouldDismissOnTooManyAttempts: Bool {get} +} + +public extension PasscodeLockConfigurationType { + + var passcodeLength: Int { + return 4 + } + + var maximumInccorectPasscodeAttempts: Int { + return -1 + } + } diff --git a/PasscodeLock/Protocols/PasscodeLockType.swift b/PasscodeLock/Protocols/PasscodeLockType.swift index 434c87d6..c5390112 100644 --- a/PasscodeLock/Protocols/PasscodeLockType.swift +++ b/PasscodeLock/Protocols/PasscodeLockType.swift @@ -10,7 +10,7 @@ import Foundation public protocol PasscodeLockType { - weak var delegate: PasscodeLockTypeDelegate? {get set} + var delegate: PasscodeLockTypeDelegate? {get set} var configuration: PasscodeLockConfigurationType {get} var repository: PasscodeRepositoryType {get} var state: PasscodeLockStateType {get} diff --git a/PasscodeLock/Views/DarkPasscodeLockView.xib b/PasscodeLock/Views/DarkPasscodeLockView.xib new file mode 100644 index 00000000..3c695973 --- /dev/null +++ b/PasscodeLock/Views/DarkPasscodeLockView.xib @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PasscodeLock/Views/PasscodeLockView.xib b/PasscodeLock/Views/PasscodeLockView.xib index c17dae99..5fde72c4 100644 --- a/PasscodeLock/Views/PasscodeLockView.xib +++ b/PasscodeLock/Views/PasscodeLockView.xib @@ -1,8 +1,12 @@ - - + + + + + - + + @@ -14,94 +18,94 @@ - - - - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -116,23 +120,23 @@ - + diff --git a/PasscodeLock/Views/PasscodeSignButton.swift b/PasscodeLock/Views/PasscodeSignButton.swift index 01f7eff6..0ed0d7fe 100644 --- a/PasscodeLock/Views/PasscodeSignButton.swift +++ b/PasscodeLock/Views/PasscodeSignButton.swift @@ -75,12 +75,12 @@ open class PasscodeSignButton: UIButton { addTarget(self, action: #selector(PasscodeSignButton.handleTouchUp), for: [.touchUpInside, .touchDragOutside, .touchCancel]) } - func handleTouchDown() { + @objc func handleTouchDown() { animateBackgroundColor(highlightBackgroundColor) } - func handleTouchUp() { + @objc func handleTouchUp() { animateBackgroundColor(defaultBackgroundColor) } diff --git a/PasscodeLock/Views/PasscodeSignPlaceholderView.swift b/PasscodeLock/Views/PasscodeSignPlaceholderView.swift index 2191a302..3794b9b2 100644 --- a/PasscodeLock/Views/PasscodeSignPlaceholderView.swift +++ b/PasscodeLock/Views/PasscodeSignPlaceholderView.swift @@ -38,6 +38,13 @@ open class PasscodeSignPlaceholderView: UIView { } } + @IBInspectable + open var borderColor: UIColor = UIColor.gray { + didSet { + setupView() + } + } + public override init(frame: CGRect) { super.init(frame: frame) @@ -59,15 +66,15 @@ open class PasscodeSignPlaceholderView: UIView { layer.cornerRadius = 8 layer.borderWidth = 1 - layer.borderColor = activeColor.cgColor + layer.borderColor = borderColor.cgColor backgroundColor = inactiveColor } fileprivate func colorsForState(_ state: State) -> (backgroundColor: UIColor, borderColor: UIColor) { switch state { - case .inactive: return (inactiveColor, activeColor) - case .active: return (activeColor, activeColor) + case .inactive: return (inactiveColor, borderColor) + case .active: return (activeColor, borderColor) case .error: return (errorColor, errorColor) } } diff --git a/PasscodeLock/en.lproj/PasscodeLock.strings b/PasscodeLock/en.lproj/PasscodeLock.strings index f71ef86c..7d2393de 100755 --- a/PasscodeLock/en.lproj/PasscodeLock.strings +++ b/PasscodeLock/en.lproj/PasscodeLock.strings @@ -19,7 +19,7 @@ "PasscodeLockConfirmDescription" = "Enter the passcode again."; /* Change Passcode State */ -"PasscodeLockChangeTitle" = "Enter Old Passcode"; +"PasscodeLockChangeTitle" = "Change Passcode"; "PasscodeLockChangeDescription" = "Enter your old passcode."; /* Passcode Mismatch State */ @@ -30,4 +30,4 @@ "PasscodeLockTouchIDReason" = "Authentication required to proceed"; /* Touch ID Fallback Button */ -"PasscodeLockTouchIDButton" = "Enter Passcode"; \ No newline at end of file +"PasscodeLockTouchIDButton" = "Enter Passcode"; diff --git a/PasscodeLockDemo/Base.lproj/Main.storyboard b/PasscodeLockDemo/Base.lproj/Main.storyboard index bea52e36..0a16279a 100644 --- a/PasscodeLockDemo/Base.lproj/Main.storyboard +++ b/PasscodeLockDemo/Base.lproj/Main.storyboard @@ -1,8 +1,12 @@ - - + + + + + - + + @@ -14,44 +18,44 @@ - + - + - + @@ -59,8 +63,22 @@ + + - + @@ -69,8 +87,12 @@ + + + + @@ -81,6 +103,7 @@ + diff --git a/PasscodeLockDemo/PasscodeLockConfiguration.swift b/PasscodeLockDemo/PasscodeLockConfiguration.swift index 5a176bd7..082708d6 100644 --- a/PasscodeLockDemo/PasscodeLockConfiguration.swift +++ b/PasscodeLockDemo/PasscodeLockConfiguration.swift @@ -10,12 +10,16 @@ import Foundation import PasscodeLock struct PasscodeLockConfiguration: PasscodeLockConfigurationType { - + + var touchIdReason: String? let repository: PasscodeRepositoryType let passcodeLength = 4 var isTouchIDAllowed = true let shouldRequestTouchIDImmediately = true - let maximumInccorectPasscodeAttempts = -1 + let shouldDisableTouchIDButton = true + let maximumInccorectPasscodeAttempts = 3 + // let maximumInccorectPasscodeAttempts = -1 + let shouldDismissOnTooManyAttempts = true init(repository: PasscodeRepositoryType) { diff --git a/PasscodeLockDemo/PasscodeSettingsViewController.swift b/PasscodeLockDemo/PasscodeSettingsViewController.swift index 7eafbdff..741594eb 100644 --- a/PasscodeLockDemo/PasscodeSettingsViewController.swift +++ b/PasscodeLockDemo/PasscodeSettingsViewController.swift @@ -15,6 +15,7 @@ class PasscodeSettingsViewController: UIViewController { @IBOutlet weak var changePasscodeButton: UIButton! @IBOutlet weak var testTextField: UITextField! @IBOutlet weak var testActivityButton: UIButton! + @IBOutlet weak var authenticateNowButton: UIButton! fileprivate let configuration: PasscodeLockConfigurationType @@ -46,6 +47,7 @@ class PasscodeSettingsViewController: UIViewController { let hasPasscode = configuration.repository.hasPasscode changePasscodeButton.isHidden = !hasPasscode + authenticateNowButton.isHidden = !hasPasscode passcodeSwitch.isOn = hasPasscode } @@ -82,6 +84,33 @@ class PasscodeSettingsViewController: UIViewController { present(passcodeLock, animated: true, completion: nil) } + @IBAction func authenticateNowButtonTap(_ sender: UIButton) { + + let repo = UserDefaultsPasscodeRepository() + let config = PasscodeLockConfiguration(repository: repo) + + let passcodeLock = PasscodeLockViewController(state: .enterOptionalPasscode, configuration: config, darkUI: sender.tag == 0 ? false : true) + + passcodeLock.successCallback = { lock in + NSLog("Success") + } + + passcodeLock.cancelCompletionCallback = { _ in + NSLog("Cancelled") + } + + passcodeLock.wrongPasswordCallback = { attemptNo in + NSLog("Wrong password attempt no %d", attemptNo) + } + + passcodeLock.tooManyAttemptsCallback = { attemptNo in + NSLog("Maximum amount of %d attempts reached", attemptNo) + } + + present(passcodeLock, animated: true, completion: nil) + + } + @IBAction func testAlertButtonTap(_ sender: UIButton) { let alertVC = UIAlertController(title: "Test", message: "", preferredStyle: .alert) diff --git a/PasscodeLockTests/Fakes/FakePasscodeLock.swift b/PasscodeLockTests/Fakes/FakePasscodeLock.swift index 518710e3..8975eaa9 100644 --- a/PasscodeLockTests/Fakes/FakePasscodeLock.swift +++ b/PasscodeLockTests/Fakes/FakePasscodeLock.swift @@ -25,7 +25,7 @@ class FakePasscodeLock: PasscodeLockType { self.configuration = configuration } - func addSign(sign: String) { + func addSign(_ sign: String) { } @@ -33,7 +33,7 @@ class FakePasscodeLock: PasscodeLockType { } - func changeStateTo(state: PasscodeLockStateType) { + func changeStateTo(_ state: PasscodeLockStateType) { lockState = state changeStateCalled = true diff --git a/PasscodeLockTests/Fakes/FakePasscodeLockConfiguration.swift b/PasscodeLockTests/Fakes/FakePasscodeLockConfiguration.swift index 46c871bc..fd3004b8 100644 --- a/PasscodeLockTests/Fakes/FakePasscodeLockConfiguration.swift +++ b/PasscodeLockTests/Fakes/FakePasscodeLockConfiguration.swift @@ -16,7 +16,9 @@ class FakePasscodeLockConfiguration: PasscodeLockConfigurationType { let maximumInccorectPasscodeAttempts = 3 let shouldRequestTouchIDImmediately = false var touchIdReason: String? = nil - + var shouldDisableTouchIDButton: Bool = true + var shouldDismissOnTooManyAttempts: Bool = true + init(repository: PasscodeRepositoryType) { self.repository = repository diff --git a/PasscodeLockTests/Fakes/FakePasscodeLockDelegate.swift b/PasscodeLockTests/Fakes/FakePasscodeLockDelegate.swift index 3d82b9bf..72d4b85a 100644 --- a/PasscodeLockTests/Fakes/FakePasscodeLockDelegate.swift +++ b/PasscodeLockTests/Fakes/FakePasscodeLockDelegate.swift @@ -10,9 +10,9 @@ import Foundation class FakePasscodeLockDelegate: PasscodeLockTypeDelegate { - func passcodeLockDidSucceed(lock: PasscodeLockType) {} - func passcodeLockDidFail(lock: PasscodeLockType) {} - func passcodeLockDidChangeState(lock: PasscodeLockType) {} - func passcodeLock(lock: PasscodeLockType, addedSignAtIndex index: Int) {} - func passcodeLock(lock: PasscodeLockType, removedSignAtIndex index: Int) {} + func passcodeLockDidSucceed(_ lock: PasscodeLockType) {} + func passcodeLockDidFail(_ lock: PasscodeLockType) {} + func passcodeLockDidChangeState(_ lock: PasscodeLockType) {} + func passcodeLock(_ lock: PasscodeLockType, addedSignAtIndex index: Int) {} + func passcodeLock(_ lock: PasscodeLockType, removedSignAtIndex index: Int) {} } diff --git a/PasscodeLockTests/Fakes/FakePasscodeRepository.swift b/PasscodeLockTests/Fakes/FakePasscodeRepository.swift index 657b67fc..9c210521 100644 --- a/PasscodeLockTests/Fakes/FakePasscodeRepository.swift +++ b/PasscodeLockTests/Fakes/FakePasscodeRepository.swift @@ -18,7 +18,7 @@ class FakePasscodeRepository: PasscodeRepositoryType { var savePasscodeCalled = false var savedPasscode = [String]() - func savePasscode(passcode: [String]) { + func savePasscode(_ passcode: [String]) { savePasscodeCalled = true savedPasscode = passcode diff --git a/PasscodeLockTests/Fakes/FakePasscodeState.swift b/PasscodeLockTests/Fakes/FakePasscodeState.swift index 61bc3e3c..e2b345a4 100644 --- a/PasscodeLockTests/Fakes/FakePasscodeState.swift +++ b/PasscodeLockTests/Fakes/FakePasscodeState.swift @@ -21,7 +21,7 @@ class FakePasscodeState: PasscodeLockStateType { init() {} - func acceptPasscode(passcode: [String], fromLock lock: PasscodeLockType) { + func acceptPasscode(_ passcode: [String], fromLock lock: PasscodeLockType) { acceptedPasscode = passcode acceptPaccodeCalled = true diff --git a/PasscodeLockTests/PasscodeLock/ChangePasscodeStateTests.swift b/PasscodeLockTests/PasscodeLock/ChangePasscodeStateTests.swift index eade20ca..721b5ce5 100644 --- a/PasscodeLockTests/PasscodeLock/ChangePasscodeStateTests.swift +++ b/PasscodeLockTests/PasscodeLock/ChangePasscodeStateTests.swift @@ -31,7 +31,7 @@ class ChangePasscodeStateTests: XCTestCase { var didChangedState = false - override func passcodeLockDidChangeState(lock: PasscodeLockType) { + override func passcodeLockDidChangeState(_ lock: PasscodeLockType) { didChangedState = true } @@ -52,7 +52,7 @@ class ChangePasscodeStateTests: XCTestCase { var called = false - override func passcodeLockDidFail(lock: PasscodeLockType) { + override func passcodeLockDidFail(_ lock: PasscodeLockType) { called = true } diff --git a/PasscodeLockTests/PasscodeLock/ConfirmPasscodeStateTests.swift b/PasscodeLockTests/PasscodeLock/ConfirmPasscodeStateTests.swift index f122f702..47b9dfbc 100644 --- a/PasscodeLockTests/PasscodeLock/ConfirmPasscodeStateTests.swift +++ b/PasscodeLockTests/PasscodeLock/ConfirmPasscodeStateTests.swift @@ -32,7 +32,7 @@ class ConfirmPasscodeStateTests: XCTestCase { var called = false - override func passcodeLockDidSucceed(lock: PasscodeLockType) { + override func passcodeLockDidSucceed(_ lock: PasscodeLockType) { called = true } @@ -61,12 +61,12 @@ class ConfirmPasscodeStateTests: XCTestCase { var didFailed = false var didChangedState = false - override func passcodeLockDidFail(lock: PasscodeLockType) { + override func passcodeLockDidFail(_ lock: PasscodeLockType) { didFailed = true } - override func passcodeLockDidChangeState(lock: PasscodeLockType) { + override func passcodeLockDidChangeState(_ lock: PasscodeLockType) { didChangedState = true } diff --git a/PasscodeLockTests/PasscodeLock/EnterPasscodeStateTests.swift b/PasscodeLockTests/PasscodeLock/EnterPasscodeStateTests.swift index 2e4dcf94..f17b43eb 100644 --- a/PasscodeLockTests/PasscodeLock/EnterPasscodeStateTests.swift +++ b/PasscodeLockTests/PasscodeLock/EnterPasscodeStateTests.swift @@ -8,19 +8,19 @@ import XCTest -class NotificaionObserver: NSObject { +class NotificationObserver: NSObject { var called = false var callCounter = 0 func observe(notification: String) { - let center = NSNotificationCenter.defaultCenter() + let center = NotificationCenter.default - center.addObserver(self, selector: "handle:", name: notification, object: nil) + center.addObserver(self, selector: #selector(self.handle), name: NSNotification.Name(rawValue: notification), object: nil) } - func handle(notification: NSNotification) { + @objc func handle(notification: NSNotification) { called = true callCounter += 1 @@ -50,7 +50,7 @@ class EnterPasscodeStateTests: XCTestCase { var called = false - override func passcodeLockDidSucceed(lock: PasscodeLockType) { + override func passcodeLockDidSucceed(_ lock: PasscodeLockType) { called = true } @@ -70,7 +70,7 @@ class EnterPasscodeStateTests: XCTestCase { var called = false - override func passcodeLockDidFail(lock: PasscodeLockType) { + override func passcodeLockDidFail(_ lock: PasscodeLockType) { called = true } @@ -86,9 +86,9 @@ class EnterPasscodeStateTests: XCTestCase { func testIncorrectPasscodeNotification() { - let observer = NotificaionObserver() + let observer = NotificationObserver() - observer.observe(PasscodeLockIncorrectPasscodeNotification) + observer.observe(notification: PasscodeLockIncorrectPasscodeNotification) passcodeState.acceptPasscode(["0"], fromLock: passcodeLock) passcodeState.acceptPasscode(["0"], fromLock: passcodeLock) @@ -99,9 +99,9 @@ class EnterPasscodeStateTests: XCTestCase { func testIncorrectPasscodeSendNotificationOnce() { - let observer = NotificaionObserver() + let observer = NotificationObserver() - observer.observe(PasscodeLockIncorrectPasscodeNotification) + observer.observe(notification: PasscodeLockIncorrectPasscodeNotification) passcodeState.acceptPasscode(["0"], fromLock: passcodeLock) passcodeState.acceptPasscode(["0"], fromLock: passcodeLock) diff --git a/PasscodeLockTests/PasscodeLock/PasscodeLockTests.swift b/PasscodeLockTests/PasscodeLock/PasscodeLockTests.swift index efb192a3..0ec9f6b9 100644 --- a/PasscodeLockTests/PasscodeLock/PasscodeLockTests.swift +++ b/PasscodeLockTests/PasscodeLock/PasscodeLockTests.swift @@ -29,7 +29,7 @@ class PasscodeLockTests: XCTestCase { var called = false - override func passcodeLockDidChangeState(lock: PasscodeLockType) { + override func passcodeLockDidChangeState(_ lock: PasscodeLockType) { called = true } @@ -51,7 +51,7 @@ class PasscodeLockTests: XCTestCase { var called = false var signIndex = 0 - override func passcodeLock(lock: PasscodeLockType, addedSignAtIndex index: Int) { + override func passcodeLock(_ lock: PasscodeLockType, addedSignAtIndex index: Int) { called = true signIndex = index @@ -78,7 +78,7 @@ class PasscodeLockTests: XCTestCase { var called = false var signIndex = 0 - override func passcodeLock(lock: PasscodeLockType, removedSignAtIndex index: Int) { + override func passcodeLock(_ lock: PasscodeLockType, removedSignAtIndex index: Int) { called = true signIndex = index diff --git a/PasscodeLockTests/PasscodeLock/SetPasscodeStateTests.swift b/PasscodeLockTests/PasscodeLock/SetPasscodeStateTests.swift index 20ef1214..e90429a0 100644 --- a/PasscodeLockTests/PasscodeLock/SetPasscodeStateTests.swift +++ b/PasscodeLockTests/PasscodeLock/SetPasscodeStateTests.swift @@ -31,7 +31,7 @@ class SetPasscodeStateTests: XCTestCase { var didChangedState = false - override func passcodeLockDidChangeState(lock: PasscodeLockType) { + override func passcodeLockDidChangeState(_ lock: PasscodeLockType) { didChangedState = true } diff --git a/README.md b/README.md index de7300db..ef02c353 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # PasscodeLock A Swift implementation of passcode lock for iOS with TouchID authentication. -Originally created by [@yankodimitrov](https://github.com/yankodimitrov/SwiftPasscodeLock), hope you're doing well. +Originally created by [@yankodimitrov](https://github.com/yankodimitrov/SwiftPasscodeLock), then forded by [@velikanov](https://github.com/velikanov/SwiftPasscodeLock) hope you're doing well. ## Installation -PasscodeLock requires Swift 2.0 and Xcode 7 +PasscodeLock requires Swift 3.0 and Xcode 8 ### [CocoaPods](http://cocoapods.org/) @@ -16,9 +16,12 @@ To integrate PasscodeLock into your Xcode project using CocoaPods, specify it in ```ruby source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, ‘8.0’ -pod 'PasscodeLock', '~> 1.0.2' +use_frameworks! + +target 'your_target_name_here' +pod 'PasscodeLock', :git => ‘https://github.com/oskarirauta/SwiftPasscodeLock.git' ``` Then, run the following command: @@ -31,7 +34,7 @@ $ pod install Add the following line to your [Cartfile](https://github.com/carthage/carthage) ```swift -github "velikanov/SwiftPasscodeLock" +github “oskarirauta/SwiftPasscodeLock" ``` ## Usage @@ -60,11 +63,14 @@ import UIKit import PasscodeLock class PasscodeLockConfiguration: PasscodeLockConfigurationType { + var touchIdReason: String? let repository: PasscodeRepositoryType var passcodeLength = 4 // Specify the required amount of passcode digits var isTouchIDAllowed = true // Enable Touch ID var shouldRequestTouchIDImmediately = true // Use Touch ID authentication immediately + var shouldDisableTouchIDButton = true // Hides manual touchID activation button from enter code view var maximumInccorectPasscodeAttempts = 3 // Maximum incorrect passcode attempts + var shouldDismissOnTooManyAttempts = true // When cancellation is available, dismiss code input view after too many wrong code attempts init(repository: PasscodeRepositoryType) { self.repository = repository @@ -87,7 +93,16 @@ let passcodeViewController = PasscodeLockViewController(state: .SetPasscode, con presentViewController(passcodeViewController, animated: true, completion: nil) ``` -You can present the `PasscodeLockViewController` in one of the four initial states using the `LockState` enumeration options: `.EnterPasscode`, `.SetPasscode`, `.ChangePasscode`, `.RemovePasscode`. +You can present the `PasscodeLockViewController` in one of the four initial states using the `LockState` enumeration options: `.enterPasscode`, `.enterOptionalPasscode`, `.setPasscode`, `.changePasscode`, `.removePasscode`. + +Following callbacks are available: +```swift + open var successCallback: ((_ lock: PasscodeLockType) -> Void)? + open var dismissCompletionCallback: (()->Void)? + open var cancelCompletionCallback: (()->Void)? + open var wrongPasswordCallback: ((_ attemptNo: Int) -> Void)? + open var tooManyAttemptsCallback: ((_ attemptNo: Int)->Void)? +``` Also you can set the initial passcode lock state to your own implementation of the `PasscodeLockStateType` protocol. @@ -99,13 +114,13 @@ The PasscodeLock will look for `PasscodeLockView.xib` inside your app bundle and Keep in mind that when using custom classes that are defined in another module, you'll need to set the Module field to that module's name in the Identity Inspector: - + Then connect the `view` outlet to the view of your `xib` file and make sure to conenct the remaining `IBOutlet`s and `IBAction`s. PasscodeLock comes with two view components: `PasscodeSignPlaceholderView` and `PasscodeSignButton` that you can use to create your own custom designs. Both classes are `@IBDesignable` and `@IBInspectable`, so you can see their appearance and change their properties right inside the interface builder: - + ### Localization