patternMinor
Ensuring non-expired token before every request
Viewed 0 times
nonexpiredrequesteverytokenbeforeensuring
Problem
I'm developing an iOS application where data is fetched from a third-party REST API. Each request must contain an authorization token.
In order to implement this, I've written a
Before every requests that requires Authorization,
I'm not happy with this code for the following reasons:
-
The nested nature of the callbacks makes it look ugly and somewhat unreadable
-
Too much repetition: The code to get the token is verbose and must be
In order to implement this, I've written a
fetchToken withCompletionBlock:(void (^)(ASToken) token,NSError error) method in AppDelegate:-(void) fetchTokenWithCompletionBlock:(void (^)(ASToken * token, NSError * error)) completionBlock
{
if (!_token) {
_token = [self retrieveTokenFromKeychain];
}
if (!_token || _token.isExpired) {
[ASMyApi loadTokenWithcompletionBlock:^(ASToken *token, NSError* error) {
if (token) {
_token = token;
[self saveTokenToKeychain: token];
}else{
completionBlock(nil,&error);
}
}];
}else{
completionBlock(_token,nil);
}
}Before every requests that requires Authorization,
fetchToken withCompletionBlock:(void (^)(ASToken*) token,NSError ** error) is called to retrieve the token. Here's an example:-(IBAction) loadCategories
{
[self.refreshControl beginRefreshing];
[((AppDelegate *)[UIApplication sharedApplication].delegate) fetchTokenWithCompletionBlock:^(ASToken *token, NSError *error) {
if (token) {
[ASMyApi loadCategories:token.token completionBlock:^(NSArray *categories, NSError *error) {
[self.refreshControl endRefreshing];
if (categories){
self.categories = categories;
}else{
[self monitorNetworkReachability];
}
[self updateUI];
}];
}else{
[self.refreshControl endRefreshing];
[self monitorNetworkReachability];
}
}];
}I'm not happy with this code for the following reasons:
-
The nested nature of the callbacks makes it look ugly and somewhat unreadable
-
Too much repetition: The code to get the token is verbose and must be
Solution
You could introduce a delegate instead passing another block in
First, the protocol:
You change the signature of
In your view controller you'll have to implement the required methods from
loadCategories method. First, the protocol:
@protocol ASMyApiCategoriesDelegate
@required
- (void) ASMyApiCategoriesDidLoad:(NSArray *)categories;
- (void) ASMyApiCategoriesDidFailToLoadWithError:(NSError *)error
@endYou change the signature of
+ [ASMyApi loadCategories:completionBlock:] to accept a delegate:+ (void) loadCategoriesToken:(id)token delegate:(id )delegate
{
NSArray *categories;
// code that loads categories
if (categories)
{
[delegate ASMyApiCategoriesDidLoad:categories];
}
else
{
[delegate ASMyApiCategoriesDidFailToLoadWithError:nil];
}
}In your view controller you'll have to implement the required methods from
ASMyApiCategoriesDelegate protocol and update loadCategories method.- (void) ASMyApiCategoriesDelegateDidLoad:(NSArray *)categories
{
[self.refreshControl endRefreshing];
self.categories = categories;
[self updateUI];
}
- (void) ASMyApiCategoriesDelegateDidFailToLoadWithError:(NSError *)error
{
[self.refreshControl endRefreshing];
[self monitorNetworkReachability];
}
- (IBAction) loadCategories
{
[self.refreshControl beginRefreshing];
[((AppDelegate *)[UIApplication sharedApplication].delegate) fetchTokenWithCompletionBlock:^(ASToken *token, NSError *error) {
if (token)
{
[ASMyApi loadCategories:token.token delegate:self];
}
else
{
[self ASMyApiCategoriesDidFailToLoadWithError:nil];
}
}];
}Code Snippets
@protocol ASMyApiCategoriesDelegate <NSObject>
@required
- (void) ASMyApiCategoriesDidLoad:(NSArray *)categories;
- (void) ASMyApiCategoriesDidFailToLoadWithError:(NSError *)error
@end+ (void) loadCategoriesToken:(id)token delegate:(id <ASMyApiCategoriesDelegate>)delegate
{
NSArray *categories;
// code that loads categories
if (categories)
{
[delegate ASMyApiCategoriesDidLoad:categories];
}
else
{
[delegate ASMyApiCategoriesDidFailToLoadWithError:nil];
}
}- (void) ASMyApiCategoriesDelegateDidLoad:(NSArray *)categories
{
[self.refreshControl endRefreshing];
self.categories = categories;
[self updateUI];
}
- (void) ASMyApiCategoriesDelegateDidFailToLoadWithError:(NSError *)error
{
[self.refreshControl endRefreshing];
[self monitorNetworkReachability];
}
- (IBAction) loadCategories
{
[self.refreshControl beginRefreshing];
[((AppDelegate *)[UIApplication sharedApplication].delegate) fetchTokenWithCompletionBlock:^(ASToken *token, NSError *error) {
if (token)
{
[ASMyApi loadCategories:token.token delegate:self];
}
else
{
[self ASMyApiCategoriesDidFailToLoadWithError:nil];
}
}];
}Context
StackExchange Code Review Q#111405, answer score: 6
Revisions (0)
No revisions yet.