Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return data gotten from a web service in objective- c (iPhone)?

This might be a dumb question. Sorry if it is.

But Im working on a project that consumes web services. I can connect to the web service and get the data I need fine.

I would like to have a method that returns this data obtained from the web service to the caller. The only problem is that the data is only obtained inside the ConnectionDidFinishLoading method, and I can't access this data from my method.

here is my code, that works fine:

- (NSData *) dataForMethod:(NSString *)webMethod withPostString:(NSString *)postString
{
    NSURL *url = [NSURL URLWithString:[SigameWebServiceAddress stringByAppendingFormat:@"%@%@", @"/", webMethod]];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
    NSString *msgLength = [NSString stringWithFormat:@"%d", [postString length]];

    [req addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
    [req setHTTPMethod:@"POST"];
    [req setHTTPBody: [postString dataUsingEncoding:NSUTF8StringEncoding]];

    conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
    if (conn) 
    {
        webData = [NSMutableData data];
    }   

    // I WOULD LIKE TO RETURN WEBDATA TO THE CALLER HERE, BUT WEBDATA IS EMPTY NOW, THE  
    //connectionDidFinishLoading ONLY GETS CALLED WITH THE DATA I WANT AFTER THE COMPILER
    //IS DONE EXECUTING MY METHOD.
}

-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) response 
{
    [webData setLength: 0];
}

-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data 
{
    [webData appendData:data];
}

-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error 
{
    NSLog(@"FATAL ERROR");
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    NSLog(@"DONE. Received Bytes: %d", [webData length]);

    NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

    //---shows the XML---
    NSLog(@"%@", theXML);  //NOW, THIS IS THE DATA I WANT. BUT HOW CAN I RETURN THIS TO 
                           //THE CALLER. I MEAN, THE CALLER THAT CALLED MY METHOD 
                           //+ (NSData *) dataForMethod: withPostString:
}

Any help here is appreciated! Thanks

like image 478
Rafael Moreira Avatar asked Jan 14 '12 19:01

Rafael Moreira


1 Answers

There are really two ways to go about this.

  1. Create a delegate interface
  2. Use Blocks

I would strongly advise against using the synchronous methods - unless you are/have created your own asynchronous framework around them (i.e. you are manually starting another thread and executing your synchronous request on that thread). In the long run you will realize you need the requests to be async, and you'll have to re-work everything such that they are.

To give a quick overview of the two options I gave:

1. Create a delegate interface

The idea here is to create a class which performs the request, and create a protocol the caller must implement. When the request is complete, you will invoke a specified method on the delegate with the data:

The protocol might look something like this:

@protocol RequestClassDelegate <NSObject>

- (void)requestCompleted:(ResponseClass *)data;
- (void)requestError:(NSError *)error;

@end

The class which makes the request might look something like this:

@interface RequestClass : NSObject

- (void)makeRequest:(id<RequestClassDelegate>)delegate;

@end

And the request class implementation might contain some of the following, in addition to your connection logic:

@implementation RequestClass
{
    __weak id<RequestClassDelegate> _delegate;
}

// Connection Logic, etc.

- (void)makeRequest:(id<RequestClassDelegate>)delegate
{
    _delegate = delegate;
    // Initiate the request...
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

    // Processing, etc.

    // Here we'll call the delegate with the result:
    [_delegate requestCompleted:theResult];
}

@end

2. Use Blocks

This solution is much the same as the first solution - but, a bit more elegant in my opinion. Here, we'll change the RequestClass to use blocks instead of a delegate:

typedef void (^requestCompletedBlock)(id);
typedef void (^requestErrorBlock)(NSError *);
@interface RequestClass : NSObject

@property (nonatomic, copy) requestCompletedBlock completed;
@property (nonatomic, copy) requestErrorBlock errored;

- (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error;

@end

And the implementation of that might look something like this:

@implementation RequestClass

@synthesize completed = _completed;
@synthesize errored = _errored;

// Connection Logic, etc.

- (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error
{
    self.completed = completed;
    self.errored = error;
    // Initiate the request...
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

    // Processing, etc.

    // Here we'll call the delegate with the result:
    self.completed(theResult);
}

@end
like image 129
Steve Avatar answered Oct 17 '22 01:10

Steve