Consuming XML Web Services in iPhone Applications

Communicating with the outside world is one of the ways to make your iPhone applications interesting and useful. This is especially true today where they are so many web services that provide so much useful functionality. However, consuming web services in iPhone is not for the faint-of-heart. Unlike other development tools (such as Microsoft Visual Studio), Xcode does not have built-in tools that make consuming web services easy. Everything must be done by hand and you need to know how to form the relevant XML messages to send to the web services and then parse the returning XML result.

This article will give you a good understanding of how to communicate with XML web services from within your iPhone application, and the examples will provide a solid foundation for consuming other web services in your own projects.

Consuming Web Services

 
Figure 1. IPToCountry Web Service: This web service exposes two web methods.

Before beginning an Xcode project to consume a web service, examining a real web service to see the different ways you can consume is worthwhile. My favorite example is to use an ASMX XML web service created using .NET. For discussion purposes, here’s a look at a web service called IPToCountry, which lets you supply an IP address and returns the country to which the IP address belongs.

The IPToCountry web service is located at http://www.ecubicle.net/iptocountry.asmx. If you use Safari to load this URL, you’ll see that it exposes two web methods as shown in Figure 1.

As an example, the FindCountryAsXML returns the result (the country) as an XML string. Clicking the FindCountryAsXML link reveals the page shown in Figure 2.

 
Figure 2. FindCountryAsXml Service: Testing the web service through a browser interface shows the XML packets exchanged between the client and the server.

The important parts are the sections following the Test section of the page. They detail the various ways in which you can consume the web service. In the .NET world, accessing the web service is a pretty straightforward affair—Visual Studio provides a built-in tool that automatically creates a web proxy service object for the web service when you download the WSDL document. For iPhone development, you need to get your hands dirty, so it’s far more important to understand the underlying mechanics of consuming web services.

Using SOAP 1.1

One way to consume this web service is to use SOAP (Simple Object Access Protocol). When using SOAP, you use the HTTP POST method to send a request to the web service, as shown in the following example:

POST /iptocountry.asmx HTTP/1.1Host: www.ecubicle.netContent-Type: text/xml; charset=utf-8Content-Length: lengthSOAPAction: "http://www.ecubicle.net/webservices/FindCountryAsXml"            string      

The parts of the code in bold are the placeholders where you need to substitute the actual values. A couple of important things to note from the above:

  • The URL for the web service is http://www.ecubicle.net/iptocountry.asmx.
  • The URL for the SOAPAction attribute is http://www.ecubicle.net/webservices/FindCountryAsXml.
  • The Content-Type for the request is text/xml; charset=utf-8.
  • The HTTP method is POST.
  • The SOAP request is shown below:
                string      
  • The Content-Length of the SOAP request is the total number of characters in the complete SOAP request.

The web service will return a response formatted as follows:

HTTP/1.1 200 OKContent-Type: text/xml; charset=utf-8Content-Length: length            xml result      

For a real request, the result (the country) will be enclosed within the block of XML code (the bold placeholder in the preceding example). You would need to extract it from the XML result.

Using SOAP 1.2

Using SOAP 1.2 is very similar to using SOAP 1.1. Here’s a sample request:

POST /iptocountry.asmx HTTP/1.1Host: www.ecubicle.netContent-Type: application/soap+xml; charset=utf-8Content-Length: length            string      

The SOAP response for SOAP 1.2 would be:

HTTP/1.1 200 OKContent-Type: application/soap+xml; charset=utf-8Content-Length: length            xml result      

Using HTTP GET

For many web services, if you do not want to use SOAP, you can often use the simpler HTTP GET method. Here’s the format to send a GET request:

GET /iptocountry.asmx/FindCountryAsXml?V4IPAddress=string HTTP/1.1Host: www.ecubicle.netHTTP/1.1 200 OKContent-Type: text/xml; charset=utf-8Content-Length: length

Take note of the following:

  • The URL for the web service is http://www.ecubicle.net/iptocountry.asmx/FindCountryAsXml?V4IPAddress=string.
  • The Content-Type for the request is text/xml; charset=utf-8.
  • The Content-Length for the request is 0, because you don’t need to send anything separately (everything gets sent through the query string).
  • The HTTP method is GET.

