AFNetworking Authentication with Web Service

Having a problem that relates more to design than to code.

My iOS apps with json web interface. I use AFNetworking, and my problem is mostly I need the init function (which authenticates the AFHTTPClient and retrieves the token) in order to finish completely before I make additional requests (which require the specified token).

From the code below, I would be interested to hear design approaches to achieve this, I would prefer to save all async requests, an alternative solution would be to make a request in initWithHost: port: user: pass synous (without using AFNetworking), which I know is bad practice and you want to avoid.

DCWebServiceManager.h

#import <Foundation/Foundation.h> #import "AFHTTPClient.h" @interface DCWebServiceManager : NSObject { NSString *hostServer; NSString *hostPort; NSString *hostUser; NSString *hostPass; NSString *hostToken; AFHTTPClient *httpClient; } // Designated Initialiser - (id)initWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass; // Instance Methods - (void)getFileList; @end 

DCWebServiceManager.m

 #import "DCWebServiceManager.h" #import "AFHTTPClient.h" #import "AFHTTPRequestOperation.h" #import "AFJSONRequestOperation.h" @implementation DCWebServiceManager - (id)initWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass { self = [super init]; if (self) { hostServer = host; hostPort = port; hostUser = user; hostPass = pass; NSString *apiPath = [NSString stringWithFormat:@"http://%@:%@/", hostServer, hostPort]; httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:apiPath]]; [httpClient setAuthorizationHeaderWithUsername:hostUser password:hostPass]; NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:@"authenticate.php" parameters:nil]; AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){ // Do operations to parse request token to be used in // all requests going forward... // ... // ... // Results in setting: hostToken = '<PARSED_TOKEN>' NSLog(@"HostToken: >>%@<<", hostToken); } failure:^(AFHTTPRequestOperation *operation, NSError *error){ NSLog(@"ERROR: %@", operation.responseString); }]; [operation start]; } return self; } - (void)getFileList { // ************************* // The issue is here, getFileList gets called before the hostToken is retrieved.. // Make the authenticate request in initWithHost:port:user:pass a synchronous request perhaps?? // ************************* NSLog(@"IN GETFILELIST: %@", hostToken); // Results in host token being nil!!! NSString *queryString = [NSString stringWithFormat:@"?list&token=%s", hostToken]; NSMutableURLRequest *listRequest = [httpClient requestWithMethod:@"GET" path:queryString parameters:nil]; AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:listRequest success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON){ NSLog(@"SUCCESS!"); } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){ NSLog(@"ERROR!: %@", error); }]; [operation start]; } @end 

ViewController.m

 .... DCWebServiceManager *manager = [[DCWebServiceManager alloc] initWithHost:@"localhost" port:@"23312" user:@"FOO" pass:@"BAR"]; [manager getFileList]; // OUTPUTS IN GETFILELIST: (nil) HostToken: >>sdf5fdsfs46a6cawca6<< .... ... 
+4
source share
2 answers

I would suggest subclassing AFHTTPClient and adding +sharedInstance and properties for the token.

 + (MyGClient *)sharedInstanceWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass { static MyClient *sharedInstance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [... your code from the init ...] }); return sharedInstance; } 

Then you can override enqueueHTTPRequestOperationWithRequest:success:failure to check the token before the queue of additional operations.

In addition, you can collect operations and insert them into the queue as soon as the token is set by overriding the installer for the property.

0
source

Like @ patric.schenke, you can subclass AFHTTPClient if you want to clear part of your code, but the real problem is that you need to make a different authentication request (if your token is zero) before making a getFileList request.

I would recommend using blocks in the same way that AFNetworking uses blocks to remain asynchronous. Move your HTTP call to your own method and only call it when your hostToken is zero:

 - (void)getFileList { if (self.token == nil) { [self updateTokenThenWhenComplete:^(void){ // make HTTP call to get file list }]; } else { // make HTTP call to get file list } } - (void)updateTokenThenWhenComplete:(void (^))callback { //... make HTTP request [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){ self.token = responseObject.token; callback(); } failure:^(AFHTTPRequestOperation *operation, NSError *error){ //... }]; } 
0
source

All Articles