Complete functions

This commit is contained in:
ismailgulek
2020-07-21 16:16:27 +03:00
parent a02e446500
commit 1573bedcce
11 changed files with 330 additions and 96 deletions

View File

@@ -38,10 +38,10 @@ final class EnterPinCodeCoordinator: EnterPinCodeCoordinatorType {
// MARK: - Setup
init(session: MXSession?) {
init(session: MXSession?, viewMode: SetPinCoordinatorViewMode) {
self.session = session
let enterPinCodeViewModel = EnterPinCodeViewModel(session: self.session)
let enterPinCodeViewModel = EnterPinCodeViewModel(session: self.session, viewMode: viewMode)
let enterPinCodeViewController = EnterPinCodeViewController.instantiate(with: enterPinCodeViewModel)
self.enterPinCodeViewModel = enterPinCodeViewModel
self.enterPinCodeViewController = enterPinCodeViewController
@@ -61,6 +61,14 @@ final class EnterPinCodeCoordinator: EnterPinCodeCoordinatorType {
// MARK: - EnterPinCodeViewModelCoordinatorDelegate
extension EnterPinCodeCoordinator: EnterPinCodeViewModelCoordinatorDelegate {
func enterPinCodeViewModelDidComplete(_ viewModel: EnterPinCodeViewModelType) {
self.delegate?.enterPinCodeCoordinatorDidComplete(self)
}
func enterPinCodeViewModelDidCompleteWithReset(_ viewModel: EnterPinCodeViewModelType) {
self.delegate?.enterPinCodeCoordinatorDidCompleteWithReset(self)
}
func enterPinCodeViewModel(_ viewModel: EnterPinCodeViewModelType, didCompleteWithPin pin: String) {
self.delegate?.enterPinCodeCoordinator(self, didCompleteWithPin: pin)
}

View File

@@ -19,6 +19,8 @@
import Foundation
protocol EnterPinCodeCoordinatorDelegate: class {
func enterPinCodeCoordinatorDidComplete(_ coordinator: EnterPinCodeCoordinatorType)
func enterPinCodeCoordinatorDidCompleteWithReset(_ coordinator: EnterPinCodeCoordinatorType)
func enterPinCodeCoordinator(_ coordinator: EnterPinCodeCoordinatorType, didCompleteWithPin pin: String)
func enterPinCodeCoordinatorDidCancel(_ coordinator: EnterPinCodeCoordinatorType)
}

View File

@@ -20,6 +20,10 @@ import Foundation
/// EnterPinCodeViewController view actions exposed to view model
enum EnterPinCodeViewAction {
case loadData
case digitPressed(_ tag: Int)
case forgotPinPressed
case cancel
case pinsDontMatchAlertAction
case forgotPinAlertAction
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<device id="retina3_5" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
@@ -13,56 +13,74 @@
<objects>
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="EnterPinCodeViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Choose a PIN for security" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
<rect key="frame" x="80.5" y="83" width="253" height="26.5"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="22"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" alignment="center" spacing="24" translatesAutoresizingMaskIntoConstraints="NO" id="xi9-P9-8WP">
<rect key="frame" x="123" y="205.5" width="168" height="24"/>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="DMT-DS-IA8">
<rect key="frame" x="0.0" y="8" width="320" height="464"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="selection_untick" translatesAutoresizingMaskIntoConstraints="NO" id="Gwx-8X-ZWk">
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ztg-5t-ECh" userLabel="Container">
<rect key="frame" x="20" y="0.0" width="280" height="86"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="callkit_icon" translatesAutoresizingMaskIntoConstraints="NO" id="UHg-qE-anw">
<rect key="frame" x="120" y="8" width="40" height="40"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="ERR-Np-6I1"/>
<constraint firstAttribute="height" constant="40" id="zYD-Rk-nOH"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Choose a PIN for security" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
<rect key="frame" x="13.5" y="57" width="253" height="29"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="22"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="aek-t8-dK4"/>
<constraint firstAttribute="width" constant="24" id="cJN-ZQ-6aQ"/>
<constraint firstAttribute="height" constant="86" id="30N-bq-9g8"/>
<constraint firstAttribute="bottom" secondItem="bxI-mu-qng" secondAttribute="bottom" id="8Qc-aK-wkM"/>
<constraint firstItem="UHg-qE-anw" firstAttribute="centerX" secondItem="ztg-5t-ECh" secondAttribute="centerX" id="Vhm-GS-xYs"/>
<constraint firstItem="bxI-mu-qng" firstAttribute="top" secondItem="UHg-qE-anw" secondAttribute="bottom" constant="9" id="laj-kY-eW1"/>
<constraint firstItem="bxI-mu-qng" firstAttribute="centerX" secondItem="ztg-5t-ECh" secondAttribute="centerX" id="p4o-hz-jZJ"/>
<constraint firstItem="UHg-qE-anw" firstAttribute="top" secondItem="ztg-5t-ECh" secondAttribute="top" constant="8" id="uj5-KC-7FD"/>
</constraints>
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="selection_untick" translatesAutoresizingMaskIntoConstraints="NO" id="qDY-R1-l5l">
<rect key="frame" x="48" y="0.0" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="QDg-LP-R4J"/>
<constraint firstAttribute="height" constant="24" id="f4G-d8-hoA"/>
</constraints>
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" tag="2" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="selection_untick" translatesAutoresizingMaskIntoConstraints="NO" id="X0l-q3-fXm">
<rect key="frame" x="96" y="0.0" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="HQA-9R-8bC"/>
<constraint firstAttribute="height" constant="24" id="Zjd-RW-DiW"/>
</constraints>
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" tag="3" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="selection_untick" translatesAutoresizingMaskIntoConstraints="NO" id="o1F-px-ZT5">
<rect key="frame" x="144" y="0.0" width="24" height="24"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="FA6-QR-ld2"/>
<constraint firstAttribute="width" constant="24" id="TcR-MF-wE3"/>
</constraints>
</imageView>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="24" id="3on-xo-2n5"/>
</constraints>
</stackView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tUL-bI-Cpy">
<rect key="frame" x="0.0" y="229.5" width="414" height="632.5"/>
<subviews>
</view>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" alignment="center" spacing="24" translatesAutoresizingMaskIntoConstraints="NO" id="xi9-P9-8WP">
<rect key="frame" x="76" y="87" width="168" height="24"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="selection_untick" translatesAutoresizingMaskIntoConstraints="NO" id="Gwx-8X-ZWk">
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="aek-t8-dK4"/>
<constraint firstAttribute="width" constant="24" id="cJN-ZQ-6aQ"/>
</constraints>
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="selection_untick" translatesAutoresizingMaskIntoConstraints="NO" id="qDY-R1-l5l">
<rect key="frame" x="48" y="0.0" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="QDg-LP-R4J"/>
<constraint firstAttribute="height" constant="24" id="f4G-d8-hoA"/>
</constraints>
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" tag="2" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="selection_untick" translatesAutoresizingMaskIntoConstraints="NO" id="X0l-q3-fXm">
<rect key="frame" x="96" y="0.0" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="HQA-9R-8bC"/>
<constraint firstAttribute="height" constant="24" id="Zjd-RW-DiW"/>
</constraints>
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" tag="3" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="selection_untick" translatesAutoresizingMaskIntoConstraints="NO" id="o1F-px-ZT5">
<rect key="frame" x="144" y="0.0" width="24" height="24"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="FA6-QR-ld2"/>
<constraint firstAttribute="width" constant="24" id="TcR-MF-wE3"/>
</constraints>
</imageView>
</subviews>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" alignment="center" spacing="21" translatesAutoresizingMaskIntoConstraints="NO" id="W0M-eq-abZ">
<rect key="frame" x="85" y="165" width="244" height="303"/>
<rect key="frame" x="38" y="112" width="244" height="303"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" alignment="center" spacing="32" translatesAutoresizingMaskIntoConstraints="NO" id="Uqh-o2-7HP">
<rect key="frame" x="0.0" y="0.0" width="244" height="60"/>
@@ -225,40 +243,59 @@
</stackView>
</subviews>
</stackView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nCW-ER-9SF" userLabel="Container">
<rect key="frame" x="40" y="416" width="240" height="48"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CRt-Fb-0Dq">
<rect key="frame" x="0.0" y="0.0" width="240" height="48"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<state key="normal" title="Forgot PIN"/>
<connections>
<action selector="forgotPinButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="hE4-CJ-Bdh"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="CRt-Fb-0Dq" firstAttribute="top" secondItem="nCW-ER-9SF" secondAttribute="top" id="QVD-G0-uHm"/>
<constraint firstAttribute="bottom" secondItem="CRt-Fb-0Dq" secondAttribute="bottom" id="gJw-QQ-7PO"/>
<constraint firstAttribute="trailing" secondItem="CRt-Fb-0Dq" secondAttribute="trailing" id="gnV-kg-BCW"/>
<constraint firstAttribute="height" constant="48" id="whx-HV-FXN"/>
<constraint firstItem="CRt-Fb-0Dq" firstAttribute="leading" secondItem="nCW-ER-9SF" secondAttribute="leading" id="wxI-iX-ugm"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="W0M-eq-abZ" firstAttribute="centerY" secondItem="tUL-bI-Cpy" secondAttribute="centerY" id="aqm-0i-Sq4"/>
<constraint firstItem="W0M-eq-abZ" firstAttribute="centerX" secondItem="tUL-bI-Cpy" secondAttribute="centerX" id="tdZ-I0-AMy"/>
<constraint firstAttribute="trailing" secondItem="ztg-5t-ECh" secondAttribute="trailing" constant="20" id="ADS-wF-wRi"/>
<constraint firstItem="ztg-5t-ECh" firstAttribute="leading" secondItem="DMT-DS-IA8" secondAttribute="leading" constant="20" id="YU9-Am-aht"/>
</constraints>
</view>
</stackView>
</subviews>
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="bxI-mu-qng" firstAttribute="centerX" secondItem="EL9-GA-lwo" secondAttribute="centerX" id="2Bf-EF-ikh"/>
<constraint firstItem="xi9-P9-8WP" firstAttribute="centerX" secondItem="EL9-GA-lwo" secondAttribute="centerX" id="4iN-py-NUV"/>
<constraint firstItem="xi9-P9-8WP" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="96" id="7mW-Ct-gas"/>
<constraint firstItem="tUL-bI-Cpy" firstAttribute="top" secondItem="xi9-P9-8WP" secondAttribute="bottom" id="Bh0-nk-4NF"/>
<constraint firstItem="bxI-mu-qng" firstAttribute="top" secondItem="bFg-jh-JZB" secondAttribute="top" constant="39" id="U9h-07-CLy"/>
<constraint firstItem="tUL-bI-Cpy" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="USU-5t-l6v"/>
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="tUL-bI-Cpy" secondAttribute="trailing" id="Xb3-zu-Zbb"/>
<constraint firstItem="bFg-jh-JZB" firstAttribute="bottom" secondItem="tUL-bI-Cpy" secondAttribute="bottom" id="fZf-Se-umS"/>
<constraint firstItem="DMT-DS-IA8" firstAttribute="top" secondItem="bFg-jh-JZB" secondAttribute="top" constant="8" id="Mlg-kp-E06"/>
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="DMT-DS-IA8" secondAttribute="trailing" id="Vwu-g7-vNh"/>
<constraint firstItem="bFg-jh-JZB" firstAttribute="bottom" secondItem="DMT-DS-IA8" secondAttribute="bottom" constant="8" id="gPa-B5-LIs"/>
<constraint firstItem="DMT-DS-IA8" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="kA7-cw-VK1"/>
</constraints>
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
</view>
<connections>
<outlet property="digitsStackView" destination="W0M-eq-abZ" id="xnb-6w-dtC"/>
<outlet property="forgotPinButton" destination="CRt-Fb-0Dq" id="kHp-wn-P0o"/>
<outlet property="informationLabel" destination="bxI-mu-qng" id="pbX-aZ-inC"/>
<outlet property="logoImageView" destination="UHg-qE-anw" id="8C0-pd-i3b"/>
<outlet property="placeholderStackView" destination="xi9-P9-8WP" id="ynl-7M-Rpb"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-3198" y="-647"/>
<point key="canvasLocation" x="-3199.1999999999998" y="-648.02955665024638"/>
</scene>
</scenes>
<resources>
<image name="back_icon" width="14" height="23"/>
<image name="callkit_icon" width="40" height="40"/>
<image name="selection_untick" width="22" height="22"/>
</resources>
</document>

View File

@@ -30,9 +30,11 @@ final class EnterPinCodeViewController: UIViewController {
// MARK: Outlets
@IBOutlet private weak var logoImageView: UIImageView!
@IBOutlet private weak var placeholderStackView: UIStackView!
@IBOutlet private weak var digitsStackView: UIStackView!
@IBOutlet private weak var informationLabel: UILabel!
@IBOutlet private weak var forgotPinButton: UIButton!
// MARK: Private
@@ -67,8 +69,13 @@ final class EnterPinCodeViewController: UIViewController {
self.update(theme: self.theme)
self.viewModel.viewDelegate = self
if #available(iOS 13.0, *) {
modalPresentationStyle = .fullScreen
isModalInPresentation = true
}
// self.viewModel.process(viewAction: .loadData)
self.viewModel.process(viewAction: .loadData)
}
override func viewWillAppear(_ animated: Bool) {
@@ -134,29 +141,87 @@ final class EnterPinCodeViewController: UIViewController {
private func render(viewState: EnterPinCodeViewState) {
switch viewState {
case .enterFirstPin:
self.renderEnterFirstPin()
case .choosePin:
self.renderChoosePin()
case .confirmPin:
self.renderConfirmPin()
case .pinsDontMatch(let error):
self.render(error: error)
case .pinsDontMatch:
self.renderPinsDontMatch()
case .unlockByPin:
self.renderUnlockByPin()
case .wrongPin:
self.renderWrongPin()
case .wrongPinTooManyTimes:
self.renderWrongPinTooManyTimes()
case .forgotPin:
self.renderForgotPin()
case .confirmPinToDisable:
self.renderConfirmPinToDisable()
}
}
private func renderEnterFirstPin() {
self.informationLabel.text = "Choose a PIN for security"
private func renderChoosePin() {
self.logoImageView.isHidden = true
self.informationLabel.text = VectorL10n.pinProtectionChoosePin
self.forgotPinButton.isHidden = true
}
private func renderConfirmPin() {
self.informationLabel.text = "Confirm your PIN"
self.informationLabel.text = VectorL10n.pinProtectionConfirmPin
// reset placeholders
renderPlaceholdersCount(0)
}
private func render(error: Error) {
private func renderPinsDontMatch() {
let error = NSError(domain: "", code: 0, userInfo: [
NSLocalizedFailureReasonErrorKey: VectorL10n.pinProtectionMismatchErrorTitle,
NSLocalizedDescriptionKey: VectorL10n.pinProtectionMismatchErrorMessage
])
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
self.errorPresenter.presentError(from: self, forError: error, animated: true) {
self.viewModel.process(viewAction: .pinsDontMatchAlertAction)
}
}
private func renderUnlockByPin() {
self.logoImageView.isHidden = false
self.informationLabel.text = VectorL10n.pinProtectionEnterPin
self.forgotPinButton.isHidden = false
}
private func renderWrongPin() {
self.placeholderStackView.vc_shake()
}
private func renderWrongPinTooManyTimes() {
let error = NSError(domain: "", code: 0, userInfo: [
NSLocalizedFailureReasonErrorKey: VectorL10n.pinProtectionMismatchErrorTitle,
NSLocalizedDescriptionKey: VectorL10n.pinProtectionMismatchErrorMessage
])
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.errorPresenter.presentError(from: self, forError: error, animated: true) {
}
}
private func renderForgotPin() {
let controller = UIAlertController(title: VectorL10n.pinProtectionResetAlertTitle,
message: VectorL10n.pinProtectionResetAlertMessage,
preferredStyle: .alert)
let resetAction = UIAlertAction(title: VectorL10n.pinProtectionResetAlertActionReset, style: .default) { (_) in
self.viewModel.process(viewAction: .forgotPinAlertAction)
}
controller.addAction(resetAction)
self.present(controller, animated: true, completion: nil)
}
private func renderConfirmPinToDisable() {
self.logoImageView.isHidden = true
self.informationLabel.text = VectorL10n.pinProtectionConfirmPinToDisable
self.forgotPinButton.isHidden = true
}
private func renderPlaceholdersCount(_ count: Int) {
@@ -172,12 +237,15 @@ final class EnterPinCodeViewController: UIViewController {
}
}
// MARK: - Actions
@IBAction private func digitButtonAction(_ sender: UIButton) {
self.viewModel.process(viewAction: .digitPressed(sender.tag))
}
@IBAction private func forgotPinButtonAction(_ sender: UIButton) {
self.viewModel.process(viewAction: .forgotPinPressed)
}
private func cancelButtonAction() {
self.viewModel.process(viewAction: .cancel)

View File

@@ -25,6 +25,7 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
// MARK: Private
private let session: MXSession?
private var viewMode: SetPinCoordinatorViewMode
private var currentOperation: MXHTTPOperation?
private var firstPin: String = ""
@@ -33,6 +34,7 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
self.viewDelegate?.enterPinCodeViewModel(self, didUpdatePlaceholdersCount: currentPin.count)
}
}
private var numberOfFailuresDuringEnterPIN: Int = 0
// MARK: Public
@@ -41,8 +43,9 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
// MARK: - Setup
init(session: MXSession?) {
init(session: MXSession?, viewMode: SetPinCoordinatorViewMode) {
self.session = session
self.viewMode = viewMode
}
deinit {
@@ -53,11 +56,23 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
func process(viewAction: EnterPinCodeViewAction) {
switch viewAction {
case .loadData:
self.loadData()
case .digitPressed(let tag):
self.digitPressed(tag)
case .forgotPinPressed:
self.viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .forgotPin)
case .cancel:
self.cancelOperations()
self.coordinatorDelegate?.enterPinCodeViewModelDidCancel(self)
case .pinsDontMatchAlertAction:
// reset pins
firstPin.removeAll()
currentPin.removeAll()
// go back to first state
self.update(viewState: .choosePin)
case .forgotPinAlertAction:
self.coordinatorDelegate?.enterPinCodeViewModelDidCompleteWithReset(self)
}
}
@@ -76,30 +91,60 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
currentPin += "\(tag)"
if currentPin.count == 4 {
if firstPin.isEmpty {
// go to next screen
firstPin = currentPin
currentPin = ""
self.update(viewState: .confirmPin)
} else {
// check first and second pins
if firstPin == currentPin {
self.coordinatorDelegate?.enterPinCodeViewModel(self, didCompleteWithPin: firstPin)
switch viewMode {
case .setPin:
// choosing pin
if firstPin.isEmpty {
// go to next screen
firstPin = currentPin
currentPin.removeAll()
self.update(viewState: .confirmPin)
} else {
self.update(viewState: .pinsDontMatch(NSError(domain: "", code: -1002, userInfo: nil)))
firstPin = ""
currentPin = ""
self.update(viewState: .enterFirstPin)
// check first and second pins
if firstPin == currentPin {
// complete with a little delay
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.coordinatorDelegate?.enterPinCodeViewModel(self, didCompleteWithPin: firstPin)
}
} else {
self.update(viewState: .pinsDontMatch)
}
}
case .unlockByPin, .confirmPinToDeactivate:
// unlocking
if currentPin != PinCodePreferences.shared.pin {
// no match
numberOfFailuresDuringEnterPIN += 1
if numberOfFailuresDuringEnterPIN < PinCodePreferences.shared.allowedNumberOfTrialsBeforeAlert {
self.viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .wrongPin)
} else {
self.viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .wrongPinTooManyTimes)
numberOfFailuresDuringEnterPIN = 0
}
currentPin.removeAll()
} else {
// match
// complete with a little delay
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.coordinatorDelegate?.enterPinCodeViewModelDidComplete(self)
}
}
}
return
}
}
}
private func loadData() {
self.update(viewState: .enterFirstPin)
switch viewMode {
case .setPin:
update(viewState: .choosePin)
case .unlockByPin:
update(viewState: .unlockByPin)
case .confirmPinToDeactivate:
update(viewState: .confirmPinToDisable)
}
}
private func update(viewState: EnterPinCodeViewState) {

View File

@@ -24,6 +24,8 @@ protocol EnterPinCodeViewModelViewDelegate: class {
}
protocol EnterPinCodeViewModelCoordinatorDelegate: class {
func enterPinCodeViewModelDidComplete(_ viewModel: EnterPinCodeViewModelType)
func enterPinCodeViewModelDidCompleteWithReset(_ viewModel: EnterPinCodeViewModelType)
func enterPinCodeViewModel(_ viewModel: EnterPinCodeViewModelType, didCompleteWithPin pin: String)
func enterPinCodeViewModelDidCancel(_ viewModel: EnterPinCodeViewModelType)
}

View File

@@ -20,7 +20,12 @@ import Foundation
/// EnterPinCodeViewController view state
enum EnterPinCodeViewState {
case enterFirstPin
case confirmPin
case pinsDontMatch(Error)
case choosePin // creating pin for the first time, enter for first
case confirmPin // creating pin for the first time, confirm
case pinsDontMatch // pins don't match
case unlockByPin // after pin has been set, enter pin to unlock
case wrongPin // after pin has been set, pin entered wrongly
case wrongPinTooManyTimes // after pin has been set, pin entered wrongly too many times
case forgotPin // after pin has been set, user tapped forgot pin
case confirmPinToDisable // after pin has been set, confirm pin to disable pin
}