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)
}
}