From abc63eca27d1ab27a89db4e206a0b58b6e080171 Mon Sep 17 00:00:00 2001 From: Frederic Jacobs Date: Fri, 10 Apr 2015 22:35:26 +0200 Subject: [PATCH] Fixes issues with registration in iOS simulator. --- Signal/src/network/PushManager.m | 188 ++++++++------- .../CodeVerificationViewController.m | 221 ++++++++++-------- 2 files changed, 232 insertions(+), 177 deletions(-) diff --git a/Signal/src/network/PushManager.m b/Signal/src/network/PushManager.m index 49445869a..37c441e35 100644 --- a/Signal/src/network/PushManager.m +++ b/Signal/src/network/PushManager.m @@ -22,145 +22,173 @@ @implementation PushManager -+ (instancetype)sharedManager { ++ (instancetype)sharedManager +{ static PushManager *sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sharedManager = [self new]; + sharedManager = [self new]; }); return sharedManager; } -- (instancetype)init{ +- (instancetype)init +{ self = [super init]; if (self) { - self.missingPermissionsAlertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"") - message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"") - delegate:nil - cancelButtonTitle:NSLocalizedString(@"OK", @"") - otherButtonTitles:nil, nil]; + self.missingPermissionsAlertView = + [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"") + message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"") + delegate:nil + cancelButtonTitle:NSLocalizedString(@"OK", @"") + otherButtonTitles:nil, nil]; } return self; } #pragma mark Register device for Push Notification locally --(TOCFuture*)registerPushNotificationFuture{ +- (TOCFuture *)registerPushNotificationFuture +{ self.pushNotificationFutureSource = [TOCFutureSource new]; [UIApplication.sharedApplication registerForRemoteNotifications]; - + return self.pushNotificationFutureSource.future; } -- (void)requestPushTokenWithSuccess:(void (^)(NSData* pushToken))success failure:(failedPushRegistrationBlock)failure{ - TOCFuture *requestPushTokenFuture = [self registerPushNotificationFuture]; - +- (void)requestPushTokenWithSuccess:(void (^)(NSData *pushToken))success failure:(failedPushRegistrationBlock)failure +{ + TOCFuture *requestPushTokenFuture = [self registerPushNotificationFuture]; + [requestPushTokenFuture catchDo:^(id failureObj) { - [self.missingPermissionsAlertView show]; - failure(failureObj); - DDLogError(@"This should not happen on iOS8. No push token was provided"); + [self.missingPermissionsAlertView show]; + failure(failureObj); + DDLogError(@"This should not happen on iOS8. No push token was provided"); }]; - - [requestPushTokenFuture thenDo:^(NSData* pushToken) { - TOCFuture *registerPushTokenFuture = [self registerForPushFutureWithToken:pushToken]; - - [registerPushTokenFuture catchDo:^(id failureObj) { - failure(failureObj); - }]; - - [registerPushTokenFuture thenDo:^(id value) { - TOCFuture *userRegistration = [self registerForUserNotificationsFuture]; - - [userRegistration thenDo:^(UIUserNotificationSettings *userNotificationSettings) { - success(pushToken); - }]; + + [requestPushTokenFuture thenDo:^(NSData *pushToken) { + TOCFuture *registerPushTokenFuture = [self registerForPushFutureWithToken:pushToken]; + + [registerPushTokenFuture catchDo:^(id failureObj) { + failure(failureObj); + }]; + + [registerPushTokenFuture thenDo:^(id value) { + TOCFuture *userRegistration = [self registerForUserNotificationsFuture]; + + [userRegistration thenDo:^(UIUserNotificationSettings *userNotificationSettings) { + success(pushToken); }]; + }]; }]; } -- (void)registrationAndRedPhoneTokenRequestWithSuccess:(void (^)(NSData* pushToken, NSString* signupToken))success failure:(failedPushRegistrationBlock)failure{ +- (void)registrationAndRedPhoneTokenRequestWithSuccess:(void (^)(NSData *pushToken, NSString *signupToken))success + failure:(failedPushRegistrationBlock)failure +{ if (!self.wantRemoteNotifications) { - success([@"Fake PushToken" dataUsingEncoding:NSUTF8StringEncoding], @""); + [self registerTokenWithRedPhoneServer:[@"Fake PushToken" dataUsingEncoding:NSUTF8StringEncoding] + withSuccess:success + failure:failure]; return; } - - [self requestPushTokenWithSuccess:^(NSData* pushToken){ - [RPServerRequestsManager.sharedInstance performRequest:[RPAPICall requestTextSecureVerificationCode] success:^(NSURLSessionDataTask *task, id responseObject) { - NSError *error; - - NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:&error]; - NSString* tsToken = [dictionary objectForKey:@"token"]; - - if (!tsToken || !pushToken || error) { - failure(error); - return; - } - - success(pushToken, tsToken); - } failure:^(NSURLSessionDataTask *task, NSError *error) { - failure(error); - }]; + + [self requestPushTokenWithSuccess:^(NSData *pushToken) { + [self registerTokenWithRedPhoneServer:pushToken withSuccess:success failure:failure]; } failure:^(NSError *error) { - [self.missingPermissionsAlertView show]; - failure([NSError errorWithDomain:pushManagerDomain code:400 userInfo:@{}]); + [self.missingPermissionsAlertView show]; + failure([NSError errorWithDomain:pushManagerDomain code:400 userInfo:@{}]); }]; } --(TOCFuture*)registerForUserNotificationsFuture{ +- (void)registerTokenWithRedPhoneServer:(NSData *)pushToken + withSuccess:(void (^)(NSData *pushToken, NSString *signupToken))success + failure:(failedPushRegistrationBlock)failure +{ + [RPServerRequestsManager.sharedInstance performRequest:[RPAPICall requestTextSecureVerificationCode] + success:^(NSURLSessionDataTask *task, id responseObject) { + NSError *error; + + NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:&error]; + NSString *tsToken = [dictionary objectForKey:@"token"]; + + if (!tsToken || !pushToken || error) { + failure(error); + return; + } + + success(pushToken, tsToken); + } + failure:^(NSURLSessionDataTask *task, NSError *error) { + failure(error); + }]; +} + +- (TOCFuture *)registerForUserNotificationsFuture +{ self.userNotificationFutureSource = [TOCFutureSource new]; - UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationType)[self allNotificationTypes] - categories:nil]; + UIUserNotificationSettings *settings = + [UIUserNotificationSettings settingsForTypes:(UIUserNotificationType)[self allNotificationTypes] + categories:nil]; [UIApplication.sharedApplication registerUserNotificationSettings:settings]; return self.userNotificationFutureSource.future; } --(BOOL) needToRegisterForRemoteNotifications { +- (BOOL)needToRegisterForRemoteNotifications +{ return self.wantRemoteNotifications && (!UIApplication.sharedApplication.isRegisteredForRemoteNotifications); } --(BOOL) wantRemoteNotifications { +- (BOOL)wantRemoteNotifications +{ BOOL isSimulator = [UIDevice.currentDevice.model.lowercaseString rangeOfString:@"simulator"].location != NSNotFound; - + if (isSimulator) { // Simulator is used for debugging but can't receive push notifications, so don't bother trying to get them return NO; } - + return YES; } --(int)allNotificationTypes{ +- (int)allNotificationTypes +{ return UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge; } -- (void)validateUserNotificationSettings{ - [[self registerForUserNotificationsFuture] thenDo:^(id value) { - //Nothing to do, just making sure we are registered for User Notifications. +- (void)validateUserNotificationSettings +{ + [[self registerForUserNotificationsFuture] thenDo:^(id value){ + // Nothing to do, just making sure we are registered for User Notifications. }]; } #pragma mark Register Push Notification Token with RedPhone server --(TOCFuture*)registerForPushFutureWithToken:(NSData*)token{ +- (TOCFuture *)registerForPushFutureWithToken:(NSData *)token +{ self.registerWithServerFutureSource = [TOCFutureSource new]; - - [RPServerRequestsManager.sharedInstance performRequest:[RPAPICall registerPushNotificationWithPushToken:token] success:^(NSURLSessionDataTask *task, id responseObject) { - if ([task.response isKindOfClass: NSHTTPURLResponse.class]){ - NSInteger statusCode = [(NSHTTPURLResponse*) task.response statusCode]; - if (statusCode == 200) { - [self.registerWithServerFutureSource trySetResult:@YES]; - } else{ - DDLogError(@"The server returned %@ instead of a 200 status code", task.response); - [self.registerWithServerFutureSource trySetFailure:[NSError errorWithDomain:pushManagerDomain code:500 userInfo:nil]]; - } - } else{ - [self.registerWithServerFutureSource trySetFailure:task.response]; + + [RPServerRequestsManager.sharedInstance performRequest:[RPAPICall registerPushNotificationWithPushToken:token] + success:^(NSURLSessionDataTask *task, id responseObject) { + if ([task.response isKindOfClass:NSHTTPURLResponse.class]) { + NSInteger statusCode = [(NSHTTPURLResponse *)task.response statusCode]; + if (statusCode == 200) { + [self.registerWithServerFutureSource trySetResult:@YES]; + } else { + DDLogError(@"The server returned %@ instead of a 200 status code", task.response); + [self.registerWithServerFutureSource + trySetFailure:[NSError errorWithDomain:pushManagerDomain code:500 userInfo:nil]]; + } + } else { + [self.registerWithServerFutureSource trySetFailure:task.response]; + } + } - - } failure:^(NSURLSessionDataTask *task, NSError *error) { - [self.registerWithServerFutureSource trySetFailure:error]; - }]; - + failure:^(NSURLSessionDataTask *task, NSError *error) { + [self.registerWithServerFutureSource trySetFailure:error]; + }]; + return self.registerWithServerFutureSource.future; } diff --git a/Signal/src/view controllers/CodeVerificationViewController.m b/Signal/src/view controllers/CodeVerificationViewController.m index 2fa8fa5f7..0fbfaf563 100644 --- a/Signal/src/view controllers/CodeVerificationViewController.m +++ b/Signal/src/view controllers/CodeVerificationViewController.m @@ -22,19 +22,26 @@ @implementation CodeVerificationViewController -- (void)viewDidLoad { +- (void)viewDidLoad +{ [super viewDidLoad]; [self initializeKeyboardHandlers]; _headerLabel.text = NSLocalizedString(@"VERIFICATION_HEADER", @""); _challengeTextField.placeholder = NSLocalizedString(@"VERIFICATION_CHALLENGE_DEFAULT_TEXT", @""); - [_challengeButton setTitle:NSLocalizedString(@"VERIFICATION_CHALLENGE_SUBMIT_CODE", @"") forState:UIControlStateNormal]; - - [_sendCodeViaSMSAgainButton setTitle:NSLocalizedString(@"VERIFICATION_CHALLENGE_SUBMIT_AGAIN", @"") forState:UIControlStateNormal]; - [_sendCodeViaVoiceButton setTitle:[@" " stringByAppendingString:NSLocalizedString(@"VERIFICATION_CHALLENGE_SEND_VIAVOICE", @"")] forState:UIControlStateNormal]; - [_changeNumberButton setTitle:[@" " stringByAppendingString:NSLocalizedString(@"VERIFICATION_CHALLENGE_CHANGE_NUMBER", @"")] forState:UIControlStateNormal]; + [_challengeButton setTitle:NSLocalizedString(@"VERIFICATION_CHALLENGE_SUBMIT_CODE", @"") + forState:UIControlStateNormal]; + + [_sendCodeViaSMSAgainButton setTitle:NSLocalizedString(@"VERIFICATION_CHALLENGE_SUBMIT_AGAIN", @"") + forState:UIControlStateNormal]; + [_sendCodeViaVoiceButton + setTitle:[@" " stringByAppendingString:NSLocalizedString(@"VERIFICATION_CHALLENGE_SEND_VIAVOICE", @"")] + forState:UIControlStateNormal]; + [_changeNumberButton + setTitle:[@" " stringByAppendingString:NSLocalizedString(@"VERIFICATION_CHALLENGE_CHANGE_NUMBER", @"")] + forState:UIControlStateNormal]; } --(void)viewWillAppear:(BOOL)animated +- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self enableServerActions:YES]; @@ -42,55 +49,64 @@ [self adjustScreenSizes]; } -- (void)didReceiveMemoryWarning { +- (void)didReceiveMemoryWarning +{ [super didReceiveMemoryWarning]; } -- (IBAction)verifyChallengeAction:(id)sender { +- (IBAction)verifyChallengeAction:(id)sender +{ [self enableServerActions:NO]; [_challengeTextField resignFirstResponder]; - + [self registerWithSuccess:^{ - [_submitCodeSpinner stopAnimating]; - [Environment.getCurrent.phoneDirectoryManager forceUpdate]; - [self.navigationController dismissViewControllerAnimated:YES completion:nil]; + [_submitCodeSpinner stopAnimating]; + [Environment.getCurrent.phoneDirectoryManager forceUpdate]; + [self.navigationController dismissViewControllerAnimated:YES completion:nil]; } failure:^(NSError *error) { - [self showAlertForError:error]; - [self enableServerActions:YES]; - [_submitCodeSpinner stopAnimating]; + [self showAlertForError:error]; + [self enableServerActions:YES]; + [_submitCodeSpinner stopAnimating]; }]; } -- (void)registerWithSuccess:(void(^)())success failure:(void(^)(NSError *))failure{ +- (void)registerWithSuccess:(void (^)())success failure:(void (^)(NSError *))failure +{ [_submitCodeSpinner startAnimating]; - [[RPServerRequestsManager sharedInstance] performRequest:[RPAPICall verifyVerificationCode:_challengeTextField.text] success:^(NSURLSessionDataTask *task, id responseObject) { - - [PushManager.sharedManager registrationAndRedPhoneTokenRequestWithSuccess:^(NSData *pushToken, NSString *signupToken) { - [TSAccountManager registerWithRedPhoneToken:signupToken pushToken:pushToken success:^{ - success(); - } failure:^(NSError *error) { - failure(error); - }]; - } failure:^(NSError *error) { + [[RPServerRequestsManager sharedInstance] performRequest:[RPAPICall verifyVerificationCode:_challengeTextField.text] + success:^(NSURLSessionDataTask *task, id responseObject) { + + [PushManager.sharedManager registrationAndRedPhoneTokenRequestWithSuccess:^(NSData *pushToken, + NSString *signupToken) { + [TSAccountManager registerWithRedPhoneToken:signupToken + pushToken:pushToken + success:^{ + success(); + } + failure:^(NSError *error) { + failure(error); + }]; + } failure:^(NSError *error) { failure(error); [_submitCodeSpinner stopAnimating]; - }]; - } failure:^(NSURLSessionDataTask *task, NSError *error) { - NSHTTPURLResponse* badResponse = (NSHTTPURLResponse*)task.response; - NSError *responseError = [self errorForResponse:badResponse]; - - failure(responseError); - [_submitCodeSpinner stopAnimating]; + }]; + } + failure:^(NSURLSessionDataTask *task, NSError *error) { + NSHTTPURLResponse *badResponse = (NSHTTPURLResponse *)task.response; + NSError *responseError = [self errorForResponse:badResponse]; - }]; + failure(responseError); + [_submitCodeSpinner stopAnimating]; + + }]; } // TODO: If useful, this could possibly go in a less-specific class -- (void)showAlertForError:(NSError *)error { - +- (void)showAlertForError:(NSError *)error +{ if (error == nil) { DDLogCError(@"%@: Error condition, but no NSError to display", self.class); return; @@ -98,123 +114,134 @@ DDLogCError(@"%@: Unable to display error because localizedDescription was not set: %@", self.class, error); return; } - + NSString *alertBody = nil; if (error.localizedFailureReason.length > 0) { alertBody = error.localizedFailureReason; } else if (error.localizedRecoverySuggestion.length > 0) { alertBody = error.localizedRecoverySuggestion; } - + SignalAlertView(error.localizedDescription, alertBody); } -- (NSError *)errorForResponse:(NSHTTPURLResponse *)badResponse { - +- (NSError *)errorForResponse:(NSHTTPURLResponse *)badResponse +{ NSString *description = NSLocalizedString(@"REGISTRATION_ERROR", @""); NSString *failureReason = nil; TSRegistrationFailure failureType; - + if (badResponse.statusCode == 401) { failureReason = REGISTER_CHALLENGE_ALERT_VIEW_BODY; failureType = kTSRegistrationFailureAuthentication; - } else if (badResponse.statusCode == 413){ + } else if (badResponse.statusCode == 413) { failureReason = NSLocalizedString(@"REGISTER_RATE_LIMITING_BODY", @""); failureType = kTSRegistrationFailureRateLimit; } else { - failureReason = [NSString stringWithFormat:@"%@ %lu", NSLocalizedString(@"SERVER_CODE", @""),(unsigned long)badResponse.statusCode]; + failureReason = [NSString + stringWithFormat:@"%@ %lu", NSLocalizedString(@"SERVER_CODE", @""), (unsigned long)badResponse.statusCode]; failureType = kTSRegistrationFailureNetwork; } - - NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description, NSLocalizedFailureReasonErrorKey: failureReason}; + + NSDictionary *userInfo = + @{NSLocalizedDescriptionKey : description, NSLocalizedFailureReasonErrorKey : failureReason}; NSError *error = [NSError errorWithDomain:TSRegistrationErrorDomain code:failureType userInfo:userInfo]; - + return error; } #pragma mark - Send codes again -- (IBAction)sendCodeSMSAction:(id)sender { - +- (IBAction)sendCodeSMSAction:(id)sender +{ [self enableServerActions:NO]; - + [_requestCodeAgainSpinner startAnimating]; - [[RPServerRequestsManager sharedInstance]performRequest:[RPAPICall requestVerificationCode] success:^(NSURLSessionDataTask *task, id responseObject) { - [self enableServerActions:YES]; - [_requestCodeAgainSpinner stopAnimating]; + [[RPServerRequestsManager sharedInstance] performRequest:[RPAPICall requestVerificationCode] + success:^(NSURLSessionDataTask *task, id responseObject) { + [self enableServerActions:YES]; + [_requestCodeAgainSpinner stopAnimating]; - } failure:^(NSURLSessionDataTask *task, NSError *error) { - - DDLogError(@"Registration failed with information %@", error.description); - - UIAlertView *registrationErrorAV = [[UIAlertView alloc]initWithTitle:REGISTER_ERROR_ALERT_VIEW_TITLE - message:REGISTER_ERROR_ALERT_VIEW_BODY - delegate:nil - cancelButtonTitle:REGISTER_ERROR_ALERT_VIEW_DISMISS - otherButtonTitles:nil, nil]; - - [registrationErrorAV show]; - - [self enableServerActions:YES]; - [_requestCodeAgainSpinner stopAnimating]; - }]; + } + failure:^(NSURLSessionDataTask *task, NSError *error) { + + DDLogError(@"Registration failed with information %@", error.description); + + UIAlertView *registrationErrorAV = [[UIAlertView alloc] initWithTitle:REGISTER_ERROR_ALERT_VIEW_TITLE + message:REGISTER_ERROR_ALERT_VIEW_BODY + delegate:nil + cancelButtonTitle:REGISTER_ERROR_ALERT_VIEW_DISMISS + otherButtonTitles:nil, nil]; + + [registrationErrorAV show]; + + [self enableServerActions:YES]; + [_requestCodeAgainSpinner stopAnimating]; + }]; } -- (IBAction)sendCodeVoiceAction:(id)sender { - +- (IBAction)sendCodeVoiceAction:(id)sender +{ [self enableServerActions:NO]; - - [_requestCallSpinner startAnimating]; - [[RPServerRequestsManager sharedInstance]performRequest:[RPAPICall requestVerificationCodeWithVoice] success:^(NSURLSessionDataTask *task, id responseObject) { - - [self enableServerActions:YES]; - [_requestCallSpinner stopAnimating]; - } failure:^(NSURLSessionDataTask *task, NSError *error) { - - DDLogError(@"Registration failed with information %@", error.description); - - UIAlertView *registrationErrorAV = [[UIAlertView alloc]initWithTitle:REGISTER_ERROR_ALERT_VIEW_TITLE - message:REGISTER_ERROR_ALERT_VIEW_BODY - delegate:nil - cancelButtonTitle:REGISTER_ERROR_ALERT_VIEW_DISMISS - otherButtonTitles:nil, nil]; - - [registrationErrorAV show]; - [self enableServerActions:YES]; - [_requestCallSpinner stopAnimating]; - }]; + [_requestCallSpinner startAnimating]; + [[RPServerRequestsManager sharedInstance] performRequest:[RPAPICall requestVerificationCodeWithVoice] + success:^(NSURLSessionDataTask *task, id responseObject) { + + [self enableServerActions:YES]; + [_requestCallSpinner stopAnimating]; + + } + failure:^(NSURLSessionDataTask *task, NSError *error) { + + DDLogError(@"Registration failed with information %@", error.description); + + UIAlertView *registrationErrorAV = [[UIAlertView alloc] initWithTitle:REGISTER_ERROR_ALERT_VIEW_TITLE + message:REGISTER_ERROR_ALERT_VIEW_BODY + delegate:nil + cancelButtonTitle:REGISTER_ERROR_ALERT_VIEW_DISMISS + otherButtonTitles:nil, nil]; + + [registrationErrorAV show]; + [self enableServerActions:YES]; + [_requestCallSpinner stopAnimating]; + }]; } --(void)enableServerActions:(BOOL)enabled { +- (void)enableServerActions:(BOOL)enabled +{ [_challengeButton setEnabled:enabled]; [_sendCodeViaSMSAgainButton setEnabled:enabled]; - [_sendCodeViaVoiceButton setEnabled:enabled]; + [_sendCodeViaVoiceButton setEnabled:enabled]; } #pragma mark - Keyboard notifications -- (void)initializeKeyboardHandlers{ - UITapGestureRecognizer *outsideTabRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboardFromAppropriateSubView)]; +- (void)initializeKeyboardHandlers +{ + UITapGestureRecognizer *outsideTabRecognizer = + [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboardFromAppropriateSubView)]; [self.view addGestureRecognizer:outsideTabRecognizer]; } -- (void)dismissKeyboardFromAppropriateSubView { +- (void)dismissKeyboardFromAppropriateSubView +{ [self.view endEditing:NO]; } -- (void)adjustScreenSizes { +- (void)adjustScreenSizes +{ CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height; CGFloat blueHeaderHeight; - + if (screenHeight < 667) { self.signalLogo.hidden = YES; blueHeaderHeight = screenHeight - 400; } else { blueHeaderHeight = screenHeight - 410; } - + _headerConstraint.constant = blueHeaderHeight; }