2014-10-29 21:58:58 +01:00
//
2017-02-09 18:02:17 +01:00
// Copyright ( c ) 2017 Open Whisper Systems . All rights reserved .
2014-10-29 21:58:58 +01:00
//
# import "CodeVerificationViewController.h"
2016-10-10 22:02:09 +02:00
# import "AppDelegate.h"
# import "Signal-Swift.h"
# import "SignalsNavigationController.h"
# import "SignalsViewController.h"
2017-03-23 14:55:39 +01:00
# import "StringUtil.h"
2016-10-10 22:02:09 +02:00
# import < PromiseKit / AnyPromise . h >
# import < SignalServiceKit / OWSError . h >
# import < SignalServiceKit / TSAccountManager . h >
2017-02-11 21:19:43 +01:00
# import < SignalServiceKit / TSNetworkManager . h >
2016-10-10 22:02:09 +02:00
# import < SignalServiceKit / TSStorageManager + keyingMaterial . h >
NS_ASSUME _NONNULL _BEGIN
2017-02-09 18:02:17 +01:00
@ interface CodeVerificationViewController ( ) < UITextFieldDelegate >
@ property ( nonatomic , readonly ) AccountManager * accountManager ;
// Where the user enters the verification code they wish to document
@ property ( nonatomic ) UITextField * challengeTextField ;
@ property ( nonatomic ) UILabel * phoneNumberLabel ;
// // User action buttons
@ property ( nonatomic ) UIButton * challengeButton ;
@ property ( nonatomic ) UIButton * sendCodeViaSMSAgainButton ;
@ property ( nonatomic ) UIButton * sendCodeViaVoiceButton ;
2014-10-29 21:58:58 +01:00
2017-02-09 18:02:17 +01:00
@ property ( nonatomic ) UIActivityIndicatorView * submitCodeSpinner ;
@ property ( nonatomic ) UIActivityIndicatorView * requestCodeAgainSpinner ;
@ property ( nonatomic ) UIActivityIndicatorView * requestCallSpinner ;
2016-10-10 22:02:09 +02:00
2014-10-29 21:58:58 +01:00
@ end
2017-02-09 18:02:17 +01:00
# pragma mark -
2014-10-29 21:58:58 +01:00
@ implementation CodeVerificationViewController
2016-10-10 22:02:09 +02:00
- ( nullable instancetype ) initWithCoder : ( NSCoder * ) aDecoder
{
self = [ super initWithCoder : aDecoder ] ;
if ( ! self ) {
return self ;
}
2016-11-12 18:22:29 +01:00
_accountManager = [ Environment getCurrent ] . accountManager ;
2016-10-10 22:02:09 +02:00
return self ;
}
- ( instancetype ) init
{
self = [ super init ] ;
if ( ! self ) {
return self ;
}
2016-11-12 18:22:29 +01:00
_accountManager = [ Environment getCurrent ] . accountManager ;
2016-10-10 22:02:09 +02:00
return self ;
}
2017-02-13 18:11:41 +01:00
# pragma mark - View Lifecycle
2015-12-22 12:45:09 +01:00
- ( void ) viewDidLoad {
2014-10-29 21:58:58 +01:00
[ super viewDidLoad ] ;
2017-02-09 18:02:17 +01:00
[ self createViews ] ;
2014-10-29 21:58:58 +01:00
[ self initializeKeyboardHandlers ] ;
2017-02-09 18:02:17 +01:00
}
2017-02-13 18:11:41 +01:00
- ( void ) viewWillAppear : ( BOOL ) animated {
[ super viewWillAppear : animated ] ;
[ self enableServerActions : YES ] ;
[ self updatePhoneNumberLabel ] ;
}
- ( void ) viewDidAppear : ( BOOL ) animated {
[ super viewDidAppear : animated ] ;
[ _challengeTextField becomeFirstResponder ] ;
}
# pragma mark -
2017-02-09 18:02:17 +01:00
- ( void ) createViews {
self . view . backgroundColor = [ UIColor whiteColor ] ;
self . view . opaque = YES ;
2017-02-13 18:11:41 +01:00
UIColor * signalBlueColor = [ UIColor ows_signalBrandBlueColor ] ;
2017-02-09 18:02:17 +01:00
UIView * header = [ UIView new ] ;
header . backgroundColor = signalBlueColor ;
[ self . view addSubview : header ] ;
[ header autoPinWidthToSuperview ] ;
[ header autoPinEdgeToSuperviewEdge : ALEdgeTop ] ;
2017-02-13 18:11:41 +01:00
// The header will grow to accomodate the titleLabel ' s height .
2017-02-09 18:02:17 +01:00
UILabel * titleLabel = [ UILabel new ] ;
titleLabel . textColor = [ UIColor whiteColor ] ;
2017-04-11 19:32:59 +02:00
titleLabel . text = [ self phoneNumberText ] ;
2017-02-09 18:02:17 +01:00
titleLabel . font = [ UIFont ows_mediumFontWithSize : 20. f ] ;
[ header addSubview : titleLabel ] ;
[ titleLabel autoPinToTopLayoutGuideOfViewController : self withInset : 0 ] ;
[ titleLabel autoPinEdgeToSuperviewEdge : ALEdgeBottom ] ;
2017-02-13 18:11:41 +01:00
[ titleLabel autoSetDimension : ALDimensionHeight toSize : 40 ] ;
2017-02-09 18:02:17 +01:00
[ titleLabel autoHCenterInSuperview ] ;
UIButton * backButton = [ UIButton buttonWithType : UIButtonTypeCustom ] ;
[ backButton setTitle : NSLocalizedString ( @ "VERIFICATION_BACK_BUTTON" , @ "button text for back button on verification view" )
forState : UIControlStateNormal ] ;
[ backButton setTitleColor : [ UIColor whiteColor ]
forState : UIControlStateNormal ] ;
backButton . titleLabel . font = [ UIFont ows_mediumFontWithSize : 14. f ] ;
[ header addSubview : backButton ] ;
2017-02-13 18:11:41 +01:00
[ backButton autoPinEdgeToSuperviewEdge : ALEdgeLeft withInset : 10 ] ;
2017-02-09 18:02:17 +01:00
[ backButton autoAlignAxis : ALAxisHorizontal toSameAxisOfView : titleLabel ] ;
[ backButton addTarget : self action : @ selector ( backButtonPressed : ) forControlEvents : UIControlEventTouchUpInside ] ;
_phoneNumberLabel = [ UILabel new ] ;
_phoneNumberLabel . textColor = [ UIColor ows_darkGrayColor ] ;
_phoneNumberLabel . font = [ UIFont ows_regularFontWithSize : 20. f ] ;
2017-04-11 19:25:34 +02:00
_phoneNumberLabel . numberOfLines = 2 ;
_phoneNumberLabel . adjustsFontSizeToFitWidth = YES ;
2017-02-09 18:02:17 +01:00
[ self . view addSubview : _phoneNumberLabel ] ;
2017-04-11 19:25:34 +02:00
[ _phoneNumberLabel autoPinWidthToSuperviewWithMargin : ScaleFromIPhone5 ( 32 ) ] ;
[ _phoneNumberLabel autoPinEdge : ALEdgeTop
toEdge : ALEdgeBottom
ofView : header
withOffset : ScaleFromIPhone5To7Plus ( 30 , 100 ) ] ;
2017-02-13 18:11:41 +01:00
const CGFloat kHMargin = 36 ;
2017-02-09 18:02:17 +01:00
_challengeTextField = [ UITextField new ] ;
_challengeTextField . textColor = [ UIColor blackColor ] ;
2016-11-08 19:08:04 +01:00
_challengeTextField . placeholder = NSLocalizedString ( @ "VERIFICATION_CHALLENGE_DEFAULT_TEXT" ,
2017-02-09 18:02:17 +01:00
@ "Text field placeholder for SMS verification code during registration" ) ;
_challengeTextField . font = [ UIFont ows_lightFontWithSize : 21. f ] ;
_challengeTextField . textAlignment = NSTextAlignmentCenter ;
2017-04-09 20:17:20 +02:00
_challengeTextField . keyboardType = UIKeyboardTypeNumberPad ;
2015-12-22 12:45:09 +01:00
_challengeTextField . delegate = self ;
2017-02-09 18:02:17 +01:00
[ self . view addSubview : _challengeTextField ] ;
2017-02-13 18:11:41 +01:00
[ _challengeTextField autoPinWidthToSuperviewWithMargin : kHMargin ] ;
2017-02-09 18:02:17 +01:00
[ _challengeTextField autoPinEdge : ALEdgeTop toEdge : ALEdgeBottom ofView : _phoneNumberLabel
2017-02-13 18:11:41 +01:00
withOffset : 25 ] ;
2017-02-09 18:02:17 +01:00
UIView * underscoreView = [ UIView new ] ;
underscoreView . backgroundColor = [ UIColor colorWithWhite : 0.5 alpha : 1. f ] ;
[ self . view addSubview : underscoreView ] ;
[ underscoreView autoPinEdge : ALEdgeTop toEdge : ALEdgeBottom ofView : _challengeTextField
2017-02-13 18:11:41 +01:00
withOffset : 3 ] ;
[ underscoreView autoPinWidthToSuperviewWithMargin : kHMargin ] ;
2017-02-09 18:02:17 +01:00
[ underscoreView autoSetDimension : ALDimensionHeight toSize : 1. f ] ;
_challengeButton = [ UIButton buttonWithType : UIButtonTypeCustom ] ;
_challengeButton . backgroundColor = signalBlueColor ;
2016-11-01 20:02:15 +01:00
[ _challengeButton setTitle : NSLocalizedString ( @ "VERIFICATION_CHALLENGE_SUBMIT_CODE" , @ "button text during registration to submit your SMS verification code" )
2017-02-09 18:02:17 +01:00
forState : UIControlStateNormal ] ;
[ _challengeButton setTitleColor : [ UIColor whiteColor ]
forState : UIControlStateNormal ] ;
_challengeButton . titleLabel . font = [ UIFont ows_mediumFontWithSize : 14. f ] ;
[ _challengeButton addTarget : self
action : @ selector ( verifyChallengeAction : )
forControlEvents : UIControlEventTouchUpInside ] ;
[ self . view addSubview : _challengeButton ] ;
[ _challengeButton autoPinEdge : ALEdgeTop toEdge : ALEdgeBottom ofView : underscoreView
2017-02-13 18:11:41 +01:00
withOffset : 15 ] ;
[ _challengeButton autoPinWidthToSuperviewWithMargin : kHMargin ] ;
2017-02-09 18:02:17 +01:00
[ _challengeButton autoSetDimension : ALDimensionHeight toSize : 47. f ] ;
2017-02-13 18:11:41 +01:00
const CGFloat kSpinnerSize = 20 ;
const CGFloat kSpinnerSpacing = 15 ;
2017-02-09 18:02:17 +01:00
_submitCodeSpinner = [ [ UIActivityIndicatorView alloc ] initWithActivityIndicatorStyle : UIActivityIndicatorViewStyleWhite ] ;
[ _challengeButton addSubview : _submitCodeSpinner ] ;
2017-02-13 18:11:41 +01:00
[ _submitCodeSpinner autoSetDimension : ALDimensionWidth toSize : kSpinnerSize ] ;
[ _submitCodeSpinner autoSetDimension : ALDimensionHeight toSize : kSpinnerSize ] ;
2017-02-09 18:02:17 +01:00
[ _submitCodeSpinner autoVCenterInSuperview ] ;
2017-02-13 18:11:41 +01:00
[ _submitCodeSpinner autoPinEdgeToSuperviewEdge : ALEdgeRight withInset : kSpinnerSpacing ] ;
2017-02-09 18:02:17 +01:00
_sendCodeViaSMSAgainButton = [ UIButton buttonWithType : UIButtonTypeCustom ] ;
_sendCodeViaSMSAgainButton . backgroundColor = [ UIColor whiteColor ] ;
2016-11-01 20:02:15 +01:00
[ _sendCodeViaSMSAgainButton setTitle : NSLocalizedString ( @ "VERIFICATION_CHALLENGE_SUBMIT_AGAIN" , @ "button text during registration to request another SMS code be sent" )
2017-02-09 18:02:17 +01:00
forState : UIControlStateNormal ] ;
[ _sendCodeViaSMSAgainButton setTitleColor : signalBlueColor
forState : UIControlStateNormal ] ;
_sendCodeViaSMSAgainButton . titleLabel . font = [ UIFont ows_mediumFontWithSize : 14. f ] ;
[ _sendCodeViaSMSAgainButton addTarget : self
action : @ selector ( sendCodeViaSMSAction : )
forControlEvents : UIControlEventTouchUpInside ] ;
[ self . view addSubview : _sendCodeViaSMSAgainButton ] ;
[ _sendCodeViaSMSAgainButton autoPinEdge : ALEdgeTop toEdge : ALEdgeBottom ofView : _challengeButton
2017-02-13 18:11:41 +01:00
withOffset : 10 ] ;
[ _sendCodeViaSMSAgainButton autoPinWidthToSuperviewWithMargin : kHMargin ] ;
2017-02-09 18:02:17 +01:00
[ _sendCodeViaSMSAgainButton autoSetDimension : ALDimensionHeight toSize : 35 ] ;
_requestCodeAgainSpinner = [ [ UIActivityIndicatorView alloc ] initWithActivityIndicatorStyle : UIActivityIndicatorViewStyleGray ] ;
[ _sendCodeViaSMSAgainButton addSubview : _requestCodeAgainSpinner ] ;
2017-02-13 18:11:41 +01:00
[ _requestCodeAgainSpinner autoSetDimension : ALDimensionWidth toSize : kSpinnerSize ] ;
[ _requestCodeAgainSpinner autoSetDimension : ALDimensionHeight toSize : kSpinnerSize ] ;
2017-02-09 18:02:17 +01:00
[ _requestCodeAgainSpinner autoVCenterInSuperview ] ;
2017-02-13 18:11:41 +01:00
[ _requestCodeAgainSpinner autoPinEdgeToSuperviewEdge : ALEdgeRight withInset : kSpinnerSpacing ] ;
2017-02-09 18:02:17 +01:00
_sendCodeViaVoiceButton = [ UIButton buttonWithType : UIButtonTypeCustom ] ;
_sendCodeViaVoiceButton . backgroundColor = [ UIColor whiteColor ] ;
[ _sendCodeViaVoiceButton setTitle : NSLocalizedString ( @ "VERIFICATION_CHALLENGE_SEND_VIA_VOICE" ,
@ "button text during registration to request phone number verification be done via phone call" )
forState : UIControlStateNormal ] ;
[ _sendCodeViaVoiceButton setTitleColor : signalBlueColor
forState : UIControlStateNormal ] ;
_sendCodeViaVoiceButton . titleLabel . font = [ UIFont ows_mediumFontWithSize : 14. f ] ;
[ _sendCodeViaVoiceButton addTarget : self
action : @ selector ( sendCodeViaVoiceAction : )
forControlEvents : UIControlEventTouchUpInside ] ;
[ self . view addSubview : _sendCodeViaVoiceButton ] ;
2017-02-13 18:11:41 +01:00
[ _sendCodeViaVoiceButton autoPinEdge : ALEdgeTop toEdge : ALEdgeBottom ofView : _sendCodeViaSMSAgainButton ] ;
[ _sendCodeViaVoiceButton autoPinWidthToSuperviewWithMargin : kHMargin ] ;
2017-02-09 18:02:17 +01:00
[ _sendCodeViaVoiceButton autoSetDimension : ALDimensionHeight toSize : 35 ] ;
_requestCallSpinner = [ [ UIActivityIndicatorView alloc ] initWithActivityIndicatorStyle : UIActivityIndicatorViewStyleGray ] ;
[ _sendCodeViaVoiceButton addSubview : _requestCallSpinner ] ;
2017-02-13 18:11:41 +01:00
[ _requestCallSpinner autoSetDimension : ALDimensionWidth toSize : kSpinnerSize ] ;
[ _requestCallSpinner autoSetDimension : ALDimensionHeight toSize : kSpinnerSize ] ;
2017-02-09 18:02:17 +01:00
[ _requestCallSpinner autoVCenterInSuperview ] ;
2017-02-13 18:11:41 +01:00
[ _requestCallSpinner autoPinEdgeToSuperviewEdge : ALEdgeRight withInset : kSpinnerSpacing ] ;
2017-02-09 18:02:17 +01:00
}
2017-04-11 19:32:59 +02:00
- ( NSString * ) phoneNumberText
{
2017-02-13 18:11:41 +01:00
OWSAssert ( [ TSAccountManager localNumber ] ! = nil ) ;
2017-04-11 19:32:59 +02:00
return [ PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber : [ TSAccountManager localNumber ] ] ;
}
- ( void ) updatePhoneNumberLabel
{
_phoneNumberLabel . text =
[ NSString stringWithFormat : NSLocalizedString ( @ "VERIFICATION_PHONE_NUMBER_FORMAT" ,
@ "Label indicating the phone number currently being verified." ) ,
[ self phoneNumberText ] ] ;
2014-10-29 21:58:58 +01:00
}
2016-10-10 22:02:09 +02:00
- ( void ) startActivityIndicator
{
[ self . submitCodeSpinner startAnimating ] ;
2014-12-03 07:40:07 +01:00
[ self enableServerActions : NO ] ;
2016-10-10 22:02:09 +02:00
[ self . challengeTextField resignFirstResponder ] ;
2015-09-01 19:22:08 +02:00
}
2014-11-21 00:51:23 +01:00
2016-10-10 22:02:09 +02:00
- ( void ) stopActivityIndicator
{
[ self enableServerActions : YES ] ;
[ self . submitCodeSpinner stopAnimating ] ;
2014-10-29 21:58:58 +01:00
}
2017-02-09 18:02:17 +01:00
- ( void ) verifyChallengeAction : ( nullable id ) sender
2016-10-10 22:02:09 +02:00
{
[ self startActivityIndicator ] ;
[ self . accountManager registerWithVerificationCode : [ self validationCodeFromTextField ] ]
. then ( ^ {
DDLogInfo ( @ "%@ Successfully registered Signal account." , self . tag ) ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self stopActivityIndicator ] ;
2017-05-24 21:55:22 +02:00
UIStoryboard * storyboard = [ UIStoryboard main ] ;
UIViewController * viewController = [ storyboard instantiateInitialViewController ] ;
OWSAssert ( [ viewController isKindOfClass : [ SignalsNavigationController class ] ] ) ;
SignalsNavigationController * navigationController = ( SignalsNavigationController * ) viewController ;
AppDelegate * appDelegate = ( AppDelegate * ) [ UIApplication sharedApplication ] . delegate ;
appDelegate . window . rootViewController = navigationController ;
OWSAssert ( [ navigationController . topViewController isKindOfClass : [ SignalsViewController class ] ] ) ;
DDLogDebug ( @ "%@ notifying signals view controller of new user." , self . tag ) ;
SignalsViewController * signalsViewController
= ( SignalsViewController * ) navigationController . topViewController ;
signalsViewController . newlyRegisteredUser = YES ;
2016-10-10 22:02:09 +02:00
} ) ;
} )
. catch ( ^ ( NSError * _Nonnull error ) {
DDLogError ( @ "%@ error verifying challenge: %@" , self . tag , error ) ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self stopActivityIndicator ] ;
[ self presentAlertWithVerificationError : error ] ;
} ) ;
} ) ;
2014-11-21 14:38:37 +01:00
}
2016-10-10 22:02:09 +02:00
- ( void ) presentAlertWithVerificationError : ( NSError * ) error
{
2017-02-11 21:19:43 +01:00
UIAlertController * alertController ;
// In the case of the "rate limiting" error , we want to show the
// "recovery suggestion" , not the error ' s "description."
if ( [ error . domain isEqualToString : TSNetworkManagerDomain ] &&
error . code = = 413 ) {
alertController = [ UIAlertController alertControllerWithTitle : NSLocalizedString ( @ "REGISTRATION_VERIFICATION_FAILED_TITLE" ,
@ "Alert view title" )
message : error . localizedRecoverySuggestion
preferredStyle : UIAlertControllerStyleAlert ] ;
} else {
alertController = [ UIAlertController alertControllerWithTitle : NSLocalizedString ( @ "REGISTRATION_VERIFICATION_FAILED_TITLE" ,
@ "Alert view title" )
message : error . localizedDescription
preferredStyle : UIAlertControllerStyleAlert ] ;
}
2016-10-10 22:02:09 +02:00
UIAlertAction * dismissAction = [ UIAlertAction actionWithTitle : NSLocalizedString ( @ "DISMISS_BUTTON_TEXT" , nil )
style : UIAlertActionStyleDefault
2017-02-09 18:02:17 +01:00
handler : ^ ( UIAlertAction * action ) {
[ _challengeTextField becomeFirstResponder ] ;
} ] ;
2016-10-10 22:02:09 +02:00
[ alertController addAction : dismissAction ] ;
2015-12-22 12:45:09 +01:00
2016-10-10 22:02:09 +02:00
[ self presentViewController : alertController animated : YES completion : nil ] ;
2015-12-22 12:45:09 +01:00
}
2016-10-10 22:02:09 +02:00
- ( NSString * ) validationCodeFromTextField {
return [ self . challengeTextField . text stringByReplacingOccurrencesOfString : @ "-" withString : @ "" ] ;
2015-12-22 12:45:09 +01:00
}
2014-12-03 07:40:07 +01:00
# pragma mark - Send codes again
2017-02-09 18:02:17 +01:00
- ( void ) sendCodeViaSMSAction : ( id ) sender {
2014-12-03 07:40:07 +01:00
[ self enableServerActions : NO ] ;
2015-12-22 12:45:09 +01:00
2015-01-14 22:30:01 +01:00
[ _requestCodeAgainSpinner startAnimating ] ;
2015-12-22 12:45:09 +01:00
[ TSAccountManager rerequestSMSWithSuccess : ^ {
2016-10-10 22:02:09 +02:00
DDLogInfo ( @ "%@ Successfully requested SMS code" , self . tag ) ;
[ self enableServerActions : YES ] ;
[ _requestCodeAgainSpinner stopAnimating ] ;
2015-12-22 12:45:09 +01:00
}
failure : ^ ( NSError * error ) {
2016-10-10 22:02:09 +02:00
DDLogError ( @ "%@ Failed to request SMS code with error: %@" , self . tag , error ) ;
[ self showRegistrationErrorMessage : error ] ;
[ self enableServerActions : YES ] ;
[ _requestCodeAgainSpinner stopAnimating ] ;
2015-12-22 12:45:09 +01:00
} ] ;
2014-12-03 07:40:07 +01:00
}
2017-02-09 18:02:17 +01:00
- ( void ) sendCodeViaVoiceAction : ( id ) sender {
2014-12-03 07:40:07 +01:00
[ self enableServerActions : NO ] ;
2015-12-22 12:45:09 +01:00
2015-01-14 22:30:01 +01:00
[ _requestCallSpinner startAnimating ] ;
2015-12-22 12:45:09 +01:00
[ TSAccountManager rerequestVoiceWithSuccess : ^ {
2016-10-10 22:02:09 +02:00
DDLogInfo ( @ "%@ Successfully requested voice code" , self . tag ) ;
[ self enableServerActions : YES ] ;
[ _requestCallSpinner stopAnimating ] ;
2015-12-22 12:45:09 +01:00
}
failure : ^ ( NSError * error ) {
2016-10-10 22:02:09 +02:00
DDLogError ( @ "%@ Failed to request voice code with error: %@" , self . tag , error ) ;
[ self showRegistrationErrorMessage : error ] ;
[ self enableServerActions : YES ] ;
[ _requestCallSpinner stopAnimating ] ;
2015-12-22 12:45:09 +01:00
} ] ;
}
- ( void ) showRegistrationErrorMessage : ( NSError * ) registrationError {
2017-05-09 16:04:48 +02:00
[ OWSAlerts showAlertWithTitle : registrationError . localizedDescription
message : registrationError . localizedRecoverySuggestion ] ;
2014-12-03 07:40:07 +01:00
}
2015-12-22 12:45:09 +01:00
- ( void ) enableServerActions : ( BOOL ) enabled {
2014-12-03 07:40:07 +01:00
[ _challengeButton setEnabled : enabled ] ;
[ _sendCodeViaSMSAgainButton setEnabled : enabled ] ;
2015-04-10 22:35:26 +02:00
[ _sendCodeViaVoiceButton setEnabled : enabled ] ;
2014-12-03 07:40:07 +01:00
}
2017-02-09 18:02:17 +01:00
- ( void ) backButtonPressed : ( id ) sender {
[ self . navigationController popViewControllerAnimated : YES ] ;
}
2014-11-21 14:38:37 +01:00
2014-10-29 21:58:58 +01:00
# pragma mark - Keyboard notifications
2015-12-22 12:45:09 +01:00
- ( void ) initializeKeyboardHandlers {
2015-04-10 22:35:26 +02:00
UITapGestureRecognizer * outsideTabRecognizer =
2015-12-22 12:45:09 +01:00
[ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( dismissKeyboardFromAppropriateSubView ) ] ;
2014-10-29 21:58:58 +01:00
[ self . view addGestureRecognizer : outsideTabRecognizer ] ;
}
2015-12-22 12:45:09 +01:00
- ( void ) dismissKeyboardFromAppropriateSubView {
2014-10-29 21:58:58 +01:00
[ self . view endEditing : NO ] ;
}
2017-02-11 21:38:33 +01:00
- ( BOOL ) textField : ( UITextField * ) textField
shouldChangeCharactersInRange : ( NSRange ) range
replacementString : ( NSString * ) insertionText {
// Verification codes take this form : "123-456" .
//
// * We only want to let the user "6 decimal digits + 1 hyphen = 7" .
// * The user shouldn ' t have to enter the hyphen - it should be added automatically .
// * The user should be able to copy and paste freely .
// * Invalid input ( including extraneous hyphens ) should be simply ignored .
//
// We accomplish this by being permissive and trying to " take as much of the user
// input as possible " .
//
// * Always accept deletes .
// * Ignore invalid input .
// * Take partial input if possible .
NSString * oldText = textField . text ;
// Construct the new contents of the text field by :
// 1. Determining the "left" substring : the contents of the old text _before _ the deletion range .
// Filtering will remove non - decimal digit characters like hyphen "-" .
2017-02-17 03:26:26 +01:00
NSString * left = [ oldText substringToIndex : range . location ] . digitsOnly ;
2017-02-11 21:38:33 +01:00
// 2. Determining the "right" substring : the contents of the old text _after _ the deletion range .
2017-02-17 03:26:26 +01:00
NSString * right = [ oldText substringFromIndex : range . location + range . length ] . digitsOnly ;
2017-02-11 21:38:33 +01:00
// 3. Determining the "center" substring : the contents of the new insertion text .
2017-02-17 03:26:26 +01:00
NSString * center = insertionText . digitsOnly ;
2017-02-11 21:38:33 +01:00
// 3 a . Trim the tail of the "center" substring to ensure that we don ' t end up
// with more than 6 decimal digits .
while ( center . length > 0 &&
left . length + center . length + right . length > 6 ) {
center = [ center substringToIndex : center . length - 1 ] ;
2015-12-22 12:45:09 +01:00
}
2017-02-11 21:38:33 +01:00
// 4. Construct the "raw" new text by concatenating left , center and right .
NSString * rawNewText = [ [ left stringByAppendingString : center ]
stringByAppendingString : right ] ;
// 5. Construct the "formatted" new text by inserting a hyphen if necessary .
NSString * formattedNewText = ( rawNewText . length <= 3
? rawNewText
: [ [ [ rawNewText substringToIndex : 3 ]
stringByAppendingString : @ "-" ]
stringByAppendingString : [ rawNewText substringFromIndex : 3 ] ] ) ;
textField . text = formattedNewText ;
// Move the cursor after the newly inserted text .
NSUInteger newInsertionPoint = left . length + center . length ;
if ( newInsertionPoint > 3 ) {
// Nudge the cursor to the right to reflect the hyphen
// if necessary .
newInsertionPoint + + ;
2015-12-22 12:45:09 +01:00
}
2017-02-11 21:38:33 +01:00
UITextPosition * newPosition = [ textField positionFromPosition : textField . beginningOfDocument
offset : ( NSInteger ) newInsertionPoint ] ;
textField . selectedTextRange = [ textField textRangeFromPosition : newPosition
toPosition : newPosition ] ;
return NO ;
2015-12-22 12:45:09 +01:00
}
2017-02-09 18:02:17 +01:00
- ( BOOL ) textFieldShouldReturn : ( UITextField * ) textField {
[ self verifyChallengeAction : nil ] ;
[ textField resignFirstResponder ] ;
return NO ;
}
2015-12-22 12:45:09 +01:00
2017-02-09 18:02:17 +01:00
- ( void ) setVerificationCodeAndTryToVerify : ( NSString * ) verificationCode {
2017-02-17 03:26:26 +01:00
NSString * rawNewText = verificationCode . digitsOnly ;
NSString * formattedNewText = ( rawNewText . length <= 3
? rawNewText
: [ [ [ rawNewText substringToIndex : 3 ]
stringByAppendingString : @ "-" ]
stringByAppendingString : [ rawNewText substringFromIndex : 3 ] ] ) ;
self . challengeTextField . text = formattedNewText ;
// Move the cursor after the newly inserted text .
UITextPosition * newPosition = [ self . challengeTextField endOfDocument ] ;
self . challengeTextField . selectedTextRange = [ self . challengeTextField textRangeFromPosition : newPosition
toPosition : newPosition ] ;
2017-02-09 18:02:17 +01:00
[ self verifyChallengeAction : nil ] ;
2015-01-30 22:58:23 +01:00
}
2016-10-10 22:02:09 +02:00
# pragma mark - Logging
+ ( NSString * ) tag
{
return [ NSString stringWithFormat : @ "[%@]" , self . class ] ;
}
- ( NSString * ) tag
{
return self . class . tag ;
}
2014-10-29 21:58:58 +01:00
@ end
2016-10-10 22:02:09 +02:00
NS_ASSUME _NONNULL _END