Reldonas
Reldonas

Reputation: 3

AFHTTPSessionManager with multiple requests in rapid succession (AFNetworking 2.0)

i am new to iOS programming, still learning.

EDIT: !!!!!! Everything in my code works. My question is about the delegation pattern i use, if i am generating problems in the background that i have no idea of, or if there is a better way to handle my situation in AFNetworking...

I have created an API for my app by subclassing AFHTTPSessionManager. My API creates a singleton and returns it and supplies public functions for various requests. And those functions create parameter lists, and make GET requests on the server like this:

- (void)getCharacterListForKeyID:(NSString *)keyID vCode:(NSString *)vCode sender:(id)delegate
{
    NSMutableDictionary *parameters = [NSMutableDictionary dictionary];

    parameters[@"keyID"] = keyID;
    parameters[@"vCode"] = vCode;

    [self GET:@"account/Characters.xml.aspx" parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {

        self.xmlWholeData = [NSMutableDictionary dictionary];
        self.errorDictionary = [NSMutableDictionary dictionary];
        NSXMLParser *XMLParser = (NSXMLParser *)responseObject;
        [XMLParser setShouldProcessNamespaces:YES];
        XMLParser.delegate = self;
        [XMLParser parse];

        if ([delegate respondsToSelector:@selector(EVEAPIHTTPClient:didHTTPRequestWithResult:)]) {
            [delegate EVEAPIHTTPClient:self didHTTPRequestWithResult:self.xmlWholeData];
        }
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        if ([delegate respondsToSelector:@selector(EVEAPIHTTPClient:didFailWithError:)]) {
            [delegate EVEAPIHTTPClient:self didFailWithError:error];
        }
    }];
}

I was using a normal protocol/delegate method earlier. But once i make calls this API more than once like this: (IT WAS LIKE THIS:)

EVEAPIHTTPClient *client = [EVEAPIHTTPClient sharedEVEAPIHTTPClient];
client.delegate = self;
[client getCharacterListForKeyID:self.keyID vCode:self.vCode];

Previous call's delegate was being overwritten by next. So i changed to above style. Passing sender as an argument in the function:

EVEAPIHTTPClient *client = [EVEAPIHTTPClient sharedEVEAPIHTTPClient];
[client getCharacterListForKeyID:self.keyID vCode:self.vCode sender:self];

And i pass this sender to GET request's success and failure blocks.

What i wonder is : "Is this a good programming practice ?". Passing objects to blocks like this should be avoided if possible ? Is there any other more elegant way in AFHTTPSessionManager to handle this type of work (making same GET request over and over with different parameters and returning results to the respective request owners) more elegantly ?

Upvotes: 0

Views: 3027

Answers (1)

Eugene
Eugene

Reputation: 10045

Delegation pattern falters when it comes to simplicity and asynchronous request processing. You should be using blocks, here's an example

Your server class:

static NSString *const kNews = @"user_news/"; // somewhere above the @implementation

- (NSURLSessionDataTask *)newsWithPage:(NSNumber *)page
                            lastNewsID:(NSNumber *)lastNewsID
                            completion:(void (^)(NSString *errMsg, NSArray *news, NSNumber *nextPage))completionBlock {
  return [self GET:kNews
        parameters:@{@"page" : page,
                     @"news_id" : lastNewsID
                     }
           success:^(NSURLSessionDataTask *task, id responseObject) {
             NSArray *news = nil;
             NSNumber *nextPage = nil;
             NSString *errors = [self errors:responseObject[@"errors"]]; // process errors

             if ([responseObject[@"status"] boolValue]) {
               news = responseObject[@"news"];
               nextPage = responseObject[@"next_page"];
               [self assignToken];
             }

             completionBlock(errors, news, nextPage);
           }
           failure:^(NSURLSessionDataTask *task, NSError *error) {
             NSString *errors = [self errors:error];
             completionBlock(errors, nil, nil);
           }];
}

The caller

- (void)dealloc {
  [_task cancel]; // you don't want this task to execute if user suddenly removes your controller from the navigation controller's stack
}

- (void)requestNews {
  typeof(self) __weak wself = self; // to avoid the retain cycle
  self.task = [[GSGServer sharedInstance] newsWithPage:self.page
                                            lastNewsID:self.lastNewsID
                                            completion:^(NSString *errMsg, NSArray *news, NSNumber *nextPage) {
                                                 if (errMsg) {
                                                   [GSGAppDelegate alertQuick:errMsg]; // shortcut for posting UIAlertView, uses errMsg for message and "Error" as a title
                                                   return;
                                                 }
                                                 [wself.news addObjectsFromArray:news];
                                                 wself.lastNewsID = [wself.news firstObject][@"id"];
                                                 wself.page = nextPage;
                                                 [wself.tableView reloadData];
                                               }];
}

Upvotes: 2

Related Questions