OK, so I'm working on only my 2nd iPhone app ever and really needed the ability to use WebServices. Unfortunately I did not see any helper classes for SOAP, which my web services will be using, so I had to do some research and found out I would have to do it, what I would call, manually.
I come from a C# programming environment and really .NET spoils you. They make everything soooo easy. So easy maybe it's bad. Anyways, this statement is also a warning. I'm very new to Objective C and Mac programming so I may make some mistakes. Please don't take what you read as how it NEEDS to be done because maybe it's not. It's what I found and it works. I also may have some memory leaks. I'm getting better but I'm use to a garbage collection type system. If you see an error, please point it out so I can learn from mistakes!
So lets get started:
First, lets browse to a Webservice I created specifically for this tutorial:
You will notice a “Hello” method that is clickable. Go ahead and click on hello. You are now seeing the SOAP message POSTS and RESULTS for SOAP 1.1 and 1.2. I'm doing a request using SOAP 1.1. Keep this page handy as we will be directly using it to create our message to POST to the above web address.
Second,
Here some coding YOU need to do. Create a View base application and add a Label, Textbox, and Button. Make sure you make all your connections and make sure to create a click method for your button. In your Controller header file add:
NSMutableData *webData;
NSMutableString *
NSXMLParser *xmlParser;
BOOL *recordResults;
@property( nonatomic, retain) NSMutableData *webData;
@property( nonatomic, retain) NSMutableString *webResults;
@property( nonatomic, retain) NSXMLParser *xmlParser;
@property( assign ) BOOL *recordResults;
Now lets do some coding to our ClickButton Method:
**Don't forget this: @synthesize name, greeting, webData, webResults, xmlParser, recordResults;
//Button Click Method
- (IBAction) buttonClick: (id) sender
{
recordResults = FALSE;
/*Setup our soap xml message. Remeber to use escape char for quotes. So when you need a quotation
//mark in a string do: \”
//list of char needing to be escaped:
//Read basics of SOAP:
I'm also adding NewLines \n, I doubt this is necessary but easier to understand and look ad when logging.
First we will be creating the SOAP message. This is the XML code that is located in the POST message for SOAP 1.1 in the web page we recently browsed to.
We will change <name>string</string> to <name>%@</name>. This basically says it is requesting an input of a string in that element.
We will also use NSString stringWithFormat, and pass in the var name.text which is the name of my textbox.
*/
NSString *soapMessage = [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"
"<Hello xmlns=\"http://viium.com/\">\n"
"<name>%@</name>\n"
"</Hello>\n"
"</soap:Body>\n"
"</soap:Envelope>\n", name.text
];
//Log it so we can see it in debug mode
NSLog(soapMessage);
//Web service I setup to do this test application.
//Plug for my website which isn't much:
NSURL *url = [NSURL URLWithString:@"http://viium.com/WebService/HelloWorld.asmx"];
//Setup the web request with the url we created
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
//need length of the soap xml for the header in the html file
NSString *msgLength = [NSString stringWithFormat:@"%d", [soapMessage length]];
/*
This is where we add our header info for an HTML message. This is required so that the service knows you are sending a SOAP message.
POST /WebService/HelloWorld.asmx HTTP/1.1
Host: viium.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: “http://viium.com/Hello”
add our htm headers which is a must for a WebService*/
[theRequest addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[theRequest addValue:@"http://viium.com/Hello" forHTTPHeaderField:@"SOAPAction"];
[theRequest addValue:msgLength forHTTPHeaderField:@"Content-Length"];
//We are doing a HTML POST
[theRequest setHTTPMethod:@"POST"];
//set the HTML Body, which is the XML Soap message we created above. Needs to be UTF8 encoding
//Needs to be of type Data
[theRequest setHTTPBody: [soapMessage dataUsingEncoding: NSUTF8StringEncoding]];
// Our Message is now fully created and can now be sent to the WebService. Create the connection with the request
// and start loading the data, also set delegate to self
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection)
{
// Create the NSMutableData that will hold
webData=[[NSMutableData data] retain];
}
else
{
// inform the user that the download could not be made
NSLog(@”ERROR”);
}
//Make keyboard go away!!!
[name resignFirstResponder];
}
/* Once the connection is made and no errors occur, it will start hitting the NSMutableURLRequest Delegates. We will now set this up */
(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// this method is called when the server has determined that it
// has enough information to create the NSURLResponse
//clear any data that may be around.
[webData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// append the new data to the webData
// webData is declared in the header file
[webData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// release the connection, and the data object if error occurs
[connection release];
[webData release];
// inform the user
NSLog(@”Connection failed! Error - %@ %@”,
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//We are done receiving the results from the Web Service.
//Manipulate data here and release Data nd Connection
NSLog(@”Succeeded! Received %d bytes of data\n”,[webData length]);
NSString *theXml = [[NSString alloc]initWithBytes:[webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
NSLog(theXml);
[theXml release];
//release if xml is already init
if( xmlParser )
{
[xmlParser release];
}
//Parse received xml from web service
//xmlParse declared in the header
xmlParser= [[NSXMLParser alloc] initWithData: webData];
[xmlParser setDelegate:self];
[xmlParser setShouldResolveExternalEntities:YES];
[xmlParser parse];
// release the connection, and the data object
[connection release];
[webData release];
}
/*Once the Request is done getting a response the data, if no error occurs, is passed into the XMLParser. We allocate it with the webData and set the delegate to
self. Now we need to setup the XML Parser delegates methods which to the actual parsing of the xml. In order to parse it correctly we have to go back to the web service
web page and look at what the response xml will look like:
<?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>
<HelloResponse xmlns=”http://viium.com/”>
<HelloResult>string</HelloResult>
</HelloResponse>
</soap:Body>
</soap:Envelope>
From this we can tell what element we are looking for, <HelloResult> and the type of data it returns: string
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
//We check the results XML for HelloResult element. Once we find it, we set recordResults to TRUE
//We do this so foundCharacters method know is needs to start appending the data.
if ( [elementName isEqualToString:@"HelloResult"])
{
if (!webResults)
{
//webResults is declared in header file
webResults = [[NSMutableString alloc] init];
}
recordResults = TRUE;
return;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
//if at the HelloResult element, record the data to NSMutableString
if( recordResults )
{
[webResults appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
//We set our label, release our objects, and set record Results back to False since we hit the
//End Element of Hello Results
//Release xmlParser in the dealloc method
//greeting is the label we setup.
if ( [elementName isEqualToString:@"HelloResult"] )
{
greeting.text = webResults;
[webResults release];
webResults = nil;
recordResults = FALSE;
return;
}
}
//release our xmlParser
- (void)dealloc
{
[xmlParser release];
[super dealloc];
}
Download run-able source here: http://viium.com/WebService/HelloWorld.zip