views:

607

answers:

3

Hi All,

I need to wait for a response from a SOAP webservice, I am calling via a NSURLConnection as I need to manipulate the data being returned and then return it from my class to the calling class. I cant figure out how I can possibly do it.

Here is my code at the moment:

#import <Foundation/Foundation.h>


@interface UsersBLL : NSObject {

 NSMutableData *webData;
 NSMutableString *soapResults;
 NSXMLParser *xmlParser;
 BOOL *recordResults;
 NSNumber *EmailCount;
}

@property(nonatomic, retain) NSMutableData *webData;
@property(nonatomic, retain) NSMutableString *soapResults;
@property(nonatomic, retain) NSXMLParser *xmlParser;



-(int)checkEmailAddress:(NSString*)emailAddress;
@end

#import "UsersBLL.h"


@implementation UsersBLL
@synthesize webData;
@synthesize soapResults;
@synthesize xmlParser;

-(id)init {
 self = [super init];
 return self;
}

-(int)checkEmailAddress:(NSString*)emailAddress {
 // Build the SOAP envelope
 NSString *soapMessage = [NSString stringWithFormat:
        @"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
        "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"&gt;\n"
        "<soap:Body>\n"
        "<CheckEmailAddress xmlns=\"http://tempuri.org/\"&gt;\n"
        "<EmailAddress>%@</EmailAddress>\n"
        "</CheckEmailAddress>\n"
        "</soap:Body>\n"
        "</soap:Envelope>\n", emailAddress];

 NSLog(soapMessage);

 NSURL *url = [NSURL URLWithString:@"http://photoswapper.mick-walker.co.uk/UsersService.asmx?op=CheckEmailAddress"];
 NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
 NSString *msgLength = [NSString stringWithFormat:@"%d", [soapMessage length]];

 [theRequest addValue: @"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
 [theRequest addValue: @"http://tempuri.org/CheckEmailAddress" forHTTPHeaderField:@"SOAPAction"];
 [theRequest addValue: msgLength forHTTPHeaderField:@"Content-Length"];
 [theRequest setHTTPMethod:@"POST"];
 [theRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];

 NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

 if( theConnection )
 {
  webData = [[NSMutableData data] retain];
 }
 else
 {
  NSLog(@"theConnection is NULL");
 }
 NSLog(@"%@", EmailCount);
}

-(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(@"ERROR with theConenction");
 [connection release];
 [webData release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
 NSLog(@"DONE. Received Bytes: %d", [webData length]);
 NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
 NSLog(theXML);
 [theXML release];

 if( xmlParser )
 {
  [xmlParser release];
 }

 xmlParser = [[NSXMLParser alloc] initWithData: webData];
 [xmlParser setDelegate: self];
 [xmlParser setShouldResolveExternalEntities: YES];
 [xmlParser parse];

 [connection release];
 [webData release];
}

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *)qName
   attributes: (NSDictionary *)attributeDict
{
 if( [elementName isEqualToString:@"CheckEmailAddressResult"])
 {
  if(!soapResults)
  {
   soapResults = [[NSMutableString alloc] init];
  }
  recordResults = TRUE;
 }
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
 if( recordResults )
 {
  [soapResults appendString: string];
 }
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
 if( [elementName isEqualToString:@"CheckEmailAddressResult"])
 {
  recordResults = FALSE;
  NSNumberFormatter *formatter = [[NSNumberFormatter alloc]init];
  EmailCount = [formatter numberFromString:soapResults];
            [formatter release];
  [soapResults release];
  soapResults = nil;
 }
}

@end

As you can see CheckEmailAddress is declared as returning an integer value (I know it returns nothing in the sample above).

What I ideally want to do, is through the CheckEmailAddress method, return the value retrieved from the web service. However as the call NSURLConnection does not wait until the request has completed, I cannot do it.

I would be grateful if anyone could give me any potential ideas for work arounds.

Kind Regards

Mick

+2  A: 

The simplest solution would be using [NSURLConnection sendSynchronousRequest:returningResponse:error:].

It does not allow as much control as the approach you've taken, but is usually enough for most applications.

frenetisch applaudierend
Just what I was about to suggest - be aware though that if it never returns for some reason, your program will hang.
Wade Williams
Yes, I get that often when referring to a host which seems to exist but does not respond or is not accessable for some reason. But setting the timeout to a reasonable value did the trick most of the time.
frenetisch applaudierend
My apologies for following up with another question, but I don't see how sendSynchronousRequest can help me get the data, parse it and return the result from my methodRegards
Mick Walker
If you don't know how to parse the data then that's a completely different problem from the one you've described.
Azeem.Butt
Instead of creating an NSURLConnection you use the synchroneous method which will return the data you did assemble yourself in the connection:* methods. Just put the code from connectionDidFinishLoading: into checkEmailAddress: and you're good.
frenetisch applaudierend
A: 

You have two choices:

  • Use +[NSURLConnection sendSynchronousRequest:returningResponse:error:]

  • Schedule the connection in a custom runloop mode, and run the loop in that mode until the data arrives or you have need to cancel the connection

Mike Abdullah
A: 

It all depends on the level of asynchronism you need:

  • If it's OK to stay blocked during the whole request you may want to use

      +[NSURLConnection sendSynchronousRequest:returningResponse:error:]
    

    But, as suggested by Wade, be careful to add a timeout to your NSURLRequest, otherwise the connection might blocks and your application will hang.

  • If not, you can simply use the NSNotificationCenter. But you must be careful with race conditions over your data, specially if you are handling multiple requests

emathias