Jann
Jann

Reputation: 2213

Converting from ASIHTTPRequest to AFNetworking

I am converting my app routines from ASIHTTP to AFNetworking due to the unfortunate discontinuation of work on that project ... and what I found out later to be the much better and smaller codebase of AFNetworking.

I am finding several issues. My code for ASIHTTPRequest is built as a method. This method takes a few parameters and posts the parameters to a url ... returning the resulting data. This data is always text, but in the interests of making a generic method, may sometimes be json, sometimes XML or sometimes HTML. Thus I built this method as a standalone generic URL downloader.

My issue is that when the routine is called I have to wait for a response. I know all the "synchronous is bad" arguments out there...and I don't do it a lot... but for some methods I want synchronous.

So, here is my question. My simplified ASIHTTP code is below, followed by the only way i could think of coding this in AFNetworking. The issue I have is that the AFNetworking sometimes does not for the response before returning from the method. The hint that @mattt gave of [operation waitUntilFinished] totally fails to hold the thread until the completion block is called... and my other method of [queue waitUntilAllOperationsAreFinished] does not necessarily always work either (and does NOT result in triggering the error portion of the [operation hasAcceptableStatusCode] clause). So, if anyone can help, WITHOUT The ever-present 'design it asynchronously', please do.

ASIHTTP version:

- (NSString *) queryChatSystem:(NSMutableDictionary *) theDict
{
    NSString *response = [NSString stringWithString:@""];
    NSString *theUrlString = [NSString stringWithFormat:@"%@%@",kDataDomain,kPathToChatScript];

    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:theUrlString]];
    for (id key in theDict)
    {
        [request setPostValue:[theDict objectForKey:key] forKey:key];
    }

    [request setNumberOfTimesToRetryOnTimeout:3];
    [request setAllowCompressedResponse:YES];
    [request startSynchronous];

    NSError *error = [request error];
    if (! error)
    {
        response = [request responseString];
    }

    return response;
}

AFNetworking version

- (NSString *) af_queryChatSystem:(NSMutableDictionary *) theDict
{
    NSMutableDictionary *theParams = [NSMutableDictionary dictionaryWithCapacity:1];

    for (id key in theDict)
    {
        [theParams setObject:[theDict objectForKey:key] forKey:key];
    }


    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:kDataDomain]];

    NSMutableURLRequest *theRequest = [httpClient requestWithMethod:@"POST" path:[NSString stringWithFormat:@"/%@",kPathToChatScript] parameters:theParams];


    __block NSString *responseString = [NSString stringWithString:@""];

    AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:theRequest] autorelease];

    operation.completionBlock = ^ {
        if ([operation hasAcceptableStatusCode]) {
            responseString = [operation responseString];
            NSLog(@"hasAcceptableStatusCode: %@",responseString);
        }
        else
        {
            NSLog(@"[Error]: (%@ %@) %@", [operation.request HTTPMethod], [[operation.request URL] relativePath], operation.error);
        }
    };

    NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
    [queue addOperation:operation];
    [queue waitUntilAllOperationsAreFinished];
    [httpClient release];


    return responseString;

}

Thanks very much for any ideas.

Upvotes: 4

Views: 5715

Answers (3)

jasonhao
jasonhao

Reputation: 2179

My solution is manually to run the current thread runloop until the callback have been processed.

Here is my code.

- (void)testRequest
{
    MyHTTPClient* api = [MyHTTPClient sharedInstance]; // subclass of AFHTTPClient
    NSDictionary* parameters = [NSDictionary dictionary]; // add query parameters to this dict.
    __block int status = 0;
    AFJSONRequestOperation* request = [api getPath:@"path/to/test"
                                        parameters:parameters
                                           success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                               // success code
                                               status = 1;
                                               NSLog(@"succeeded");
                                           } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                               // failure
                                               status = 2;
                                               NSLog(@"failed");
                                           }];
    [api enqueueHTTPRequestOperation:request];
    [api.operationQueue waitUntilAllOperationsAreFinished];

    while (status == 0)
    {

        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                 beforeDate:[NSDate date]];
    }

    STAssertEquals(status, 1, @"success block was executed");
}

Upvotes: 0

GianPac
GianPac

Reputation: 492

- (void)af_queryChatSystem:(NSMutableDictionary *) theDict block:(void (^)(NSString *string))block {
...
}

Now within the completionBlock do:

block(operation.responseString);

block will act as the delegate for the operation. remove

-waitUntilAllOperationsAreFinished

and

return responseString

You call this like:

[YourInstance af_queryChatSystem:Dict block:^(NSString *string) {
    // use string here
}];

Hope it helps. You can refer to the iOS example AFNetworking has

Upvotes: 3

Mundi
Mundi

Reputation: 80265

I strongly recommend to use this opportunity to convert to Apple's own NSURLConnection, rather than adopt yet another third party API. In this way you can be sure it won't be discontinued. I have found that the additional work required to get it to work is minimal - but it turns out to be much more robust and less error prone.

Upvotes: 0

Related Questions