Matt Price
Matt Price

Reputation: 34629

Creating a class to return data from an Async call on webservice

I'm writing an app which uses a web service to get some JSON data, I will need get different data from the web service across my different view controllers. So I want to create a class to handle this, with the aim to releasing this in the future.

In my class I want to make use of the AFNetworking framework using AFJSONRequestOperation to get the JSON data from the web service but this returns the data asynchronously, so isn't as simple as just returning data on the class method.

How do I make my class handle this data and pass it back to the calling class? Do I have to use delegate like I would normally when passing data back or is there another way?

+(NSDictionary*)fetchDataFromWebService:(NSString *)query{

NSURL *url = [NSURL URLWithString:query];

NSURLRequest *request = [NSURLRequest requestWithURL:url];

AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
    NSLog(@"Success");


} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
    NSLog(@"Fail");
}];

[operation start];

return ??? // I can't return anything here because AFJSONRequestOperation is completed Async
}

So should I do this, and use a delegate

+(void)fetchDataFromWebService:(NSString *)query{

NSURL *url = [NSURL URLWithString:query];

NSURLRequest *request = [NSURLRequest requestWithURL:url];

AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
    NSLog(@"Success");
    [self.delegate didFinishFetchingJSON:(NSDictionary*)json];        

} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
    NSLog(@"Fail");
    [self.delegate didFinishFetchingJSON:(NSDictionary*)json withError:(NSError*)error];
}];

[operation start];
}

Any help on the best way and best practice to create this sort of class using an async call would be very helpful.

Many Thanks in advance

Upvotes: 0

Views: 920

Answers (2)

Marcus Adams
Marcus Adams

Reputation: 53880

Yes, you'll use delegate methods.

I use a regular NSURLRequest and NSUrlConnection object and its delegate methods to make asynchronous calls and use NSJSONSerialization to parse the JSON into a NSDictionary. No third party libraries required.

NSURLRequest also has a dictionary that you can use to set any kind of data you will need to handle the request once it's returned. This way, you could potentially handle all of your requests in the same delegate methods and figure out what to do based on the request properties.

URL Loading System Programming Guide

By default NSUrlConnection's initRequest method even runs the delegate methods on the main thread, so you wouldn't have any thread safety concerns. However, you can also have them run on a separate thread by setting startImmediately to NO.

I'm not averse to using third party libraries, but this isn't a case where you need them.

Upvotes: 1

Keith Smiley
Keith Smiley

Reputation: 63994

When you do async calls like this definitely don't expect a traditional return setup. Your delegate idea would definitely work, you could also pass data around as a @property or something. But my preferred way is this:

 - (void)postText:(NSString *)text
   forUserName:(NSString *)username
 ADNDictionary:(NSDictionary *)dictionary
     withBlock:(void(^)(NSDictionary *response, NSError *error))block;

I declare the method with a block as a parameter. The implementation looks like this:

- (void)postText:(NSString *)text
     forUserName:(NSString *)username
   ADNDictionary:(NSDictionary *)dictionary
       withBlock:(void(^)(NSDictionary *response, NSError *error))block
{
    // Custom logic

    [[KSADNAPIClient sharedAPI] postPath:@"stream/0/posts"
                              parameters:params
                                 success:^(AFHTTPRequestOperation *operation, id responseObject)
     {
         if (block) {
             block(responseObject, nil);
         }
     }
                                 failure:^(AFHTTPRequestOperation *operation, NSError *error)
     {
         if (block) {
             block([NSDictionary dictionary], error);
         }
     }];
}

As you can see once I get a response from the web service I pass the objects back into the block that it was called with. I call this method with:

[[KSADNAPIClient sharedAPI] postText:postText
                             forUserName:username
                           ADNDictionary:parameters
                               withBlock:^(NSDictionary *response, NSError *error)
    {
        if (error) {
          // Handle error
        } else {
          // Do other stuff
        }
    }];

So once you call it this block doesn't do anything until it gets a response from the service. Then inside this block if you wanted to you could call something like:

[self loadInfo:response];

Upvotes: 2

Related Questions