Using SOAP web service in an iOS application
I recently needed to connect to SOAP web services written in .NET/PHP from one of my iOS applications when I realized that is there is no built in support for SOAP messages (although iOS5 and above has support for JSON via the NSJSONSerialization class). Obviously I could decide to construct the SOAP XML messages manually and POST them to the web service via NSURLRequest, I soon realized that this approach is not feasible due to the many (50+) web services that my application needs to use.
Fortunately I find wsdl2objc, an Objective-C code-generator for SOAP web services which automatically parses the web service WSDL definition and generates the appropriate classes to send a request and parse the response.
The tool is a simple Mac OS app that asks you for the location to your web service WSDL definition, which can be a URL or a local file, and where to put the generated code:
Supply the required information and press Parse WSDL. The tool should quickly finish and give you the resulting source code files, which look similar to below:
Among the files, MyService.h and MyService.m contain the definition and implementation of your web service based on the WSDL file. The rest of the files are supporting classes to parse the XML messages. If you have more than one WSDL file, run the generator for each WSDL and include all the generated service class files in your project. The supporting classes only need to be included once.
You should have no issues compiling the generated code and make a simple web service call using the sample code given on the wsdl2objc home page. However, depending on the web service, you may run into any of the following problems:
1. The web service does not receive the parameters passed in properly, or, for if the service is written in Java, exception “Cannot find dispatch method for {serviceURL}/{serviceName}” will be thrown on the server.
This is because the namespace attribute, xmlns, is missing from the SOAP body envelope. To fix this, locate the method:
- (NSString *)serializedFormUsingHeaderElements:(NSDictionary *)headerElements bodyElements:(NSDictionary *)bodyElements
Look for the following block of code
if((headerElements != nil) && ([headerElements count] > 0)) {
xmlNodePtr headerNode = xmlNewDocNode(doc, soapEnvelopeNs, (const xmlChar*)”Header”, NULL);
xmlAddChild(root, headerNode);
for(NSString *key in [headerElements allKeys]) {
id header = [headerElements objectForKey:key];
xmlAddChild(headerNode, [header xmlNodeForDoc:doc elementName:key]);
}
}
and add the following code just below the above code:
xmlNewNs(root, (const xmlChar*)”http://localhost/MyService”, (const xmlChar*)”ns1″);
Remember to modify the URL to point to the correct namespace of your web service. ns1 in the above code specifies the namespace that each of the web service method must adhere to. Some web services may not require this, but for those that require, failing to provide may result in issue (2) below.
2. Simple values (integers, strings) are received property by the web service, but complex values (custom types) are received as blank
This is because the web service methods do not specify the namespace (ns1) it belongs to. Fixing this requires manual modification of MyService.m for each of the web service in the WSDL file. Assuming your service is called MyService, look for the following line:
NSMutableDictionary *bodyElements = nil;
bodyElements = [NSMutableDictionary dictionary];
if(callerParams != nil) [bodyElements setObject:callerParams forKey:@”MyService”];
Replace the last line with
if(callerParams != nil) [bodyElements setObject:callerParams forKey:@”ns1:MyService”];
3. Special characters such as ampersand will not be received on the server
This is because the generated code forgets to escape the ampersand character, as required by the SOAP protocol. To fix this, open USAdditions.m, locate the xmlNodeForDoc method, and replace the method code with the following:
There is a reason why the last block of code is a colorful image :). Had it been text, Google Blogger will try to unescape the ampersand character (the withString part), resulting in wrong code.
With all the above changes, the generated code should be compatible with most SOAP web services.