2

I am successfully connecting to a .NET web service from iOS and print the response xml document to the console via NSLog. This I am doing via the AFNetworking Library. I am now able to connect to this same web service via NSURLConnection, however, I am having trouble extracting the xml document that is being held inside my NSMutableData object. The AFNetworking library is able to do this automatically which makes things a whole lot easier, but I want to know how to do this using native iOS libraries. Here is the code that I am working with:

//This is the code I am using to make the connection to the web service
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    [request setHTTPBody:[soapBody dataUsingEncoding:NSUTF8StringEncoding]];

    [request setHTTPMethod:@"POST"];

    [request addValue:@"http://tempuri.org/Customers" forHTTPHeaderField:@"SOAPAction"];
    [request addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];

    // Convert your data and set your request's HTTPBody property
    NSString *stringData = @"some data";
    NSData *requestBodyData = [stringData dataUsingEncoding:NSUTF8StringEncoding];
    request.HTTPBody = requestBodyData;

    // Create url connection and fire request
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [conn start];

Here is the code that I have to receive the data from the web service:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {

    _responseData = [[NSMutableData alloc] init];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

    [_responseData appendData:data];
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse*)cachedResponse {
    return nil;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // The request is complete and data has been received
    NSString *strData = [[NSString alloc]initWithData:_responseData encoding:NSUTF8StringEncoding];

    NSLog(@"%@", strData);

}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    // The request has failed for some reason!

}


/*
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

        NSLog(@"%@", [operation responseString]);

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

        NSLog(@"failed");

    }];

    [operation start];
    */

I simply want to print out the entire xml document that I am receiving, and not necessarily parse through the document for now.

Thanks in advance to all who reply.

