Friday, April 22, 2011

Working with Web services in iPhone

Before you start reading in detail i would request you read this post of mine so that you will come to know in detail regarding the usage of a web service.

In this post we will learn how to use a webservice by sending a SOAP request to the server and reading the output which will be in xml, so in this post I have used the webservice that is provided by w3schools. 

The webservice provided by w3school is used to convert the Celsius temperature into Fahrenheit, the output returned by any webservice can be in json or xml and in this demo the webservice returns an xml structure that will be parsed by using an xml parser

Design Phase:  For the design phase we will make it simple not too much flashy, so here’s a view at the final output.



Design Explanation: The first text field will accept the user input for the temperature that he wants to convert into Fahrenheit and then will display the output in the other text field (Fahrenheit text field).

Step 1: Open Xcode and select the windows based application from the ios template, give this project an appropriate name and then add UIViewController subclass file to this project with the name myView, so now you must be having two new files added into your project that’s myView.h and myView.m, now you can either use interface builder or you can manually code for this view. Get the design ready in the first step just like the above pic and then go to step 2.

Step 2: For making a call to the webservice running in some server you need to call that service via SOAP request, so inorder to know whats the format of your soap request you need to go to the url where your web method is and their you will get the list of appropriate methods that you may want to use so select the appropriate method, below given are the snaps that will help you out for the web method that I am using 

Webservice main page



The SOAP request and response page



Step 3: To execute a web method you need to first need to create the soap body format and pass some of the parameters that are required by that method, and that you can do with the help of the below line


NSString *soapFormat = [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/\">\n"
"<soap:Body>\n"
"<CelsiusToFahrenheit xmlns=\"http://tempuri.org/\">\n"
"<Celsius>%@</Celsius>\n"
"</CelsiusToFahrenheit>\n"
"</soap:Body>\n"
"</soap:Envelope>\n",txt1.text];

Next you need to specify the location of the web method and that you can do with the help of the NSURL class


NSURL *locationOfWebService = [NSURL URLWithString:@"http://www.w3schools.com/webservices/tempconvert.asmx"];



Now you need to create a soap header  by using the class NSMutableURLRequest , remember soap body and soap header are two different things, 


NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc]initWithURL:locationOfWebService];
NSString *msgLength = [NSString stringWithFormat:@"%d",[soapFormat length]];
[theRequest addValue:@"text/xml" forHTTPHeaderField:@"Content-Type"];
[theRequest addValue:@"http://tempuri.org/CelsiusToFahrenheit" forHTTPHeaderField:@"SOAPAction"];
[theRequest addValue:msgLength forHTTPHeaderField:@"Content-Length"];
[theRequest setHTTPMethod:@"POST"];
//the below encoding is used to send data over the net
[theRequest setHTTPBody:[soapFormat dataUsingEncoding:NSUTF8StringEncoding]];


and finally you check whether the connection is established or not with the help of NSURLConnection as it provides support for loading the URL Requests


NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:theRequest delegate:self];
if (connect) 
{

webData = [[NSMutableData alloc]init];
}
else {
NSLog(@"No Connection established");

}


In the above code I have took a member of NSMutableData and initialized it, this variable will have the entire xml structure that will be required by us to parse, 

The entire code looks like this


-(IBAction)invokeService
{
if ([txt1.text length]==0) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"WebService" message:@"Supply Data in text field" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok",nil];
[alert show];
[alert release];
}
else {
[txt1 resignFirstResponder];





NSString *soapFormat = [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/\">\n"
"<soap:Body>\n"
"<CelsiusToFahrenheit xmlns=\"http://tempuri.org/\">\n"
"<Celsius>%@</Celsius>\n"
"</CelsiusToFahrenheit>\n"
"</soap:Body>\n"
"</soap:Envelope>\n",txt1.text];
NSURL *locationOfWebService = [NSURL URLWithString:@"http://www.w3schools.com/webservices/tempconvert.asmx"];
NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc]initWithURL:locationOfWebService];
NSString *msgLength = [NSString stringWithFormat:@"%d",[soapFormat length]];
[theRequest addValue:@"text/xml" forHTTPHeaderField:@"Content-Type"];
[theRequest addValue:@"http://tempuri.org/CelsiusToFahrenheit" forHTTPHeaderField:@"SOAPAction"];
[theRequest addValue:msgLength forHTTPHeaderField:@"Content-Length"];
[theRequest setHTTPMethod:@"POST"];
//the below encoding is used to send data over the net
[theRequest setHTTPBody:[soapFormat dataUsingEncoding:NSUTF8StringEncoding]];

NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:theRequest delegate:self];
if (connect) {
webData = [[NSMutableData alloc]init];
}
else {
NSLog(@"No Connection established");
}
}
}



Step 4: The NSURLConnection class has delegate method that we must implement so that we can read the xml structure that is returned by the webservice, if you want to see the xml format of the webservice that is returned then it will be provided just below the soap request section.

The delegate methods of NSURLConnection that you will be using are 

didReceiveResponse: In this method you set the mutabledata’s length to zero so that the data present from any previous request is clear

didReceiveData: Append the mutabledata variable with the data received from the webservice

didFailWithError: If the internet connection crashes then write code inside this method to prompt a message to the user for connection failure.

connectionDidFinishLoading: You will be writing code inside this method once the loading of the xml output is done in the mutable data

So heres how the entire code looks


//NSURLConnection delegate method

-(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]);
xmlParser = [[NSXMLParser alloc]initWithData:webData];
[xmlParser setDelegate: self];
[xmlParser parse];
[connection release];
}



Step 5: Now its time to parse the xml for this I haven’t used GData Parser I have used the NSXMLParser to parse the xml, so from step 4 you can see I have allocated and initialized memory space for the parser and it has some delegate method that I have used to get the work done


- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[nodeContent appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:@"CelsiusToFahrenheitResult"]) {
finaldata = nodeContent;
output.text = finaldata;
}
output.text = finaldata;
}


You may use any xml parser of your choice

Now go to the appDelegate.m file and add the view to your window


#import "webserviceDemoAppDelegate.h"
#import "myView.h"

@implementation webserviceDemoAppDelegate

@synthesize window;
#pragma mark -
#pragma mark Application lifecycle

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    
    // Override point for customization after application launch.
    
myView *obj = [[myView alloc]initWithNibName:@"myView" bundle:nil];
[window addSubview:obj.view];
    [self.window makeKeyAndVisible];
    
    return YES;
}


press build and go to run the application, here's a view at the final output





You may download the source code from this link.

If you are god with dot net then you may create your own SOAP based services, just follow the given tutorial below:

- Creating SOAP based services part 1
- Creating SOAP based services part 2

iHope this post has helped you out for dealing with webservice,  any comments good or bad are always welcome until then Happy iCoding and have a great Day. You can also request me a tutorial

Follow iPhone by Radix group on facebook.