The web service will return its result in the following format:

xml result

Using HTTP POST

In addition to using HTTP GET, you can also use HTTP POST. Here’s the format for sending the request:

POST /iptocountry.asmx/FindCountryAsXml HTTP/1.1Host: www.ecubicle.netContent-Type: application/x-www-form-urlencodedContent-Length: lengthV4IPAddress=string

Take note of the following:

  • The URL for the web service is http://www.ecubicle.net/iptocountry.asmx/FindCountryAsXml.
  • The Content-Type for the request is application/x-www-form-urlencoded.
  • The Content-Length for the request is the length of V4IPAddress=string.
  • The HTTP method is POST.

The web service returns the following result:

HTTP/1.1 200 OKContent-Type: text/xml; charset=utf-8Content-Length: lengthxml result

Consuming Web Services in iPhone Applications

With all the background explained, you can now tackle the exciting task of consuming a web service in your iPhone application! First, you need to learn to communicate with the web service using SOAP.

Using Xcode, create a View-based Application project and name it WebServices. Double-click the WebServicesViewController.xib file to open it in Interface Builder. Double-click on the View item and populate it with the views listed below and shown in Figure 3:

  • Label
  • Text Field
  • Round Rect Button
  • Activity Indicator
 
Figure 3. Building a Web Service Client: Populate the View window with the various views shown here.

Back in Xcode, edit the WebServicesViewController.h file and add the statements shown in bold in the following code:

#import @interface WebServicesViewController : UIViewController {    //---outlets---    IBOutlet UITextField *ipAddress;    IBOutlet UIActivityIndicatorView *activityIndicator;        //---web service access---    NSMutableData *webData;    NSMutableString *soapResults;    NSURLConnection *conn;}@property (nonatomic, retain) UITextField *ipAddress;@property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;- (IBAction)buttonClicked:(id)sender;@end

Save the file and return to Interface Builder, where you need to perform the following actions:

  • Control-click on the File’s Owner item and drag it over the Text Field view. Select ipAddress.
  • Control-click on the File’s Owner item and drag it over the Activity Indicator view. Select activityIndicator.
  • Control-click the Rounded Rect Button view and drag it over the File’s Owner item. Select the buttonClicked: action.
 
Figure 4. Completed Connections: The figure shows the outlet connections and actions for the File’s Owner item.

If you right-click on the File’s Owner item now, you should see the connections as shown in Figure 4.

In the WebServicesViewController.m file, first create the setters and getters for the Text Field and Activity Indicator properties:

import "WebServicesViewController.h"@implementation WebServicesViewController@synthesize ipAddress;@synthesize activityIndicator;

Next, define the buttonClicked: method (see Listing 1), which will formulate the SOAP request packet and send it over to the web service.

Let’s spend some time examining what you just did by adding the code in Listing 1. First, you create the SOAP request packet:

    NSString *soapMsg =         [NSString stringWithFormat:            @""             ""             ""             ""             "%@"             ""             ""             "",  ipAddress.text        ];

Next, you create a URL load request object using instances of the NSMutableURLRequest and NSURL objects:

    NSURL *url = [NSURL URLWithString:       @"http://www.ecubicle.net/iptocountry.asmx"];    NSMutableURLRequest *req =       [NSMutableURLRequest requestWithURL:url];

You then populate the request object with the various headers, such as Content-Type, SOAPAction, and Content-Length. You also set the HTTP method and HTTP body:

    NSString *msgLength =       [NSString stringWithFormat:@"%d", [soapMsg length]];    [req addValue:@"text/xml; charset=utf-8"        forHTTPHeaderField:@"Content-Type"];    [req addValue:@"http://www.ecubicle.net/webservices/FindCountryAsXml"       forHTTPHeaderField:@"SOAPAction"];    [req addValue:msgLength       forHTTPHeaderField:@"Content-Length"];    [req setHTTPMethod:@"POST"];    [req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];

Before you make the actual request to the web service, you get the Activity Indicator view to start animating, thus providing a visual feedback to the user that the application is waiting for a response from the web service:

[activityIndicator startAnimating];

To establish the connection with the web service, you use the NSURLConnection class together with the request object just created:

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

