Consuming XML Web Services in iPhone Applications
Consuming XML Web Services in iPhone Applications
Find out the various ways you can call web services and parseresponses from your iPhone applications to give your applications ahuge range of data sources.
by Wei-Meng Lee
ommunicating with the outside world isone of the ways to make your iPhone applications interesting anduseful. This is especially true today where they are so many webservices 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 serviceseasy. Everything must be done by hand and you need to know how toform the relevant XML messages to send to the web services and thenparse the returning XML result. This article will give you a good understanding of how tocommunicate with XML web services from within your iPhoneapplication,and the examples will provide a solid foundation forconsuming other web services in your own projects. Consuming Web Services
Before beginning an Xcode project to consume a web service,examining a real web service to see the different ways you canconsume is worthwhile. My favorite example is to use an ASMX XMLweb service created using .NET. For discussion purposes,here's alook at a web service called The As an example,the
The important parts are the sections following the Test sectionof the page. They detail the various ways in which you can consumethe web service. In the .NET world,accessing the web service is apretty straightforward affair—Visual Studio provides a built-intool that automatically creates a web proxy service object for theweb service when you download the WSDL document. For iPhonedevelopment,you need to get your hands dirty,so it's far moreimportant to understand the underlying mechanics of consuming webservices. Using SOAP 1.1One way to consume this web service is to use SOAP (SimpleObject Access Protocol). When using SOAP,you use theHTTPPOST POST /iptocountry.asmx HTTP/1.1 Host: www.ecubicle.net Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://www.ecubicle.net/webservices/FindCountryAsXml" <?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> <FindCountryAsXml xmlns="http://www.ecubicle.net/webservices/"> <V4IPAddress>string</V4IPAddress> </FindCountryAsXml> </soap:Body> </soap:Envelope> The parts of the code in bold are the placeholders where youneed to substitute the actual values. A couple of important thingsto note from the above:
The web service will return a response formatted as follows: HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?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> <FindCountryAsXmlResponse For a real request,the result (the country) will be enclosedwithin the block of XML code (the bold placeholder in the precedingexample). You would need to extract it from the XML result.
Using SOAP 1.2Using SOAP 1.2 is very similar to using SOAP 1.1. Here's asample request: POST /iptocountry.asmx HTTP/1.1 Host: www.ecubicle.net Content-Type: application/soap+xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"> <soap12:Body> <FindCountryAsXml xmlns="http://www.ecubicle.net/webservices/"> <V4IPAddress>string</V4IPAddress> </FindCountryAsXml> </soap12:Body> </soap12:Envelope> The SOAP response for SOAP 1.2 would be: HTTP/1.1 200 OK Content-Type: application/soap+xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"> <soap12:Body> <FindCountryAsXmlResponse Using HTTP
|
Figure 3.Building a Web Service Client: |
Back in Xcode,editthe
#import <UIKit/UIKit.h> @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 toperform the following actions:
- Control-click on the File’s Owner item and drag it over theText Field view. Select
ipAddress. - Control-click on the File’s Owner item and drag it over theActivity Indicator view.Select
activityIndicator. - Control-click the Rounded Rect Button view and drag it over theFile’s Owner item. Selectthe
buttonClicked: action.
Figure 4.Completed Connections:The figure shows the outletconnections and actions for the File’s Owner item. |
If you right-click on the File’s Owner item now,you should seethe connections as shown in
Inthe
import "WebServicesViewController.h" @implementation WebServicesViewControlle r @synthesize ipAddress; @synthesize activityIndicator;
Next,definethe
Let’s spend some time examining what you just did by adding thecode in
NSString *soapMsg = [NSString stringWithFormat: @"<?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>" "<FindCountryAsXml xmlns="http://www.ecubicle.net/webservices/">" "<V4IPAddress>%@</V4IPAddress>" "</FindCountryAsXml>" "</soap:Body>" "</soap:Envelope>",ipAddress.text ];
Next,you create a URL load request object using instances ofthe 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,suchas
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 getthe Activity Indicator view to start animating,thus providing avisual feedback to the user that the application is waiting for aresponse from the web service:
[activityIndicator startAnimating];
To establish the connection with the web service,you use theNSURLConnection class together with the request object justcreated:
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self]; if (conn) { webData = [[NSMutableData data] retain]; }
The NSURLConnection object will now send the request to the webservice,and will asynchronously call various methods (which youwill define next) as it receives responses from the web service.The
When data starts streaming in from the web service,the
-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) response { [webData setLength: 0]; }
Note that the preceding code initializes the lengthof
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data { [webData appendData:data]; }
If there is an error during the transmission,the
-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error { [webData release]; [connection release]; }
When the connection has finished and succeeded in downloadingthe response,the
-(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 receivedfrom the web service to the Debugger Console window and then stopthe Activity Indicator view from animating.
Finally,release all the properties and objects inthe
- (void)dealloc { [ipAddress release]; [activityIndicator release]; [xmlParser release]; [soapResults release]; [super dealloc]; }
That’s it! Press Command-R to test the application on the iPhoneSimulator. Enter the IPaddress
<?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> <FindCountryAsXml xmlns="http://www.ecubicle.net/webservices/"> <V4IPAddress>34.5.6.7</V4IPAddress> </FindCountryAsXml> </soap:Body> </soap:Envelope>
The web service SOAP response contains "United States."
<?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> <FindCountryAsXmlResponsexmlns="http://www.ecubicle.net/webservices/"> <FindCountryAsXmlResult> <IPCountryService xmlns=""> <Country>United States</Country> </IPCountryService> </FindCountryAsXmlResult> </FindCountryAsXmlResponse > </soap:Body> </soap:Envelope>
The response from the web service indicates that you havemanaged to communicate with the web service. The problem now is howto parse the XML to extract the relevant result that you want. Inthis case,the result you want is encapsulated inthe
Using HTTP POST with Web Services
To talk to a web service using HTTP POST,you just need tomodifythe
- (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 simplypost a single string to the web service,suchas
<?xml version="1.0" encoding="utf-8"?> <IPCountryService> <Country>United States</Country> </IPCountryService>
Using HTTPGETwithWeb Services
Using HTTP
- (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 theHTTP
<?xml version="1.0" encoding="utf-8"?> <IPCountryService> <Country>United States</Country> </IPCountryService>
Parsing XML Responses
At this point,you're familiar with the various ways to call aweb service,now you need to learn how to parse the XML response.The iPhone SDK includes an NSXMLParser object that you can use toparse an XML file. The NSXMLParser class is an implementation ofthe Simple API for XML (SAX),which parses an XML document (orstring) serially.
An NSXMLParser object reads an XML document and scans it frombeginning to end. As it encounters the various items in thedocument (such as elements,attributes,comments,and so on),itnotifies 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 followingstatements tothe
#import <UIKit/UIKit.h> @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
Inthe
-(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 setShouldResolveExternal Entities:YES]; [xmlParser parse]; [connection release]; [webData release]; }
You create an instance of the NSXMLParser class and theninitialize it with the response returned by the web service. As theparser encounters the various items in the XML document,it willfire off several methods,which you need to define next.
The first method to implementis
//---when the start of an element is found--- -(void) parser:(NSXMLParser *) parser didStartElement:(NSString *) elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *) qName attributes:(NSDictionary *) attributeDict { if( [elementName isEqualToString:@"Country"]) { if (!soapResults) { soapResults = [[NSMutableString alloc] init]; } elementFound = YES; } }
The preceding code checks to see whether the current tagis
The next method to implementis
-(void)parser:(NSXMLParser *) parser foundCharacters:(NSString *)string { if (elementFound) { [soapResults appendString: string]; } }
Here,if the last tag foundwas
Finally,when the parser encounters the end of an element,itfiresthe
//---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: |
message:soapResults delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; [soapResults setString:@""]; elementFound = FALSE; } }
Here,you simply look for theclosing
Finally! You can now test the application on the iPhoneSimulator by pressing Command-R. Enter an IP address and click theFind Country button.
At this point,you've seen a working iPhone application thatillustrates the various ways you can consume a web service in youriPhone applications: SOAP,HTTP
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!