WebServicesCore, Why Hath Thou Forsaken Me
22 Jan 2007 - 3 minute readI've been hacking around with more webservices-based applications recently (Twitterer for example) and I've also reminded myself what an utter pain in the ass they can be in Cocoa. With twitter, they make available both JSON and XML-based webservices, which is good since they use basic HTTP authentication for their user-specific webservices (i.e. everything but retrieving the public timeline). The XML-based webservices are more or less straight-forward to hack up with Cocoa, all one really needs to do is write a parser (NSXMLDocument) and then make use of the URL loading classes (NSURLConnection, NSMutableURLRequest) to retrieve and process, or POST data. This method of interacting with "webservices" (more wgetting than anything else) is rudimentary at best, and in my personal opinion, isn't as robust as SOAP webservices are. It does however, work. Regardless of the framework, programming language, or geo-orbital location of the moon, they will work because all you're doing is making an HTTP GET and then parsing the results.
The benefit of SOAP webservices however, is the almost "literal" translation of objects encapsulated into SOAP messages, into runtime objects. In effect, if I have a Person object defined in my WSDL and similarly defined in my application (with a good SOAP framework) the objects should be encoded and decoded appropriately when passed via SOAP. The real power of SOAP can be realized when used with ASP.NET (ick) webservices and .NET clients (see ASP.NET - Mono) where you can relatively quickly and easily build and deploy a services-oriented application. That's .NET/Mono however, my work is dealing with NuSOAP and Cocoa, a less than ideal mix.
To start hashing out a Cocoa-webservices-oriented application, your first stop should be at a local pub, brewery, or mayor's office for a good round of intoxication, only after every tissue in your body is soaked with fine casket-aged whiskey will you be prepared to embark on your journey. It's usually best to start with a complete WSDL, you can then use /Developer/Tools/WSMakeStubs to generate what resembles some Objective-C that you can flesh out to some extent to provide an intermediary layer between your sane Cocoa code and the actual SOAP method calls. Cocoa makes SOAP painful. The stub code revolves around one magical "object" WSMethodInvocationRef. Think of WSMethodInvocationRef like you would a sadistic elf that only pops out of his little elf home to kick you in the groin before shouting "TRY AGAIN IDIOT" and scurrying away, there's a bit of magic involved, but mostly pain. Since the invocations will just return a generic "id" type, the only way to really be sure what your webservice invocation returns is to either call [NSObject respondsToSelector:(SEL)] or just trust that the webservice you called will return what you expect, whether it be an NSString, NSDictionary, NSArray, or NSNumber (NSNumber is what's returned in place of numbers and booleans, it's WebServiceCore's cheap way of boxing those primitives). TRY AGAIN IDIOT.
Something else to note is that things you might expect to be able to use, such as basic HTTP authentication are absolutely non-existent in the magic WebServicesCore black-box. With a URL loading-based webservice (JSON, XML) you can just use the delegate method:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
And authenticate from there, popping around through the various NSURLConnection delegate methods. I have also experimented with updating the SOAP endpoint to include a URL something like the following:
While one might assume the HTTP subsystem hidden behind that magical WSMethodInvocationRef would handle this appropriately, and translate it to the basic HTTP authentication tokens, it just doesn't work. TRY AGAIN IDIOT.
The alternatives are few and far between if you absolutely need to use SOAP webservices for a certain Cocoa project, I am working on converting a framework that Jonathan Wight wrote to use the URL loading classes in Cocoa, but other than that learn to love WSMethodInvocationRef or plead with your web developers to rewrite their existing webservices with REST, JSON, etc. WebServicesCore is an antiquated pain in the ass, and probably hasn't been updated since Steve Jobs was at NeXT.
TRY AGAIN IDIOT.