The NSURLConnection object will now send the request to the web service, and will asynchronously call various methods (which you will define next) as it receives responses from the web service. The data method of the NSMutableData class returns an empty data object that represents a wrapper for byte buffers, which you will use to receive incoming data from the web service.

When data starts streaming in from the web service, the connection:didReceiveResponse: method will be called, which you need to implement here:

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

Note that the preceding code initializes the length of webData to 0. As the data progressively comes in from the web service, the connection:didReceiveData: method will be called repeatedly. You use the method to append the data received to the webData object:

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

If there is an error during the transmission, the connection:didFailWithError: method will be called:

-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error {[webData release];    [connection release];}

When the connection has finished and succeeded in downloading the response, the connectionDidFinishLoading: method will be called:

-(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);    [theXML release];        [activityIndicator stopAnimating];            [connection release];    [webData release];}

For this example, you simply print the XML response received from the web service to the Debugger Console window and then stop the Activity Indicator view from animating.

Finally, release all the properties and objects in the dealloc method:

- (void)dealloc {        [ipAddress release];    [activityIndicator release];    [xmlParser release];    [soapResults release];    [super dealloc];}

That’s it! Press Command-R to test the application on the iPhone Simulator. Enter the IP address 34.5.6.7 in the Text Field, and then tap the Find Country button. In Xcode, press Shift-Command-R to open the Debugger Console window. You’ll see that the following request was sent to the web service:

                  34.5.6.7         

The web service SOAP response contains “United States.”

                                             United States                              

The response from the web service indicates that you have managed to communicate with the web service. The problem now is how to parse the XML to extract the relevant result that you want. In this case, the result you want is encapsulated in the element. The last section of this article shows how to parse the XML response, but for now, let’s see what you should do if you want to use the HTTP GET and HTTP POST methods, as opposed to using the SOAP method to talk to the web service.

Using HTTP POST with Web Services

To talk to a web service using HTTP POST, you just need to modify the buttonClicked: method, like this:

- (IBAction)buttonClicked:(id)sender {    NSString *postString =         [NSString stringWithFormat:@"V4IPAddress=%@",         ipAddress.text];    NSLog(postString);        NSURL *url = [NSURL URLWithString:         @"http:// www.ecubicle.net/iptocountry.asmx/FindCountryAsXml"];    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]];        [activityIndicator startAnimating];        conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];    if (conn) {        webData = [[NSMutableData data] retain];    }    }

Basically, you do away with forming the SOAP message and simply post a single string to the web service, such as V4IPAddress=34.5.6.7. When you run the modified application and watch the Debugger Console, you should see a response like this:

    United States

Using HTTP GET with Web Services

Using HTTP GET to talk to a web service is even simpler, because you pass all the request information via the query string. The following shows the modified buttonClicked: method to access the web service using the HTTP GET method:

- (IBAction)buttonClicked:(id)sender {NSString *queryString =        [NSString stringWithFormat:          @"http://www.ecubicle.net/iptocountry.asmx/" +           "FindCountryAsXml?V4IPAddress=%@",           ipAddress.text];NSURL *url = [NSURL URLWithString:queryString];NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];[req addValue:@"text/xml; charset=utf-8"          forHTTPHeaderField:@"Content-Type"];[req addValue:0 forHTTPHeaderField:@"Content-Length"];[req setHTTPMethod:@"GET"];[activityIndicator startAnimating];conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];if (conn) {webData = [[NSMutableData data] retain];}}

Like the HTTP POST method, the response in the Debugger Console when you run the modified application should look like this:

    United States

Parsing XML Responses

At this point, you’re familiar with the various ways to call a web service, now you need to learn how to parse the XML response. The iPhone SDK includes an NSXMLParser object that you can use to parse an XML file. The NSXMLParser class is an implementation of the Simple API for XML (SAX), which parses an XML document (or string) serially.

An NSXMLParser object reads an XML document and scans it from beginning to end. As it encounters the various items in the document (such as elements, attributes, comments, and so on), it notifies its delegates so that you can take appropriate action, such as extracting the value of an element.

To parse the response from the web service, add the following statements to the WebServicesViewController.h file:

