Friday, April 18, 2008

Web Services Fun

Before we work more with libxml, let's talk about web services. Web services are a good practical application of XML and, it would seem to me, many iPhone apps will need to work as web service clients, so it seems like a good detour.

The easiest way to access web services from Objective-C is by leveraging functionality found in Core Services, a framework that is available in both Cocoa and Cocoa Touch. For both platforms it needs to be manually linked into your project as it is not included by default in the Xcode templates.

For a Cocoa project, simply select Project -> Add to Project... then add the framework found at the following location to your project, making sure to uncheck the "copy into your project folder" option:
For the iPhone, well, you'll need to figure that one yourself; I've already gotten in trouble once with Apple for telling people where it is, but if you use Spotlight, you should be able to find it without too much difficulty.

Now once you've got the Core Services framework included in your project, how do you use it? Well, if the web service you want to access has a WSDL then it's really quite easy. As part of the Developer tool install, a little command line utility was installed on your machine called WSMakeStubs which will generate stubs with all the methods you need to access that web service. For a Cocoa project, the way to use it is as follows:
WSMakeStubs -x ObjC -name MyClassFileName -url http://server:port/path/to/wsdl
So, let's say that we wanted to create stubs for accessing the National Weather Service. For that, you would type this:
WSMakeStubs -x ObjC -name WeatherService -url
Once you issue that command, it will create four files for you:
  • WeatherService.m
  • WeatherService.h
  • WSGeneratedObj.m
  • WSGeneratedObj.h
The first two implement the class you'll actually use to interact with the web service, the second two are generic classes that you need in your project, but which will be generated every time you run WSMakeStubs. If you run WSMakeStubs again, you can just delete the new versions, as they will be exactly the same as the ones you already have.

No, go ahead and add these four files to any Xcode project where you want to access the web service. If you're playing along at home, I just created a Foundation Tool Xcode project and added the four files to it. We need to import the WeatherService.h file wherever we intend to use it, like so:
#import "WeatherService.h"
In order to use this service, we need to know what functions this web service exposes to us. So, how do we know what methods we can call? Well, you can look at the WSDL or documentation that goes along with the web service if those are available. If not you can simply open up WeatherService.h and scroll down toward the bottom. The last @interface declaration in the file — ndfdXMLService — is the class we'll be working with. You'll notice that it has several class methods which you (of course) remember are the methods that begin with a + rather than a -.  These class methods correspond to the functions exposed in the web service. Let's pick one to try out, shall we? How about:
+ (id) LatLonListZipCode:(CFTypeRef /* Complex type|zipCodeListType */) in_zipCodeList;
This is the simplest one in the web service, so that makes it good for showing the basics of accessing a web service. Now, whenever possible, WSMakeStubs uses known datatypes when constructing method calls. Sometimes, however, you'll see parameter specified as CFTypeRef, which is similar to id in that it's a generic pointer whose type is not specified. That means either that that parameter is more complex than a single datatype can hold OR that WSMakeStubs was unable to determine which datatype or object would be best for holding that parameter. Since it knows it doesn't know everything, WSMakeStubs includes a comment with a link to the XSD file where the parameter is defined to help you out. 

The problem is, it's not obvious nor well-documented how to use web service methods that take CFTypeRef parameters. It's easy enough to figure out how to pass in a raw datatype, but not so easy otherwise.

Now, unlike this particular one, a great many web services will take only simple parameters and will be relatively easy to use. Here is an example of how you might call such a web service function:
id result = [myWebService callMethodThatTakesInt:1];
The value of result will either be an NSString or an NSData instance depending on the web service. Ordinarily, the result will contain XML data with the web services response to your call.

But what do we do in this case since we have a complex type? Well, unfortunately, we have to read the documentation and poke around the stubs and experiment a little. In many cases, complex objects expect an NSDictionary with the xml tag name used as the key and the tag's contents as the corresponding object. For complex types where there are nested tags, some of the objects in the NSDictionary you create might themselves be NSDictionary instances. Because of this, building your parameter objecs can get fairly complex in some instances. 

Fortunately for us, it turns out that this web service function just takes a single string containing a space-separated list of zip codes. Well, we can do that. Heck, we can do that in one line of code:
id result = [ndfdXMLService LatLonListZipCode:@"13413 94588 12901"]; 
Notice that we are using NSString, and not c strings here. You could also use CFString because they are toll-free bridged to NSString but since we're in Cocoa, let's stick with Cocoa, shall we?

After running this code, result will be a string that contains XML with tags representing the longitude and latitude for each of the zip codes passed in. Blogger won't let me paste that XML in - it keeps thinking the XML is HTML and if I change the tags to use > and < it helpfully converts those back to angle brackets. *sigh* Well, the price is right. Anyway, you can see the XML here if you want.

Now, we have the server's response in XML... if only we had an easy way to parse XML in Cocoa and Cocoa Touch. Oh, wait... I, um...  promised you something vaguely similar to that yesterday, didn't I? :)

Coming next: The libxml wrapper and a little practical application of it.


Phillip Jacobs said...

This works in simulator, but once you try to compile for the device, it blows up with errors. Have you had any luck compiling your test to work on the device? If so, can you explain how?

HoooHaaa said...

I also have problems getting it to work on the device. I can't find the coreservices in the official SDK final that the issue?
Great article if I could get it all to work.

Ramya said...

Thanks for the sample app. That was really helpful. I too have the same problem. I would be truly grateful if you could let me know how to compile it on the device.

james jack said...

Me also - the simulators all have the coreservices.framework, but the iPhone sdk does not. I tried spotlight and it didn't find the missing framework. I need to get this working for an application which will initially be free and will bring Chemical structure searching capabilities to the iPhone. A windows mobile version of the same is already working.

If you got this working can you please explain how?

james jack said...

Anyone else looking - look here:

the author of this blog references it in a later blog...

Jesse Barnum said...

Did anybody ever get this to work on the iPhone instead of just the simulator? I really don't want to mess with REST proxies, I just want to use the SOAP stubs.

Mark said...

Outstanding blog, nice presentation about web design and development in Miami I love the contents and thoughts. I am so happy to vote and just now I voted for your blog in blogger awards. Keep it up and write more please. I invite your precious visit and suggestions about my Blog and site also. Please make your choice for me also by using this link. Miami web design

Edwin said...

scrub m65 kamagra attorney lawyer body scrub field jacket lovegra marijuana attorney injury lawyer

JeansPilot said...

JeansPilot offers the chance to buy a large variety of men’s and women’s jeans clothing from the world famous Italian Brands.
Online jeans clothing store looks for original fashion clothing sales and clearances of worldwide known designers. We participate in fashion auctions to get the lowest possible price for Top quality Clothes, Shoes and Accessories.
Buy Jeans

h4ns said...

What youre saying is completely true. I know that everybody must say the same thing, but I just think that you put it in a way that everyone can understand. I also love the images you put in here. They fit so well with what youre trying to say. Im sure youll reach so many people with what youve got to say.

Arsenal vs Huddersfield Town live streaming
Arsenal vs Huddersfield Town live streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Notts County vs Manchester City Live Streaming
Notts County vs Manchester City Live Streaming
Bologna vs AS Roma Live Streaming
Bologna vs AS Roma Live Streaming
Juventus vs Udinese Live Streaming
Juventus vs Udinese Live Streaming
Napoli vs Sampdoria Live Streaming
Napoli vs Sampdoria Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
AS Monaco vs Marseille Live Streaming
AS Monaco vs Marseille Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Technology News | News Today | Live Streaming TV Channels