syedfa
  • 2,801
  • 1
  • 41
  • 74
  • 1
    So what happens with this code? How far do you get? Does it finish loading? What's the log output for `strData`? If it doesn't finish, add a log of the error in the `didFailWithError` method. – rmaddy May 01 '13 at 15:05
  • Sorry about that. There is no output unfortunately to the console when I do NSLog(strData); However, if I do NSLog(_responseData); I get <> appear in my console. I hope this helps. – syedfa May 01 '13 at 15:10
  • That means the response is completing but you are getting no data. Have you run your app through the debugger and set breakpoints on all of the `NSURLConnection` delegate methods? See what is being call and see what is not being called. Do you have control over the server? If so, see what request it's getting and what response it is sending. Compare that to what you get in the iOS app. – rmaddy May 01 '13 at 15:12
  • Minor observation: you don't need to `start` your `NSURLConnection`. You only need to do that if you use `initWithRequest:delegate:startImmediately:` with `NO` for `startImmediately`. Also, how is `_responseData` defined? If `weak`, you would not be retained for you. Also, if this is a backing ivar for a `responseData` property, you generally should [use accessor methods when to set property values](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW5). – Rob May 01 '13 at 15:26
  • @Rob: Thanks for pointing that out. _responseData is defined as being (nonatomic, retain) as a property in the header file. – syedfa May 01 '13 at 15:29
  • @rmaddy: No I haven't unfortunately, and I guess that will be my next step. However, I wanted to mention that when I used the AFNetworking library, it was able to print the resulting xml document with a simple output command as I have shown in my edited code block above. How is it that the AFNetworking code is able to receive data and print it, and with the native code, I am not? – syedfa May 01 '13 at 15:29
  • @syedfa Regarding its being `retain`, thanks for the clarification. It's good practice to use the setter (per that Apple link I shared), but that wouldn't cause the problem. Also, I'd be inclined to use `strong` if this is ARC (and if this isn't ARC, this code will leak; try choosing "Analyze" from the "Product" menu and it should point out issues). – Rob May 01 '13 at 15:31
  • @syedfa Also, are you doing this from a background queue? If so, you might not see your `NSURLConnectionDataDelegate` methods get called (because you'll lose your run loop). Doing delegate-based `NSURLConnection` in background queue is tricky, so if you need to do that, please clarify. Have you put `NSLog` in your `didReceiveData`? Is it getting called at all? Also, a log in `didFailWithError` might be useful, too. – Rob May 01 '13 at 15:35
  • @Rob: It is ARC, so I have made the change from retain to strong as you suggested. The code I am executing is not from a background queue, I am simply running it from a very basic iPhone app I am building for test purposes in XCode (a single view application). As for the other delegate methods, I do get output from the didReceiveResponse delegate method, but no response from the didReceiveData method which corroborates what rmaddy pointed out earlier (i.e. I'm receiving a response, but no data). I guess my confusion is, why was I receiving output when using the other library? – syedfa May 01 '13 at 15:45
  • @syedfa let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/29254/discussion-between-rob-and-syedfa) – Rob May 01 '13 at 16:12

1 Answers1

0

Looking at the help document on your server, it suggested an example request:

<?xml version="1.0" encoding="utf-8"?>
<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/">
  <soap:Body>
    <Customers xmlns="http://tempuri.org/" />
  </soap:Body>
</soap:Envelope>

So, I put that example request in a text file, and initiated the request via NSURLConnection like so:

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

NSString *filename = [[NSBundle mainBundle] pathForResource:@"soap1-1example" ofType:@"xml"];
NSData *requestBodyData = [NSData dataWithContentsOfFile:filename];
request.HTTPBody = requestBodyData;

[request setHTTPMethod:@"POST"];

[request addValue:@"http://tempuri.org/Customers" forHTTPHeaderField:@"SOAPAction"];
[request addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];

[NSURLConnection connectionWithRequest:request delegate:self];

Note, I'm not calling start, because as the documentation says unless you use "initWithRequest:delegate:startImmediately: method and provide NO for the startImmediately parameter," it will start for you automatically. Your original code is, in effect, starting the connection twice, which could be problematic.

Anyway, using my revised code with the above request, it worked fine, using both NSURLConnection as well as with AFNetworking. The response I got (prettified) was:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
    <CustomersResponse xmlns="http://tempuri.org/">
    <CustomersResult>
        <Customer>
            <FirstName>Mohammad</FirstName>
            <LastName>Azam</LastName>
        </Customer>
        <Customer>
            <FirstName>John</FirstName>
            <LastName>Doe</LastName>
        </Customer>
    </CustomersResult>
</CustomersResponse>
</soap:Body>
</soap:Envelope>

If it's still not working for you, perhaps you can share the particulars of the request you're submitting.


By the way, since nothing here necessitates the delegate methods (e.g. you're not streaming, you're not doing any authentication, etc.), in you can eliminate all of the NSURLConnectionDataDelegate methods (didReceiveResponse, didReceiveData, etc.), and just use NSURLConnection class method, sendAsynchronousRequest (in iOS 5 and later):

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

NSString *filename = [[NSBundle mainBundle] pathForResource:@"soap1-1example" ofType:@"xml"];
NSData *requestBodyData = [NSData dataWithContentsOfFile:filename];
NSAssert(requestBodyData, @"requestBodyData is nil!");
request.HTTPBody = requestBodyData;

[request setHTTPMethod:@"POST"];

[request addValue:@"http://tempuri.org/Customers" forHTTPHeaderField:@"SOAPAction"];
[request addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];

if (!self.networkQueue)
{
    self.networkQueue = [[NSOperationQueue alloc] init];
    self.networkQueue.name = @"com.yourdomain.yourapp.networkQueue";
}

[NSURLConnection sendAsynchronousRequest:request
                                   queue:self.networkQueue
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

                           if (!error)
                           {
                               NSString *string = [[NSString alloc] initWithData:data
                                                                        encoding:NSUTF8StringEncoding];

                               NSLog(@"%s: data=%@", __FUNCTION__, string);
                           }
                           else
                           {
                               NSLog(@"%s: error=%@", __FUNCTION__, error);
                           }
                       }];
Rob
  • 415,655
  • 72
  • 787
  • 1,044