Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Lanky Streak

macrumors newbie
Original poster
Apr 28, 2009
14
0
Wiltshire, UK
I'm a total newbie Mac programmer having worked for years as a developer using Microsoft technologies. I've only written one Objective-C app, a simple 'hello world' program.

For my second app I want to connect to a simple test web service I've written using Microsoft VB.NET. This web service has two functions: Sum and Product. They each accept two numeric arguments and return, you guessed it, the sum or product of the two values. The web service works fine from a Microsoft app but I can't get it to work from my Objective-C app. I'm using code I copied from this forum and have modified, but clearly my modification is wrong The web service is being activated but it always returns an answer of zero which makes me suspect the parameters aren't being passed in correctly. I'm using Xcode 3.1 on Leopard.

1) How can I view the XML that's sent to, and received back from, the web service?

2) Can anyone see what's wrong with this code? It just takes the two parameters from two combo boxes, and writes the answer to a textview box.

Thanks guys. I've spent over a day on this without success.

Code:
- (IBAction) doProduct: sender
{
	NSString *myArg1 = [Arg1 stringValue];
	NSLog(@"dblArg1 = %@", myArg1);
	NSString *myArg2 = [Arg2 stringValue];
	NSLog(@"dblArg2 = %@", myArg2);
	
	WSMethodInvocationRef rpcCall;
	NSURL *rpcURL = [NSURL URLWithString: @"http://www.vbsampler.net/testwebservice/maths.asmx"];
	NSString *methodName = @"Product";
	NSString *nameSpace = @"http://www.vbsampler.net/";
	
	NSArray *vals = [NSArray arrayWithObjects: myArg1, myArg2, nil];
	NSArray *keys = [NSArray arrayWithObjects:@"dblArg1", @"dblArg2", nil];
	NSDictionary *params = [NSDictionary dictionaryWithObjects: vals forKeys: keys];
	
	NSArray *paramOrder = [NSArray arrayWithObjects:@"dblArg1", @"dblArg2", nil];

	//NSDictionary *result;
	NSDictionary *headers = [NSDictionary dictionaryWithObject:@"http://tempuri.org/Product" forKey:@"SOAPAction"];
	
	rpcCall = WSMethodInvocationCreate((CFURLRef) rpcURL, (CFStringRef) methodName, kWSSOAP2001Protocol);
	
	WSMethodInvocationSetParameters (rpcCall, (CFDictionaryRef) params, (CFArrayRef)paramOrder);
	WSMethodInvocationSetProperty(rpcCall, (CFStringRef) kWSHTTPExtraHeaders, (CFTypeRef) headers);

	// for good measure, make the call follow redirects.
    WSMethodInvocationSetProperty(rpcCall,    kWSHTTPFollowsRedirects, kCFBooleanTrue);
	WSMethodInvocationSetProperty(rpcCall, kWSSOAPMethodNamespaceURI, (CFStringRef) nameSpace);

    // set debug props
    WSMethodInvocationSetProperty(rpcCall, kWSDebugIncomingBody,     kCFBooleanTrue);
    WSMethodInvocationSetProperty(rpcCall, kWSDebugIncomingHeaders, kCFBooleanTrue);
    WSMethodInvocationSetProperty(rpcCall, kWSDebugOutgoingBody,     kCFBooleanTrue);
    WSMethodInvocationSetProperty(rpcCall, kWSDebugOutgoingHeaders, kCFBooleanTrue);
	
	NSDictionary *result = (NSDictionary *) (WSMethodInvocationInvoke(rpcCall));
	
	if (WSMethodResultIsFault ((CFDictionaryRef) result)) {
		NSLog(@"result = %@", [result objectForKey: (NSString *) kWSFaultString]);
		[txtAnswer setString: @"Error"];
	} 
	else 
	{		
		[txtAnswer setString: [result objectForKey:@"ProductResult"]];
		NSLog(@"result = %@", [result objectForKey:@"ProductResult"]);
	}
}
 
