theMik
theMik

Reputation: 13

AFNetworking HTTPRequestOperation need to set array from completion block but this isn't working?

I'm using AFNetworking with AFHTTPRequestOperation to pull XML data from a webservice. This is working fine and im getting the data I need but I need to split this data into objects and initialize a NSMutableArray with this data. This is working in the completion block, but just before I return the array in my method the data is gone? How do I do this?

Here is some of my code:

NSMutableArray *result = [[NSMutableArray alloc] init];    
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSString* response = [operation responseString];
    NSData* xmlData = [response dataUsingEncoding:NSUTF8StringEncoding];
    NSError *xmlError;
    GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&xmlError];
    NSArray *allElements = [doc.rootElement elementsForName:@"Misc"];
    for (GDataXMLElement *current in allElements)
    {
        NSString *titel;
        NSString *tekst;

        NSArray *titels = [current elementsForName:@"Titel"];
        if(titels.count > 0)
        {
            GDataXMLElement *firstTitel = (GDataXMLElement *) [titels objectAtIndex:0];
            titel = firstTitel.stringValue;
        } else continue;

        NSArray *teksts = [current elementsForName:@"Tekst"];
        if(teksts.count > 0)
        {
            GDataXMLElement *firstTekst = (GDataXMLElement *) [teksts objectAtIndex:0];
            tekst = firstTekst.stringValue;
        } else continue;

        HVMGUniversalItem *item = [[HVMGUniversalItem alloc] initWithTitel:titel AndTekst:tekst];
        [result addObject:item];
    }
    NSLog(@"%i", result.count);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", [operation error]);
}];

[operation start];
NSLog(@"%i", result.count);
return result;

What am I doing wrong? Why isn't the data present in the array when returning?

Upvotes: 1

Views: 2878

Answers (1)

Lorenzo B
Lorenzo B

Reputation: 33428

Why isn't the data present in the array when returning?

Because AFNetworking use an async pattern. So the return code will be performed before the operation will be completed.

You need to use a different approach or follow Can AFNetworking return data synchronously (inside a block)?. The latter is discouraged.

A solution could be to:

-> Create a NSOperationQueue within your class that will include your operation. Create it as a property for your class like.

@property (nonatomic, strong, readonly) NSOperationQueue* downloadQueue;

- (NSOperationQueue*)downloadQueue
{
    if(downloadQueue) return downloadQueue;

    downloadQueue = // alloc init here
}

-> Create a property for your array (synthesize also it)

@property (nonatomic, strong) NSMutableArray* result;

-> Wrap your code within a specific method like doOperation.

self.result = [[NSMutableArray alloc] init];

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];

__weak YourClass* selfBlock = self; 

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSString* response = [operation responseString];
    NSData* xmlData = [response dataUsingEncoding:NSUTF8StringEncoding];
    NSError *xmlError;
    GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&xmlError];
    NSArray *allElements = [doc.rootElement elementsForName:@"Misc"];
    for (GDataXMLElement *current in allElements)
    {
        NSString *titel;
        NSString *tekst;

        NSArray *titels = [current elementsForName:@"Titel"];
        if(titels.count > 0)
        {
            GDataXMLElement *firstTitel = (GDataXMLElement *) [titels objectAtIndex:0];
            titel = firstTitel.stringValue;
        } else continue;

        NSArray *teksts = [current elementsForName:@"Tekst"];
        if(teksts.count > 0)
        {
            GDataXMLElement *firstTekst = (GDataXMLElement *) [teksts objectAtIndex:0];
            tekst = firstTekst.stringValue;
        } else continue;

        HVMGUniversalItem *item = [[HVMGUniversalItem alloc] initWithTitel:titel AndTekst:tekst];
        [selfBlock.result addObject:item];
    }
    NSLog(@"%i", result.count);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", [operation error]);
}];

[downloadQueue addOperation:operation];

-> if you need to notify that result has object send a notification, use the delegate pattern, etc...

Hope that helps.

Upvotes: 1

Related Questions