Tuesday, September 23, 2008

Mac to WCF Webservice - step 2

Following the failure of my previous attempt to connect a Cocoa application to my Microsoft WCF WebService (Mac to WCF-It Never Happened), I spent some time investigating the issues. It would seem that all the elements are there within the Cocoa Framework to accomplish what I want, but from reading the various blogs its not a simple matter of using SVCUTIL to pump out a proxy class to the client.

Not to be discouraged, I have found that the Cocoa equivalent of SVCUTIL (the Microsoft Proxy class generator) is called "WSMakeStubs" and its accessible from the command line. Assuming your webservice is up and running (read step 2 for instructions), start the Terminal application and change your directory to /Developer/Tools. Type the following command:
WSMakeStubs -x ObjC -dir [output location] -name [ObjectName] -url [url to the WCF file]?wsdl

eg:
WSMakeStubs -x ObjC -dir /Users/User1 -name JokeGen -url http://192.168.1.106:8080/JokeGen/Joke.svc?wsdl

This created me 4 files;
  • WSGeneratedObj.m

  • WSGeneratedObj.h

  • JokeGen.m

  • JokeGen.h


Which can then be added to the XCode project and referenced in the AppContoller.m by adding the following lines:
#import "WSGeneratedObj.h"
#import "JokeGen.h"


Adding a button to the application and joining to the Outlet was followed by adding the following code to the AppContoller.m file:

-(IBAction)pingTest:(id)sender
{
// Show the User that something is going on
[progress startAnimation:nil];

//Create the URL
WSMethodInvocationRef soapCall;
WSGeneratedObj *sq = [[WSGeneratedObj alloc] init];
soapCall = [sq
createInvocationRef: @"http://192.168.1.106:8080/JokeGen/Joke.svc"
methodName: @"Ping"
protocol: (NSString*) kWSSOAP2001Protocol
style: (NSString*) kWSSOAPStyleRPC
soapAction: @"http://tempuri.org/IJoke/Ping"
methodNamespace: NULL
];

NSDictionary *result;

result = (NSDictionary *)WSMethodInvocationInvoke(soapCall);

if(WSMethodResultIsFault) {
NSLog(@"Error:");
NSLog([result description]);
} else {
NSLog(@"No Error:");
NSLog([result description]);
}
[sq release];

//Update the interface
[progress stopAnimation:nil];
}


Following a Build and Run I received the following error message:

Current language: auto; currently objective-c
2008-09-23 17:27:50.640 AmaZone[1166:813] Error:
2008-09-23 17:27:50.642 AmaZone[1166:813] {
"/FaultCode" = -1;
"/FaultString" = "The message with Action 'SOAPAction' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).";
"/kWSHTTPResponseMessage" = ;
"/kWSResultIsFault" = 1;
}


I assume that this was due to some basic miss-match between the client SOAP request and that expected by the server. Doing a number of tests and investigations it turned out that the file created by WSMakeStubs misses out on some important information.
The method Namespace for the WebService call is initially set to NULL, when it should match the SoapAction "http://tempuri.org/", be sure to include the trailing "/".

Following all these changes you should be able to call and retrieve the WCF WebService from a Mac Application.