In short, SOAP web services are giant pain in the ass. Yes, if you're using Visual Studio, it's stupid easy generating them and consuming them because you're not exposed to all of the magic behind the scenes.

I've never tried consuming one via Objective-C. I would start here http://www.cocoadev.com/index.pl?ObjCSoap, maybe here http://mac-objective-c.blogspot.com/2009/04/soap-webservices-in-objective-ccocoa.html and, of course, here http://www.google.com/search?q=obje...s=org.mozilla:en-US:official&client=firefox-a

If you plan on going to production with this, consider exposing something that's a little more portable to all technologies.
 
Thanks for that sbauer. I've trawled Google but hadn't come across these pages so I'll study them carefully. This query isn't for a production application, merely my way of trying to learn. I've been a freelance developer using Microsoft Visual Studio since the 1990s so like them or hate them Microsoft has indirectly put food on my family's table for years.

I bought my first ever Mac last year and have been so impressed that when time permits I'm determined to transfer my skills to Objective-C and Cocoa (a long, slow process, although I programmed with C and Unix back in the late 1980s).

You're right, SOAP web services are a pain in the ass. I was just hoping to build a 'bridge' between what I know and what I'm struggling to learn.
 
Thanks for that sbauer. I've trawled Google but hadn't come across these pages so I'll study them carefully. This query isn't for a production application, merely my way of trying to learn. I've been a freelance developer using Microsoft Visual Studio since the 1990s so like them or hate them Microsoft has indirectly put food on my family's table for years.

I bought my first ever Mac last year and have been so impressed that when time permits I'm determined to transfer my skills to Objective-C and Cocoa (a long, slow process, although I programmed with C and Unix back in the late 1980s).

You're right, SOAP web services are a pain in the ass. I was just hoping to build a 'bridge' between what I know and what I'm struggling to learn.

I know what you're saying. I'm a full time .NET dev myself. It pays well and the market is rather large. Can't argue with that.

Good luck with your problem. Learning is always a good thing.
 
Hey Sayer, that's a seriously interesting tutorial. At first glance I can't see how my app is different from the sample in the tutorial but I'm a believer in working methodically through tutorials and I'll do that later today. Thank you for responding.

You could always use a language like Python or Ruby to handle the SOAP side of things and just embed it in your Objective-C application. I believe Python especially is better for SOAP than the Apple stuff.

Python integration is also pretty easy and totally transparent, and with SOAP being web based you are not going to notice a speed difference between your Objective-C and Python code.

Edit: Check this out:

http://diveintopython.org/soap_web_services/
 
You could always use a language like Python or Ruby to handle the SOAP side of things and just embed it in your Objective-C application. I believe Python especially is better for SOAP than the Apple stuff.

Python integration is also pretty easy and totally transparent, and with SOAP being web based you are not going to notice a speed difference between your Objective-C and Python code.

Edit: Check this out:

http://diveintopython.org/soap_web_services/

