Open Source XML-RPC Class for Cocoa

All of this code is really, really old! And not very good! You’re better off looking somewhere else!

Download XMLRPCCocoa 1.0b1.

What it is

It’s an Open Source (BSD license) XML-RPC client class. Included is a simple application that demonstrates how to use it.

This XML-RPC code is used in NetNewsWire.

What is XML-RPC?

It’s a way of calling remote procedures via HTTP and XML. See XML-RPC.Com for more information.

Why not use Apple’s XML-RPC implementation?

At this writing (7 March 2003) the implementation of XML-RPC in Apple’s WebServicesCore has a crashing bug. In some cases when a method response contains an empty element, there’s a crash.

We fully expect this (and other smaller bugs) to get fixed—but we couldn’t wait. (NetNewsWire’s weblog editor uses XML-RPC heavily; it’s how external editors work with weblogs.)

Using this XML-RPC code has other benefits: it’s Open Source, so you can see the code. As a set of Cocoa classes it’s easier to use than a C API, and you can create sub-classes.

Software used

This class uses CURLHandle for the actual HTTP transport. (A pre-built CURLHandle framework is included in the download.)

The base64 encoding and decoding code is a slightly modified version of the code by Dave Winer. See C source code for Base 64.

BETA

This is a beta. It needs more testing. It needs documentation. (But the below should be enough to get you started.)

How to make an XML-RPC call

The following example code comes from XMLRPCCocoa.m, the code for the demo app that comes with the download.

getStateName

Calling an XML-RPC method requires a few things: the URL of the XML-RPC server, a method name, and a list of parameters.

The sample code creates an XMLRPCCall object given the URL (as a string) of the XML-RPC server.

XMLRPCCall *rpcCall = [[XMLRPCCall alloc] initWithURLString: rpcURL];

Then it sets the method name.

[rpcCall setMethodName: @"examples.getStateName"];

Then it sets the parameters.

params = [NSArray arrayWithObject: stateNumber];
[rpcCall setParameters: params];

In this example there’s just one parameter, but parameters should always be an array—even when there are no parameters.

In XML-RPC parameter order matters: there are no named parameters.

Then it calls the XML-RPC method.

[rpcCall invokeInNewThread: self callbackSelector: @selector (getStateNameCallback:)];

The call to invokeInNewThread does two things: first it calls the XML-RPC method, then it calls your code: it calls your callback specified by the target and callbackSelector.

Your callback is always called on the main thread, even though the XML-RPC transport is run on another thread.

getStateNameCallback

Callbacks take one parameter, the XMLRPCCall object.

The sample callback first outputs the raw XML request and response to the Console. This is useful for debugging. It’s not something users usually want to see. (To put it mildly.)

NSLog (@"XML-RPC request text: %@", [rpcCall requestText]);
NSLog (@"XML-RPC response text: %@", [rpcCall responseText]);

Then it checks to see if the call succeeded.

if ([rpcCall succeeded]) {

Success means there were no download or parse errors and the XML-RPC server did not return a fault object.

The sample code gets the returned value like this:

NSString *stateName = (NSString *) [rpcCall returnedObject];

The call to returnedObject is the whole point of this whole thing—it’s the value (as a Cocoa object) returned by the XML-RPC server.

If there was an error of some kind, then the sample code handles errors in displayError.

displayError

There are several types of possible errors.

A download error could be that, simply, no data was returned, even though a 200 response was returned by the server.

A download error could also be that a response other than 200 was returned: 404, for example.

Here’s the sample code handling download errors.

if ([rpcCall isDownloadError]) {
	int statusCode = [rpcCall statusCode];
	BOOL noData = [rpcCall isNoDataError];

	title = @"Download Error";
		
	if (noData) /*200 response but no data*/
		message = @"The server returned no response.";
	else
		message = [NSString stringWithFormat:
			@"The server returned an unexpected response code: %i.",
			statusCode];
	} /*if*/

Another type of error is a parse error. This happens when you get invalid XML from the server. One potential cause of this error is that you actually get HTML. For instance, if you use an incorrect RPC URL when calling a method, you may get an HTML page returned instead of the XML-RPC response you were expecting.

Here’s how to get parse errors.

else if ([rpcCall isParseError]) {
	title = [rpcCall parseErrorTitle];
	message = [rpcCall parseErrorMessage];
	} /*if*/

The final type of error is an XML-RPC fault. (See the XML-RPC spec for more about faults.)

A fault is something the XML-RPC server returns. It’s not a network error. Here’s how the sample code gets the fault info.

else if ([rpcCall isFault]) {
	title = @"XML-RPC Fault";
	message = [NSString stringWithFormat:
		@"Fault code: %i\nFault message: %@", [rpcCall faultCode],
		[rpcCall faultString]];		
	} /*else if*/

More docs to come...

There will be more documentation, including standard Cocoa-class-type documentation. For now, if you have any questions, send email to brent at ranchero dot com.

© 1995-2014 Ranchero Software, LLC