Self-maintained passcode lock

- No cancel button anywhere in the passcode lock yet
- Poor UI
This commit is contained in:
Yishi Lin 2018-01-29 03:23:34 +08:00
parent 30ae08bed5
commit da3c4f0bc0
10 changed files with 298 additions and 210 deletions

View file

@ -0,0 +1,59 @@
//
// PasscodeLockPresenter.swift
// PasscodeLock
//
// Created by Yanko Dimitrov on 8/29/15.
// Copyright © 2015 Yanko Dimitrov. All rights reserved.
//
import UIKit
open class PasscodeLockPresenter {
fileprivate var mainWindow: UIWindow?
fileprivate lazy var passcodeLockWindow = UIWindow(frame: UIScreen.main.bounds)
open var isPasscodePresented = false
open let passcodeLockVC: PasscodeLockViewController
public init(mainWindow window: UIWindow?, viewController: PasscodeLockViewController) {
mainWindow = window
passcodeLockVC = viewController
}
public convenience init(mainWindow window: UIWindow?) {
let passcodeLockVC = PasscodeLockViewController()
self.init(mainWindow: window, viewController: passcodeLockVC)
}
open func present(windowLevel: CGFloat?) {
guard PasscodeLock.shared.hasPasscode else { return }
guard !isPasscodePresented else { return }
isPasscodePresented = true
mainWindow?.endEditing(true)
moveWindowsToFront(windowLevel: windowLevel)
passcodeLockWindow.isHidden = false
let userDismissCompletionCallback = passcodeLockVC.dismissCompletionCallback
passcodeLockVC.dismissCompletionCallback = { [weak self] in
userDismissCompletionCallback?()
self?.dismiss()
}
passcodeLockWindow.rootViewController = passcodeLockVC
}
open func dismiss() {
isPasscodePresented = false
passcodeLockWindow.isHidden = true
passcodeLockWindow.rootViewController = nil
}
fileprivate func moveWindowsToFront(windowLevel: CGFloat?) {
let windowLevel = windowLevel ?? UIWindowLevelNormal
let maxWinLevel = max(windowLevel, UIWindowLevelNormal)
passcodeLockWindow.windowLevel = maxWinLevel + 1
}
}

View file

@ -0,0 +1,125 @@
//
// PasscodeLockViewController.swift
// PasscodeLock
//
// Created by Yanko Dimitrov on 8/28/15.
// Copyright © 2015 Yanko Dimitrov. All rights reserved.
//
import UIKit
import LocalAuthentication
open class PasscodeLockViewController: UIViewController {
open var dismissCompletionCallback: (()->Void)?
open var successCallback: (()->Void)?
open var cancelCallback: (()->Void)?
lazy var enterPasscodeAlert: UIAlertController = {
let enterPasscodeAlert = UIAlertController(title: "Authenticate Pass", message: "Unlock with passcode for Pass", preferredStyle: .alert)
enterPasscodeAlert.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in
textField.placeholder = "passcode"
textField.isSecureTextEntry = true
textField.addTarget(self, action: #selector(self.passcodeTextFieldDidChange(_:)), for: UIControlEvents.editingChanged)
textField.clearButtonMode = UITextFieldViewMode.whileEditing
textField.becomeFirstResponder()
})
let myContext = LAContext()
var authError: NSError?
if #available(iOS 8.0, macOS 10.12.1, *) {
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
var biometryType = "Touch ID"
if #available(iOS 11.0, *) {
if myContext.biometryType == LABiometryType.faceID {
biometryType = "Face ID"
}
}
let bioAction = UIAlertAction(title: "Use " + biometryType, style: .default) { (action:UIAlertAction) -> Void in
self.authenticate()
}
enterPasscodeAlert.addAction(bioAction)
}
}
return enterPasscodeAlert
}()
open override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
}
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
authenticate()
}
internal func dismissPasscodeLock(completionHandler: (() -> Void)? = nil) {
// clean up the textfield
enterPasscodeAlert.textFields?[0].text = ""
if presentingViewController?.presentedViewController == self {
// if presented as modal
dismiss(animated: true, completion: { [weak self] in
self?.dismissCompletionCallback?()
completionHandler?()
})
// if pushed in a navigation controller
} else {
_ = navigationController?.popViewController(animated: true)
dismissCompletionCallback?()
completionHandler?()
}
}
// MARK: - PasscodeLockDelegate
open func passcodeLockDidSucceed() {
dismissPasscodeLock(completionHandler: successCallback)
}
open func passcodeLockDidCancel() {
dismissPasscodeLock(completionHandler: cancelCallback)
}
public func authenticate() {
print(enterPasscodeAlert.isBeingPresented)
let myContext = LAContext()
let myLocalizedReasonString = "Authentication is needed to access Pass."
var authError: NSError?
if #available(iOS 8.0, *) {
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in
if success {
DispatchQueue.main.async {
// user authenticated successfully, take appropriate action
self.passcodeLockDidSucceed()
}
} else {
// User did not authenticate successfully
self.showPasswordAlert()
}
}
} else {
// could not evaluate policy; look at authError and present an appropriate message to user
self.showPasswordAlert()
}
} else {
// fallback on earlier versions
self.showPasswordAlert()
}
}
@objc func passcodeTextFieldDidChange(_ sender: UITextField) {
// check whether the passcode is correct
if PasscodeLock.shared.check(passcode: sender.text ?? "") {
self.passcodeLockDidSucceed()
}
}
func showPasswordAlert() {
self.present(enterPasscodeAlert, animated: true, completion: nil)
}
}

View file

@ -1,28 +1,24 @@
//
// PasscodeRepository.swift
// pass
// PasscodeLock.swift
// PassKit
//
// Created by Mingshen Sun on 7/2/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
// Created by Yishi Lin on 28/1/2018.
// Copyright © 2017 Yishi Lin. All rights reserved.
//
import Foundation
import PasscodeLock
import LocalAuthentication
public class PasscodeLockRepository: PasscodeRepositoryType {
private let passcodeKey = "passcode.lock.passcode"
open class PasscodeLock {
public static let shared = PasscodeLock()
public var hasPasscode: Bool {
if passcode != nil {
return true
}
return false
fileprivate let passcodeKey = "passcode.lock.passcode"
fileprivate var passcode: String? {
return SharedDefaults[.passcodeKey]
}
private var passcode: String? {
return SharedDefaults[.passcodeKey]
public var hasPasscode: Bool {
return passcode != nil
}
public func save(passcode: String) {

View file

@ -1,26 +0,0 @@
//
// PasscodeLockConfiguration.swift
// pass
//
// Created by Mingshen Sun on 7/2/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
import Foundation
import PasscodeLock
public class PasscodeLockConfiguration: PasscodeLockConfigurationType {
public static let shared = PasscodeLockConfiguration()
public let repository: PasscodeRepositoryType
public let passcodeLength = 4
public var isTouchIDAllowed = SharedDefaults[.isTouchIDOn]
public let shouldRequestTouchIDImmediately = true
public let maximumInccorectPasscodeAttempts = 3
init() {
self.repository = PasscodeLockRepository()
}
}