Wow, this is all fascinating. What an interesting Python website. Thanks Cromulent (great word - I'd never heard it until it was mentioned in an episode of The Simpsons). I feel like a kid in a candy store. I have heard of Python and Ruby but never explored them deeply. Mac ownership has opened a rich new dimension for me, both as a user and as a developer. I'm really grateful to have my study directed by people who know so much more than I do.
 
Along with soap, you will receive an executable, wsdl2h.exe using which you can generate header files. Use these header files in your Mac obj C to make soap calls to server. Look into the open source project "www.ifolder.com" Mac code. You will know how soap is being used in the project.
 
Along with soap, you will receive an executable, wsdl2h.exe using which you can generate header files. Use these header files in your Mac obj C to make soap calls to server. Look into the open source project "www.ifolder.com" Mac code. You will know how soap is being used in the project.

Forgive me satyam90, I don't understand. Please know that I'm a real newcomer to Mac programming. Where will I find wsdlh.exe? Thanks.
 
You could always use a language like Python or Ruby to handle the SOAP side of things and just embed it in your Objective-C application. I believe Python especially is better for SOAP than the Apple stuff.

Python integration is also pretty easy and totally transparent, and with SOAP being web based you are not going to notice a speed difference between your Objective-C and Python code.

Edit: Check this out:

http://diveintopython.org/soap_web_services/

If you are only consuming SOAP services in Python, you might also want to check out SUDS which makes accessing WS's a dawdle.
 
Forgive me satyam90, I don't understand. Please know that I'm a real newcomer to Mac programming. Where will I find wsdlh.exe? Thanks.

I'm afraid there's some mixup here. He refers to gSOAP (www.genivia.com), the _only_ way I've been able to reliable do multi-platform SOAP. Contrary to all the hype, WebServices are anything but "drop-in cross platform" services, especially when you're talking about services more involved than "get stock quote".

gSOAP is an open-source SOAP stack. It comes with a wsdl2h executable which consumes a WSDL (as a file or as an URI) and generates a C or C++ header file from it. Then you use soapcpp2 to construct proxy/stub code out of that header file.

I've been able to get our WebService working on my iPod Touch using gSOAP, but only after a lot of effort. You can safely forget about using any Apple-supplied tooling to generate the SOAP plumbing.
 
I'm afraid there's some mixup here. He refers to gSOAP (www.genivia.com), the _only_ way I've been able to reliable do multi-platform SOAP. Contrary to all the hype, WebServices are anything but "drop-in cross platform" services, especially when you're talking about services more involved than "get stock quote".

gSOAP is an open-source SOAP stack. It comes with a wsdl2h executable which consumes a WSDL (as a file or as an URI) and generates a C or C++ header file from it. Then you use soapcpp2 to construct proxy/stub code out of that header file.

I've been able to get our WebService working on my iPod Touch using gSOAP, but only after a lot of effort. You can safely forget about using any Apple-supplied tooling to generate the SOAP plumbing.

I'd like to say a big thank you to you Sander, and to everyone else who has taken the trouble to reply to my questions here. This MacRumors forum is an excellent resource for newbies like me. You have opened my eyes to a rich new world of developer tools and technologies that I shall study.
 
Despite some excellent and informative replies here, my original questions about connecting to my test web service with an Objective-C app were never answered. In case it helps anyone else, I did finally get my app working, although only after tweaking and recompiling my web service. I post below the working code.

Code:
@implementation myWsClass
- (IBAction) doProduct: sender
{
    NSString *myArg1 = [Arg1 stringValue];
    NSString *myArg2 = [Arg2 stringValue];
   
    WSMethodInvocationRef rpcCall;
    NSURL *rpcURL = [NSURL URLWithString: @"http://www.vbsampler.net/testwebservice/maths.asmx"];
    NSString *methodName = @"Product";
    NSString *nameSpace = @"http://vbsampler.net/testwebservice";
   
    NSArray *vals = [NSArray arrayWithObjects: myArg1, myArg2, nil];
    NSArray *keys = [NSArray arrayWithObjects:@"dblArg1", @"dblArg2", nil];
    NSDictionary *params = [NSDictionary dictionaryWithObjects: vals forKeys: keys];
   
    NSArray *paramOrder = [NSArray arrayWithObjects:@"dblArg1", @"dblArg2", nil];
 
    NSDictionary *headers = [NSDictionary dictionaryWithObject:@"http://vbsampler.net/testwebservice/Product" forKey:@"SOAPAction"];
   
    rpcCall = WSMethodInvocationCreate((CFURLRef) rpcURL, (CFStringRef) methodName, kWSSOAP2001Protocol);
   
    WSMethodInvocationSetParameters (rpcCall, (CFDictionaryRef) params, (CFArrayRef)paramOrder);
    WSMethodInvocationSetProperty(rpcCall, (CFStringRef) kWSHTTPExtraHeaders, (CFTypeRef) headers);
 
    // for good measure, make the call follow redirects.
    WSMethodInvocationSetProperty(rpcCall,    kWSHTTPFollowsRedirects, kCFBooleanTrue);
    WSMethodInvocationSetProperty(rpcCall, kWSSOAPMethodNamespaceURI, (CFStringRef) nameSpace);
 
    NSDictionary *result = (NSDictionary *) (WSMethodInvocationInvoke(rpcCall));
   
    if (WSMethodResultIsFault ((CFDictionaryRef) result)) {
      [lblAnswer setStringValue: @"Error"];
    }
    else
    {      
      [lblAnswer setStringValue: [result objectForKey:@"ProductResult"]];
    }
}
@end
 
that code doesnot work while calling webservice after fix intervals continously

Thanks this code really helps me to connect webservice with my app.

I am calling the web service continously after fix intervals of time.
after some time I am getting this error from webservice :
domain = 1;
error = 24;
msg = "Stream Error";

and then on next calls webservice does not respond then I have to restart my application and it then connects again and works properly.

any help or guidance?

thanks
ashish
 
Please help!

Lanky Streak - please can you send me your web service code?

I am stuck with exactly the same problem: web service seems to work perfectly until I try to pass parameters.

Debugging the Xcode, parameters are definitely being sent in the SOAP packet but debuggiung at .NET end, they never arrive!

I am tearing my hair out.

Thanks,

Mb

Despite some excellent and informative replies here, my original questions about connecting to my test web service with an Objective-C app were never answered. In case it helps anyone else, I did finally get my app working, although only after tweaking and recompiling my web service. I post below the working code.

Code:
@implementation myWsClass
- (IBAction) doProduct: sender
{
    NSString *myArg1 = [Arg1 stringValue];
    NSString *myArg2 = [Arg2 stringValue];
   
    WSMethodInvocationRef rpcCall;
    NSURL *rpcURL = [NSURL URLWithString: @"http://www.vbsampler.net/testwebservice/maths.asmx"];
    NSString *methodName = @"Product";
    NSString *nameSpace = @"http://vbsampler.net/testwebservice";
   
    NSArray *vals = [NSArray arrayWithObjects: myArg1, myArg2, nil];
    NSArray *keys = [NSArray arrayWithObjects:@"dblArg1", @"dblArg2", nil];
    NSDictionary *params = [NSDictionary dictionaryWithObjects: vals forKeys: keys];
   
    NSArray *paramOrder = [NSArray arrayWithObjects:@"dblArg1", @"dblArg2", nil];
 
    NSDictionary *headers = [NSDictionary dictionaryWithObject:@"http://vbsampler.net/testwebservice/Product" forKey:@"SOAPAction"];
   
    rpcCall = WSMethodInvocationCreate((CFURLRef) rpcURL, (CFStringRef) methodName, kWSSOAP2001Protocol);
   
    WSMethodInvocationSetParameters (rpcCall, (CFDictionaryRef) params, (CFArrayRef)paramOrder);
    WSMethodInvocationSetProperty(rpcCall, (CFStringRef) kWSHTTPExtraHeaders, (CFTypeRef) headers);
 
    // for good measure, make the call follow redirects.
    WSMethodInvocationSetProperty(rpcCall,    kWSHTTPFollowsRedirects, kCFBooleanTrue);
    WSMethodInvocationSetProperty(rpcCall, kWSSOAPMethodNamespaceURI, (CFStringRef) nameSpace);
 
    NSDictionary *result = (NSDictionary *) (WSMethodInvocationInvoke(rpcCall));
   
    if (WSMethodResultIsFault ((CFDictionaryRef) result)) {
      [lblAnswer setStringValue: @"Error"];
    }
    else
    {      
      [lblAnswer setStringValue: [result objectForKey:@"ProductResult"]];
    }
}
@end
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.