Skip to content

Commit

Permalink
Merge branch 'release/0.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
leilee committed Oct 10, 2016
2 parents 64b5d2e + 24a16bd commit 2b3682f
Show file tree
Hide file tree
Showing 21 changed files with 460 additions and 386 deletions.
22 changes: 11 additions & 11 deletions PassLock/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,27 @@ import Foundation
public typealias Password = String

public enum PassLockType {
case SetPassword
case ChangePassword
case RemovePassword
case Unlock
case setPassword
case changePassword
case removePassword
case unlock
}

extension PassLockType {

var title: String? {
switch self {
case .SetPassword: return "设置密码"
case .RemovePassword: return "关闭密码"
case .ChangePassword: return "更改密码"
case .setPassword: return "设置密码"
case .removePassword: return "关闭密码"
case .changePassword: return "更改密码"
default: return nil
}
}

var passwordInputTitle: String {
switch self {
case .SetPassword, .RemovePassword, .Unlock: return "请输入密码"
case .ChangePassword: return "请输入旧密码"
case .setPassword, .removePassword, .unlock: return "请输入密码"
case .changePassword: return "请输入旧密码"
}
}

Expand All @@ -43,7 +43,7 @@ public struct PasswordViewConfiguration {
let strokeHeight: CGFloat
let strokeColor: UIColor

init(digit: Int = 4, spacing: CGFloat = 20, strokeHeight: CGFloat = 2, strokeColor: UIColor = UIColor.blackColor()) {
init(digit: Int = 4, spacing: CGFloat = 20, strokeHeight: CGFloat = 2, strokeColor: UIColor = UIColor.black) {
self.digit = digit
self.spacing = spacing
self.strokeHeight = strokeHeight
Expand All @@ -62,7 +62,7 @@ public struct PassLockConfiguration {
keychainConfig: KeychainConfiguration = KeychainConfiguration(),
retryCount: Int = 5,
usingTouchID: Bool = false,
passLockType: PassLockType = .SetPassword) {
passLockType: PassLockType = .setPassword) {
self.passwordConfig = passwordConfig
self.keychainConfig = keychainConfig
self.retryCount = retryCount
Expand Down
8 changes: 4 additions & 4 deletions PassLock/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ extension String {
// MARK: - Shake

extension UIView {
func shake(completion: (() -> Void)?) {
func shake(_ completion: (() -> Void)?) {
CATransaction.begin()
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.1
animation.repeatCount = 2
animation.autoreverses = true
animation.fromValue = NSValue(CGPoint: CGPointMake(self.center.x - 8.0, self.center.y))
animation.toValue = NSValue(CGPoint: CGPointMake(self.center.x + 8.0, self.center.y))
animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 8.0, y: self.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 8.0, y: self.center.y))
CATransaction.setCompletionBlock(completion)
layer.addAnimation(animation, forKey: "position")
layer.add(animation, forKey: "position")
CATransaction.commit()
}
}
10 changes: 3 additions & 7 deletions PassLock/GCDHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@

import Foundation

func delay(delay: NSTimeInterval, queue: dispatch_queue_t = dispatch_get_main_queue(), closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
queue, closure)
func delay(_ delay: TimeInterval, queue: DispatchQueue = DispatchQueue.main, closure:@escaping ()->()) {
queue.asyncAfter(
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}
2 changes: 1 addition & 1 deletion PassLock/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.1.4</string>
<string>0.2.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
34 changes: 17 additions & 17 deletions PassLock/Keychain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ public struct Keychain {

public let config: KeychainConfiguration

private var keychainQuery: [String : AnyObject] {
fileprivate var keychainQuery: [String : AnyObject] {
var query = [String : AnyObject]()
query[kSecClass as String] = kSecClassGenericPassword
query[kSecAttrAccount as String] = config.account
query[kSecAttrService as String] = config.service
query[kSecAttrAccount as String] = config.account as AnyObject?
query[kSecAttrService as String] = config.service as AnyObject?
if let accessGroup = config.accessGroup {
query[kSecAttrAccessGroup as String] = accessGroup
query[kSecAttrAccessGroup as String] = accessGroup as AnyObject?
}

return query
Expand All @@ -57,7 +57,7 @@ extension Keychain {
query[kSecReturnData as String] = kCFBooleanTrue

var result: AnyObject?
let status = withUnsafeMutablePointer(&result) {
let status = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}

Expand All @@ -66,37 +66,37 @@ extension Keychain {
}

guard let queryResult = result as? [String : AnyObject],
let passwordData = queryResult[kSecValueData as String] as? NSData,
let password = String(data: passwordData, encoding: NSUTF8StringEncoding)
let passwordData = queryResult[kSecValueData as String] as? Data,
let password = String(data: passwordData, encoding: String.Encoding.utf8)
else {
return nil
}

return password
}

public func setPassword(password: String) -> Bool {
let encodedPassword = password.dataUsingEncoding(NSUTF8StringEncoding)
var status = SecItemCopyMatching(keychainQuery, nil)
@discardableResult public func setPassword(_ password: String) -> Bool {
let encodedPassword = password.data(using: String.Encoding.utf8)
var status = SecItemCopyMatching(keychainQuery as CFDictionary, nil)
var attributes = [String : AnyObject]()

switch status {
case noErr:
attributes[kSecValueData as String] = encodedPassword
status = SecItemUpdate(keychainQuery, attributes)
attributes[kSecValueData as String] = encodedPassword as AnyObject?
status = SecItemUpdate(keychainQuery as CFDictionary, attributes as CFDictionary)
case errSecItemNotFound:
var query = keychainQuery
query[kSecValueData as String] = encodedPassword
status = SecItemAdd(query, nil)
query[kSecValueData as String] = encodedPassword as AnyObject?
status = SecItemAdd(query as CFDictionary, nil)
default: break
}

return status == noErr
}

public func deletePassword() -> Bool {
let status = SecItemDelete(keychainQuery)
@discardableResult public func deletePassword() -> Bool {
let status = SecItemDelete(keychainQuery as CFDictionary)
return status == noErr
}

}
}
96 changes: 48 additions & 48 deletions PassLock/PassLockViewController+States.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,48 @@
import UIKit

enum PassLockState {
case Input, Confirm, Reconfirm, Done
case input, confirm, reconfirm, done
}

enum PassLockEvent {
case Valid, Invalid
case valid, invalid
}

extension PassLockState {
func nextEvent(x x: Password?, y: Password?) -> PassLockEvent {
func nextEvent(x: Password?, y: Password?) -> PassLockEvent {
switch self {
case .Confirm, .Reconfirm:
return x == y ? .Valid : .Invalid
case .confirm, .reconfirm:
return x == y ? .valid : .invalid
default:
return .Valid
return .valid
}
}
}

extension PassLockViewController {

func setPasswordStateMachine() -> StateMachine<PassLockState, PassLockEvent, Password> {
return StateMachine<PassLockState, PassLockEvent, Password>(initialState: .Input) { [weak self] state, event in
return StateMachine<PassLockState, PassLockEvent, Password>(initialState: .input) { [weak self] state, event in
switch (state, event) {
case (.Input, .Valid): return (.Confirm, { _, _, info in
case (.input, .valid): return (.confirm, { _, _, info in
// input => confirm
self?.titleLabel.text = "验证密码"
self?.passwordInputView.clear()
self?.currentPassword = info
})
case (.Confirm, .Valid): return (.Done, { _, _, _ in
case (.confirm, .valid): return (.done, { _, _, _ in
// confirm => done
guard let strongSelf = self, let password = self?.currentPassword else {
return
}
strongSelf.descriptionLabel.hidden = true
strongSelf.descriptionLabel.isHidden = true
strongSelf.keychain?.setPassword(password)
strongSelf.delegate?.passLockController(strongSelf, didSetPassLock: .Success(password))
strongSelf.delegate?.passLockController(strongSelf, didSetPassLock: .success(password))
})
case (.Confirm, .Invalid): return (.Confirm, { _, _, _ in
case (.confirm, .invalid): return (.confirm, { _, _, _ in
// confirm again
self?.descriptionLabel.text = "密码不匹配, 请再试一次"
self?.descriptionLabel.hidden = false
self?.descriptionLabel.isHidden = false
self?.passwordInputView.shake() {
self?.passwordInputView.clear()
}
Expand All @@ -61,54 +61,54 @@ extension PassLockViewController {
}

func changePasswordStateMachine() -> StateMachine<PassLockState, PassLockEvent, Password> {
return StateMachine<PassLockState, PassLockEvent, Password>(initialState: .Confirm) { [weak self] state, event in
return StateMachine<PassLockState, PassLockEvent, Password>(initialState: .confirm) { [weak self] state, event in
switch (state, event) {
case (.Confirm, .Valid): return (.Input, { _, _, _ in
case (.confirm, .valid): return (.input, { _, _, _ in
// confirm => input
self?.passwordInputView.clear()
self?.titleLabel.text = "请输入新密码"
self?.descriptionLabel.hidden = true
self?.descriptionLabel.isHidden = true
})
case (.Confirm, .Invalid):
case (.confirm, .invalid):
guard let strongSelf = self else {
return nil
}
strongSelf.retryCount += 1
if strongSelf.retryCount >= strongSelf.config.retryCount {
// exceed retry count, failure
return (.Done, { _, _, _ in
strongSelf.descriptionLabel.hidden = true
strongSelf.delegate?.passLockController(strongSelf, didChangePassLock: .Failure)
return (.done, { _, _, _ in
strongSelf.descriptionLabel.isHidden = true
strongSelf.delegate?.passLockController(strongSelf, didChangePassLock: .failure)
})
} else {
// retry
return (.Confirm, { _, _, _ in
return (.confirm, { _, _, _ in
strongSelf.descriptionLabel.text = "密码不匹配, 您还有 \(strongSelf.config.retryCount - strongSelf.retryCount) 次尝试机会"
strongSelf.descriptionLabel.hidden = false
strongSelf.descriptionLabel.isHidden = false
strongSelf.passwordInputView.shake() {
strongSelf.passwordInputView.clear()
}
})
}
case (.Input, .Valid): return (.Reconfirm, { _, _, info in
case (.input, .valid): return (.reconfirm, { _, _, info in
// input => reconfirm
self?.titleLabel.text = "验证密码"
self?.passwordInputView.clear()
self?.currentPassword = info
})
case (.Reconfirm, .Valid): return (.Done, { _, _, info in
case (.reconfirm, .valid): return (.done, { _, _, info in
// reconfirm => done
guard let strongSelf = self, let password = self?.currentPassword else {
return
}
strongSelf.descriptionLabel.hidden = true
strongSelf.descriptionLabel.isHidden = true
strongSelf.keychain?.setPassword(password)
strongSelf.delegate?.passLockController(strongSelf, didChangePassLock: .Success(password))
strongSelf.delegate?.passLockController(strongSelf, didChangePassLock: .success(password))
})
case (.Reconfirm, .Invalid): return (.Reconfirm, { _, _, _ in
case (.reconfirm, .invalid): return (.reconfirm, { _, _, _ in
// reconfirm again
self?.descriptionLabel.text = "密码不匹配, 请再试一次"
self?.descriptionLabel.hidden = false
self?.descriptionLabel.isHidden = false
self?.passwordInputView.shake() {
self?.passwordInputView.clear()
}
Expand All @@ -119,33 +119,33 @@ extension PassLockViewController {
}

func removePasswordStateMachine() -> StateMachine<PassLockState, PassLockEvent, Password> {
return StateMachine<PassLockState, PassLockEvent, Password>(initialState: .Confirm) { [weak self] state, event in
return StateMachine<PassLockState, PassLockEvent, Password>(initialState: .confirm) { [weak self] state, event in
switch (state, event) {
case (.Confirm, .Valid): return (.Done, { _, _, _ in
case (.confirm, .valid): return (.done, { _, _, _ in
// confirm => done
guard let strongSelf = self else {
return
}
strongSelf.descriptionLabel.hidden = true
strongSelf.descriptionLabel.isHidden = true
strongSelf.keychain?.deletePassword()
strongSelf.delegate?.passLockController(strongSelf, didRemovePassLock: .Success(nil))
strongSelf.delegate?.passLockController(strongSelf, didRemovePassLock: .success(nil))
})
case (.Confirm, .Invalid):
case (.confirm, .invalid):
guard let strongSelf = self else {
return nil
}
strongSelf.retryCount += 1
if strongSelf.retryCount >= strongSelf.config.retryCount {
// exceed retry count, failure
return (.Done, { _, _, _ in
strongSelf.descriptionLabel.hidden = true
strongSelf.delegate?.passLockController(strongSelf, didRemovePassLock: .Failure)
return (.done, { _, _, _ in
strongSelf.descriptionLabel.isHidden = true
strongSelf.delegate?.passLockController(strongSelf, didRemovePassLock: .failure)
})
} else {
// retry
return (.Confirm, { _, _, _ in
return (.confirm, { _, _, _ in
strongSelf.descriptionLabel.text = "密码不匹配, 您还有 \(strongSelf.config.retryCount - strongSelf.retryCount) 次尝试机会"
strongSelf.descriptionLabel.hidden = false
strongSelf.descriptionLabel.isHidden = false
strongSelf.passwordInputView.shake() {
strongSelf.passwordInputView.clear()
}
Expand All @@ -157,32 +157,32 @@ extension PassLockViewController {
}

func unlockStateMachine() -> StateMachine<PassLockState, PassLockEvent, Password> {
return StateMachine<PassLockState, PassLockEvent, Password>(initialState: .Confirm) { [weak self] state, event in
return StateMachine<PassLockState, PassLockEvent, Password>(initialState: .confirm) { [weak self] state, event in
switch (state, event) {
case (.Confirm, .Valid): return (.Done, { _, _, _ in
case (.confirm, .valid): return (.done, { _, _, _ in
// confirm => unlock
guard let strongSelf = self else {
return
}
strongSelf.descriptionLabel.hidden = true
strongSelf.delegate?.passLockController(strongSelf, didUnlock: .Success(nil))
strongSelf.descriptionLabel.isHidden = true
strongSelf.delegate?.passLockController(strongSelf, didUnlock: .success(nil))
})
case (.Confirm, .Invalid):
case (.confirm, .invalid):
guard let strongSelf = self else {
return nil
}
strongSelf.retryCount += 1
if strongSelf.retryCount >= strongSelf.config.retryCount {
// exceed retry count, failure
return (.Done, { _, _, _ in
strongSelf.descriptionLabel.hidden = true
strongSelf.delegate?.passLockController(strongSelf, didUnlock: .Failure)
return (.done, { _, _, _ in
strongSelf.descriptionLabel.isHidden = true
strongSelf.delegate?.passLockController(strongSelf, didUnlock: .failure)
})
} else {
// retry
return (.Confirm, { _, _, _ in
return (.confirm, { _, _, _ in
strongSelf.descriptionLabel.text = "密码不匹配, 您还有 \(strongSelf.config.retryCount - strongSelf.retryCount) 次尝试机会"
strongSelf.descriptionLabel.hidden = false
strongSelf.descriptionLabel.isHidden = false
strongSelf.passwordInputView.shake() {
strongSelf.passwordInputView.clear()
}
Expand Down
Loading

0 comments on commit 2b3682f

Please sign in to comment.