views:

1460

answers:

1

Hi Guys,

Could you please help me? I have reviewed similar stackoverflow questions/answers to this but I am still stumped.

I'm a beginner and I'm really struggling with this. With the iPhone, I can download XML from a URL but I cannot store the result string in a NSString variable and see it from the calling function.

I have the following declarations in a custom made class:

@interface comms : NSObject {
    NSString *currURL, *receivedString;
    NSMutableData *receivedData;
    NSURLConnection *conn;
}

@property (copy, readwrite) NSString *currURL;
@property (copy, readonly) NSString *receivedString;
@property (nonatomic, assign) NSMutableData *receivedData;
@property (nonatomic, assign) NSURLConnection *conn;

-(void) getContentURL;

I have the following method in the comms class:

-(void) getContentURL
{
    NSLog( @"Begin getContentURL." );

    // Request data related.
    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] 
                                    autorelease];
    [request setURL:[NSURL URLWithString: currURL]];

    // Content-Type related.
    [request setValue:@"application/x-www-form-urlencoded" 
   forHTTPHeaderField:@"Content-Type"];

    // Create Connection.
    conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];

    if (conn) {
        // The connection was established.
        receivedData = [[NSMutableData data] retain];
        NSLog( @"Data will be received from URL: %@", request.URL );
    }
    else
    {
        // The download could not be made.
        NSLog( @"Data could not be received from: %@", request.URL );
    }

    // PROBLEM - receivedString is NULL here.
    NSLog( @"From getContentURL: %@", receivedString );
}

I have created the required delegates in the comms class as per the following:

-(void)connection:(NSURLConnection *)connection didReceiveResponse:
    (NSURLResponse *)response
{
    // Discard all previously received data.
    [receivedData setLength:0];
}

-(void)connection:(NSURLConnection *)connection didReceiveData:
(NSData *)data
{
    // Append the new data to the receivedData.
[receivedData appendData:data];     
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // Connection succeeded in downloading the request.
    NSLog( @"Succeeded! Received %d bytes of data", [receivedData length] );

    // Convert received data into string.
    receivedString = [[NSString alloc] initWithData:receivedData 
        encoding:NSASCIIStringEncoding];
    NSLog( @"From connectionDidFinishLoading: %@", receivedString );

    // release the connection, and the data object
    [conn release];
    [receivedData release];
}

I can successfully output the receivedString string using NSLog in the connectionDidFinishLoading delegate.

    // Convert received data into string.
    receivedString = [[NSString alloc] initWithData:receivedData 
        encoding:NSASCIIStringEncoding];
    NSLog( @"From connectionDidFinishLoading: %@", receivedString );

However, when I output the receivedString string in the getContentURL it's null (and hence is also null from the ViewController.m class which I call the comms class from).

    // PROBLEM - receivedString is NULL here.
    NSLog( @"From getContentURL: %@", receivedString );

Any ideas on how I can see the value of receivedString in getContentURL and from the ViewController.m class?

Thanks in advance,

George Byron

+1  A: 

NSURLConnection is an asynchronous API. When you start the request, the object will spawn a new thread, and only update your main one via the callback/delegate methods. Your current method will return most likely before the request is finished, and so the string of the result will not have downloaded yet!

If you want to do this synchronously, you will have two options:

  1. Use the built in synchronous download method. Note that as this blocks, it will not allow the user to interact with the UI.
  2. Use the C functions CFRunLoopRun() and CFRunLoopStop() to start the run loop inside your calling function, wait until the download is complete or failed, then return control back to the calling method with CFRunLoopStop().
chpwn
Thanks chpwn, that's great!I have implemented the 'synchronous download' method for now, because it's still a prototype we're building. Next week, I will definitely implement the 'asynchronous loop' method.If I start the run loop in the the getContentURL method, would this actually let me save the downloaded data into the receievedString property?
George Byron
Basically, what it will do, is allow the callbacks to be called without first exiting that function/message.
chpwn