#import @interface WebServicesViewController : UIViewController {    //---outlets---    IBOutlet UITextField *ipAddress;    IBOutlet UIActivityIndicatorView *activityIndicator;        //---web service access---    NSMutableData *webData;    NSMutableString *soapResults;    NSURLConnection *conn;    //---xml parsing---    NSXMLParser *xmlParser;    BOOL *elementFound;        }@property (nonatomic, retain) UITextField *ipAddress;@property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;- (IBAction)buttonClicked:(id)sender;@end

In the WebServicesViewController.m file, add the statements shown below in bold to the connectionDidFinishLoading: method:

-(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);[theXML release];        [activityIndicator stopAnimating];        if (xmlParser)    {        [xmlParser release];    }        xmlParser = [[NSXMLParser alloc] initWithData: webData];    [xmlParser setDelegate: self];    [xmlParser setShouldResolveExternalEntities:YES];    [xmlParser parse];        [connection release];[webData release];}

You create an instance of the NSXMLParser class and then initialize it with the response returned by the web service. As the parser encounters the various items in the XML document, it will fire off several methods, which you need to define next.

The first method to implement is parser:didStartElement:namespaceURI:qualifiedName:attributes:, which is fired when the start tag of an element is found:

//---when the start of an element is found----(void) parser:(NSXMLParser *) parser    didStartElement:(NSString *) elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *) qNameattributes:(NSDictionary *) attributeDict {        if( [elementName isEqualToString:@"Country"])    {        if (!soapResults)        {            soapResults = [[NSMutableString alloc] init];        }        elementFound = YES;    }}

The preceding code checks to see whether the current tag is . If it is, you set the Boolean variable elementFound to YES.

The next method to implement is parser:foundCharacters:, which gets fired when the parser finds the text of an element:

-(void)parser:(NSXMLParser *) parser foundCharacters:(NSString *)string{    if (elementFound)    {        [soapResults appendString: string];    }}

Here, if the last tag found was , you want to extract the value of the Country element into soapResults.

Finally, when the parser encounters the end of an element, it fires the parser:didEndElement:namespaceURI:qualifiedName: method:

//---when the end of element is found----(void)parser:(NSXMLParser *)parser     didEndElement:(NSString *)elementName     namespaceURI:(NSString *)namespaceURI     qualifiedName:(NSString *)qName{    if ([elementName isEqualToString:@"Country"])    {        //---displays the country---        NSLog(soapResults);                UIAlertView *alert = [[UIAlertView alloc]           initWithTitle:@"Country found!" 
 
Figure 5. Country Found: The completed example application displays the country name after parsing the XML result.
                                                                  message:soapResults         delegate:self          cancelButtonTitle:@"OK"         otherButtonTitles:nil];        [alert show];        [alert release];        [soapResults setString:@""];        elementFound = FALSE;     }}

Here, you simply look for the closing tag; when you find it you know that the value of the Country element has been correctly extracted. You then print out the value using a UIAlertView object.

Finally! You can now test the application on the iPhone Simulator by pressing Command-R. Enter an IP address and click the Find Country button. Figure 5 shows you the result!

At this point, you’ve seen a working iPhone application that illustrates the various ways you can consume a web service in your iPhone applications: SOAP, HTTP GET, and HTTP POST. The example both calls a web service and shows how to extract data from the XML-formatted response. Using web services (and being able to parse XML) in your iPhone applications can open up an entire world full of data to your applications.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

The Latest

your company's audio

4 Areas of Your Company Where Your Audio Really Matters

Your company probably relies on audio more than you realize. Whether you’re creating a spoken text message to a colleague or giving a speech, you want your audio to shine. Otherwise, you could cause avoidable friction points and potentially hurt your brand reputation. For example, let’s say you create a

chrome os developer mode

How to Turn on Chrome OS Developer Mode

Google’s Chrome OS is a popular operating system that is widely used on Chromebooks and other devices. While it is designed to be simple and user-friendly, there are times when users may want to access additional features and functionality. One way to do this is by turning on Chrome OS

homes in the real estate industry

Exploring the Latest Tech Trends Impacting the Real Estate Industry

The real estate industry is changing thanks to the newest technological advancements. These new developments — from blockchain and AI to virtual reality and 3D printing — are poised to change how we buy and sell homes. Real estate brokers, buyers, sellers, wholesale real estate professionals, fix and flippers, and beyond may