iOS: creating a Login Module and passing data between scenes

Scenario: from a storyboard scene present a login module that consists of two scenes: a login scene and a ‘waiting while logging in’ scene. The presented login module returns a response object to the presenting storyboard scene.

The login module consists of two xib files, each xib file has its own view controller:
1. LoginViewController1, with a login screen and a submit button, is the presenting LoginViewController for the presented LoginViewController2,
2. LoginViewController2, with a waiting gif, is the presented LoginViewController for the presenting LoginViewController.

Both presented LoginViewControllers are passing data back to their presenting viewControllers. For this purpose the presented LoginViewControllers define (1) a delegate protocol, (2) a delegate property and (3) call the presenting LoginViewController’s delegate protocol method implementation, and the presenting LoginViewControllers (1) implement the delegate protocol methods and (2) are set to be the delegate of the presented LoginViewController.


// LoginViewController1.h
#import "LoginViewController2.h"
#import "LoginResponse.h"
@class LoginViewController1;
@protocol LoginViewController1Delegate <NSObject>
/**
* the delegate methods must be implemented by the presenting view controller
*/
- (void)returnFrom:(LoginViewController1 *)controller withResponse:(LoginResponse *)response;
@end
@interface LoginViewController1 : UIViewController <LoginViewController2Delegate>
@property (weak, nonatomic) IBOutlet UITextField *usernameTextfield;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextfield;
- (IBAction)loginButtonPressed:(id)sender;
@end


// LoginViewController1.m
#import "LoginViewController1.h"
#import "LoginViewController2.h"
@interface LoginViewController1 ()
@end
@implementation LoginViewController1
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.passwordTextfield.secureTextEntry = YES;
}
- (IBAction)loginButtonPressed:(id)sender {
NSLog(@"loginButtonPressed-1");
LoginViewController2 *vc2 = [[LoginViewController2 alloc] initWithNibName:@"LoginViewController2" bundle:nil];
vc2.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
vc2.delegate = self;
[self presentViewController:vc2 animated:YES completion:nil];
//[self presentModalViewController: vc2 animated:YES];
}
- (void)returnFrom:(LoginViewController2 *)controller withResponse:(LoginResponse *)response
{
// note that the dismiss is defined within the completion callback to prevent 'dismiss while being presented' error
[controller dismissViewControllerAnimated:YES completion:^{
LoginResponse *response1 = [[LoginResponse alloc] init];
response1 = response;
[self.delegate returnFrom:self withResponse:response1];
}];
}
@end

If you are using a StoryBoard in stead of separate xib files, you replace the loginButtonPressed action with a modal segue, and in the prepareForSegue you set the delegate for the destinationViewController.


- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
LoginViewController2 *vc2 =[segue destinationViewController];
vc2.delegate = self;
}


// LoginViewController2.h
#import "LoginResponse.h"
@class LoginViewController2;
@protocol LoginViewController2Delegate <NSObject>
/**
* the delegate methods must be implemented by the presenting view controller
*/
- (void)returnFrom:(LoginViewController2 *)controller withResponse:(LoginResponse *)response;
@end
@interface LoginViewController2 : UIViewController
/**
* The presenting view controller must be set to be the delegate and extend the presented class
*/
@property (weak, nonatomic) id delegate;
@end


// LoginViewController2.m
#import "LoginViewController2.h"
@interface LoginViewController2 ()
@end
@implementation LoginViewController2
- (void) receivedLoginResponseFromServer
{
LoginResponse *response = [[LoginResponse alloc] init];
response.authorizationToken = @"authToken from vc2";
[self.delegate returnFrom:self withResponse:response];
@end

The view controller in the storyboard that calls the login module must implement the protocol.


// MainViewController.h
#import "LoginViewController1.h"
@interface MainViewController : UIViewController <LoginViewController1Delegate>
@end


// MainViewController.m
#import "MainViewController.h"
#import "LoginViewController1.h"
@interface MainViewController ()
@end
@implementation MainViewController
@synthesize authToken;
- (void) viewDidAppear:(BOOL)animated
{
NSLog(@"mvc viewDidAppear");
if(![self isLoggedIn]){
NSLog(@"not logged in");
[self promptLogin];
}else{
NSLog(@"is logged in");
}
}
-(void) promptLogin
{
LoginViewController1 *vc1 = [[LoginViewController1 alloc] initWithNibName:@"LoginViewController1" bundle:nil];
vc1.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
vc1.delegate = self;
[self presentViewController:vc1 animated:YES completion:nil];
}
- (void)returnFrom:(LoginViewController1 *)controller withResponse:(LoginResponse *)response
{
NSLog(@"mvc response: %@", response.authorizationToken);
self.authToken = response.authorizationToken;
[controller dismissViewControllerAnimated:YES completion:nil];
}

If you are using a LoginStoryBoard instead of separate xib files, the promptLogin method would look as follows.

-(void) promptLogin
{
UIStoryboard *loginStoryBoard = [UIStoryboard storyboardWithName:@"LoginStoryboard" bundle:nil];
LoginViewController1 *vc1 = [loginStoryBoard instantiateInitialViewController];
vc1.delegate = self;
vc1.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
vc1.delegate = self;
[self presentViewController:vc1 animated:YES completion: nil];
}

Download:
LoginModule_ios.zip

Leave a Reply

Your email address will not be published. Required fields are marked *