Views from Phanfare CEO and Co-founder Andrew Erlichson

Link Web Service Integration, Mac vs. PC


As Ben mentioned in the first installment (I am a guest blogger for this installment), part of the original decision to build Phanfare on Microsoft’s .NET platform was the ease with which Visual Studio lets you build web services and clients that integrate with those services.

Building a .NET web service method within Visual Studio isn’t really much different than writing any other function. You make it a web method by simply adding the “[WebMethod]? attribute. Visual Studio takes care of everything else under-the-hood. For example, here is the C# web service definition of an imaginary method:


[WebMethod]
public PhanfareAlbum GetAlbum(string sessionId, long albumId, bool getImagesToo, out int albumVersion, out string albumName, out PhanfareImage[] images)
{

}

Easy, huh?

Using that web method in another application (like the Phanfare Photo client) is also simple. By referencing the web service in the client application’s Visual Studio project, Visual Studio will fetch the service’s WSDL document (Web Services Definition Language — an XML document that describes the methods that the service provides) and generates a proxy class that contains the stub methods — methods that look just like the ones you wrote on the service side. This generated code handles all the icky details of performing the under-the-hood magic to invoke the method on another machine across the network, while making it look like a local method call to you, the programmer.

The web method declared above is invoked from a C# client application like this:


PhanfareWebService ws = new PhanfareWebService();
string albumName;
int albumVersion;
PhanfareImage[] images;

// See how nice? The client call looks just like a local call to the web method.
PhanfareAlbum myAlbum = ws.GetAlbum(mySessionId, 12345, iWantTheDarnImages, out albumVersion, out albumName, out images);

// Now use the info!
Debug.WriteLine(String.Format(“This album was created on {0}?, myAlbum.createdDate.ToString()));

Facile!

Now for the Mac side of the story.

To make the Phanfare Photo Mac client work, it would have to talk to the same SOAP web service as the Windows client. When we first began looking at the Mac platform, we were excited to have read somewhere that OS X supports SOAP-based web services. However our excitement quickly dissipated when, in typical Apple style, we couldn’t find any coherent, useful documentation on how to integrate a Cocoa application with a SOAP-based web service (if you find some, please let us know. Here is Apple’s documentation. Now go build a complex web service client on the Mac. Have fun.). From what I could gather you put all of your parameters (no complex user-defined types, please) into a hash table (so if I get the parameters wrong I can still compile my client with no errors? Wonderful.), pass it to some web service invocation function, and then get a hash table back. Oh and if you run Apple’s WSMakeStubs application on Phanfare’s WSDL you basically get a lot of methods that have nothing but comments stating that complex types are not supported. Great.

After quickly realizing that Apple’s limited support for web services wasn’t going to make our lives any easier, we started looking for alternatives (to be honest Apple’s support seemed so lacking that we never even tried to build a test application before looking elsewhere). What we found was gSOAP, by Robert van Engelen at Florida State University. We decided to use gSOAP for several reasons: it is open source and distributed under the very favorable gSOAP License, it is extremely well documented, it is widely used, and it has an extremely active user community. Oh, and it works on the Mac (it even came with a Mac-specific makefile).

The primary complaint we had with gSOAP was that it did not integrate seamlessly with Objective-C/Cocoa, the environment in which we’d chosen to develop the Mac client. While gSOAP worked quite well in our tests, calling it from Objective-C/Cocoa looked something like this:


struct soap *mySoap = soap_new2(SOAP_IO_DEFAULT, SOAP_IO_DEFAULT);

// gSOAP takes input and provides output parameters via structs,
// not return values and in/out parameters the way the
// Visual Studio-genarated stub methods do.
struct _ns1__GetAlbum inParameters;

// We’re using NSStrings in our code. gSOAP needs a char *, so I need to convert.
inParameters.session_id = [mySessionId UTF8String];

inParameters.albumId = 12345;

// We’re using Objective-C booleans in our code, need to convert to the
// gSOAP-defined true_ and false_ enumeration values.
inParameters.getImagesToo = iWantTheDarnImages == YES ? true_ : false_;

struct _ns1__GetAlbumResponse soapOutParameters;
int soapRv = soap_call__ns1__GetAlbum(soap, endpoint, NULL, &soapInParameters, &soapOutParameters);

// Now use the info! But wait! I must convert the time_t to an NSDate first!
NSLog([NSString stringWithFormat:@?This album was created on %s?, [[NSDate dateWithTimeIntervalSince1970:soapOutParameters->albumDate] description]]);

// Now, all my return parameters
// (the PhanfareAlbum, albumName, albumVersion, and the array of images)
// are in “soapOutParameters? and are C types.
// But I want NSStrings, NSDates, NSArrays, Objective-C Booleans…
// not char *’s, time_t’s and C arrays!
// I want Objective-C objects, not C structs!
// So, I guess I have to convert them. For example…

NSString albumName = [[NSString stringWithCString:soapOutParameters->albumName] retain];
int albumVersion = soapOutParameters->albumVesrion;

Clearly this will become very tedious very quickly and is not conducive to rapidly developing an application (especially on a platform that’s new to you and especially when you make lots of web service calls, like we do). What we needed was a way to make the web service calls look like the calls we were used to, but using Objective-C/Cocoa types.

To achieve this, we modified the gSOAP compiler (the thing that generates the web service stub methods from the WSDL — the same thing Visual Studio does when you add a web service reference to your project) to emit Objective-C in addition the code it already generates. This new code includes Objective-C class equivalents for all user-defined classes, using Cocoa/Objective-C types where appropriate (e.g., for member strings, arrays, booleans, dates, and other user-defined classes). It also contains an automatically generated Objective-C proxy class that implements Objective-C methods that look just like the original web service methods we’ve come to know and love, but using Objective-C/Cocoa types. These methods contain all the yucky glue (like in the example above) needed to convert between the C and Objective-C/Cocoa types (again, for strings, dates, arrays, binary data and such).

Now the web service call from Objective-C looks like this:


// PhanfareWebService is the Objective-C “proxy? class
PhanfareWebService *ws = [PhanfareWebService init];
int albumVersion;
NSString *albumName;
NSArray *images;

// See how nice this is now? Everything returned to me is an Objective-C/Coca type.
PhanfareAlbum *myAlbum = [ws GetAlbum:sessionId albumId:12345 getImagesToo:getTheDarnImages outabumVersion:&albumVersion outalbumName:&albumName outimages:&images];

// Now use the info!
NSLog([NSString stringWithFormat:@?This album was created on %s?, [[myAlbum createdDate] description]]);

Much nicer, huh?

In addition the proxy layer does a few other nice things, like cleaning up some minor rough edges in gSOAP (e.g., beautifying enumeration types and values and such) and providing support for pooling multiple gSOAP contexts to simplify multithreading.

So, after initial excitement, then disappointment in Apple’s support for SOAP web services, gSOAP, with the addition of our own Objective-C/Cocoa layer, has turned out to be a very nice alternative. It has caused us very few headaches (and most of those were from bugs in our own generated code) and is now as easy to code against as Visual Studio generated stubs on the Windows side. Granted, we don’t have the right-click-and-choose-Update-Web-Reference level of integration and ease that Visual Studio provides. But we’ve come darn close.

    Back to Phanfare blog home »

© 2007-8 Phanfare, Inc.