Showing posts with label Web Services. Show all posts
Showing posts with label Web Services. Show all posts

Friday, October 24, 2008

SOAP Web Services Redux

I should have posted a follow-up long before now, so my apologies. Some of my earliest postings here were on accessing Web Services from the iPhone. At the time I wrote that posting, I had not yet been accepted into the iPhone developer program, and wasn't aware that CoreServices.framework existed in the simulator, but not on the device.

I got accepted into the program and discovered the problem just before WWDC, and spent a good part of that conference trying to corner somebody from Apple about it. I was not able to get anyone who was even aware of the discrepancy, let alone somebody who could tell me whether the framework was intentionally left off of the device or if it would ever be brought over. The best I got was a very non-committal, off-the-record statement that WebServicesCore probably "wasn't long for the world". It hasn't been updated for a few years, and probably won't be. REST is the way of the future, why bother with SOAP, seems to be the feeling.

Of course, there is a huge installed base of SOAP web services which, I would argue, is a good enough reason to update and expand WebServicesCore. More importantly, many of the people coming to the iPhone are coming from .NET, which has extremely robust support for SOAP web services. I would argue that not having a good SOAP client in Cocoa Touch or Foundation makes our platform look bad.

Anyway, if you do need to access SOAP web services from the iPhone, you're stuck rolling your own or looking for a third-party SOAP library. There is an excellent thread over at iPhoneDevSDK. Right now, that's the best source of information I have to offer anybody about SOAP web services.

Now, if you're interested in RESTful web services, Apple has an article on that. In fact, they have an article that I co-wrote.

Tuesday, October 14, 2008

Another ADC Article Online

Dave and I wrote this one a while back, and it's finally percolated onto the ADC web site. Pretty happy with this article, and it feels good to have another one see the light of day, even without a byline.

http://developer.apple.com/webapps/articles/creatingrestfulclients.html

Monday, June 2, 2008

Web Services Core

I've been doing a lot of work with Web Services Core lately. Although I honestly hate to criticize Apple, I must say they've really dropped the ball with their Web Services support. SOAP web services have moved forward quite a bit, but Web Services Core seems to be frozen at circa 2003 functionality, and WSMakeStubs is even worse.

The first, and more minor problem I've hit recently, is that WSMakeStubs doesn't provide an easy mechanism for supporting HTTP authentication. That's not too big of a deal since it can be worked around with a few tweaks to the generated code. Web Services Core does provide a way to support this, but it's not necessarily something that would be obvious to somebody new to the platform.

The show-stopper, however, for many web services is that there is absolutely no mechanism for specifying attributes for an XML attribute that's passed inside your SOAP envelope. So, if a web service requires some piece of information to be passed in using an attribute, you're pretty much screwed.

There is a way to make Web Services Core do this, but it's such a hack, that you'd probably be better off rolling your own client libraries. It involves writing your own serializer and overriding the XML serialization for the CFTypes you use. It's an ugly hack, difficult to implement and even harder to explain. It's not the type of code you want to maintain if you're likely to have much turnover in your development staff.

I opened a bug report with Apple, but I'm not overly optimistic that they'll fix this, to be honest. They don't seem overly concerned with SOAP web services at the moment. 

This could be a big deal if you're an iPhone developer, though. You see, Apple appears to be trying to position the iPhone into the enterprise by adding Exchange support, and a lot of new iPhone developers that I've met come from a background of working in Corporate IT shops. Many of the web services used inside the corporate enterprise are written using Microsoft tools that support (and even encourage) using attributes in the XML tags in SOAP methods. In fact,a prime example of this is the Exchange WSDL itself. It has required attributes in at least one tag on every single exposed method. This means you currently can't write a Cocoa client for even a single exposed method from the Exchange WSDL using WSMakeStubs or Webs Services Core. You can write one, of course, but it's going to be a lot of work.

I'm honestly concerned about the impression this is going to leave on people coming to the iPhone from .Net, which has very robust Web Services libraries. Many of the new Cocoa developers I've talked to lately have experience with .Net. Of course, the long-time Cocoa developers are trying to convince these newcomers how wonderful Cocoa is, but things like this kind of get in the way of proving that point. 

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:
/Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/CoreServices.framework
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 http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl
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 http://www.weather.gov/forecasts/xml/DWMLgen/schema/DWML.xsd|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.