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

MrCrispy

macrumors member
Original poster
Apr 10, 2008
71
0
Jacksonville, Florida
ok, so I'm just experimenting with some development. I'm trying to connect to a very simple .NET web service I created. Basically, I feed it a number between 1 and 3 and it sends me back a string telling me what number I fed it. Like I said...simple.

The documentation for dealing with web services is...limited..to say the least. I've pieced together what I can and have come up with the following:

Code:
- (IBAction)fetchResults:(id)sender
{
	NSString *ddlValue = [ddlNumb stringValue];
	
	NSLog(@"DDLValue = %@", ddlValue);
	
	WSMethodInvocationRef rpcCall;
	NSURL *rpcURL = [NSURL URLWithString: @"http://test.dragonden.net/service.asmx"];
	NSString *methodName = @"BringIt";
	NSDictionary *params = [NSDictionary dictionaryWithObject: ddlValue forKey: @"x"];
	NSDictionary *result;
	
	rpcCall = WSMethodInvocationCreate((CFURLRef) rpcURL, (CFStringRef) methodName, kWSXMLRPCProtocol);
	
	WSMethodInvocationSetParameters (rpcCall, (CFDictionaryRef) params, NULL);
	
	result = (NSDictionary *) (WSMethodInvocationInvoke(rpcCall));
	
	NSLog(@"result = %@", result);
	
	if (WSMethodResultIsFault ((CFDictionaryRef) result)) {
		NSLog(@"result = %@", [result objectForKey: (NSString *) kWSFaultString]);
		[lblResult setStringValue: @"Error"];
	} else {
		[lblResult setStringValue: [result objectForKey: (NSString *) kWSMethodInvocationResult]];
	}
		
}

It doesn't throw up any errors on the compile but when I run it and hit the button, the WSThingy results come back as

Code:
"/FaultCode" = -65794;
    "/FaultExtra" =     {
        domain = -1;
        error = -65795;
        msg = "Can't find a 'methodResponse' element";
    };
    "/FaultString" = "/CFStreamFault";
    "/kWSHTTPResponseMessage" = <NSCFType: 0x1a8900>;
    "/kWSResultIsFault" = 1;

Searching for anything in that error block has proven to be fruitless. Can anyone see something that I'm missing? I'm hoping its something along the lines of "lol you noob. you forgot a ? at the end of your string. Go back to automator!!!11!!!!oneone"
 

Sander

macrumors 6502a
Apr 24, 2008
521
67
I haven't tried consuming WebServices in Objective-C yet, but I notice you're using kWSXMLRPCProtocol. Are you sure this is OK? SOAP is not the same thing as XMLRPC.

Have you successfully talked to your WebService from a different client (say, also written in .NET)? In other words, are you sure your WebService is OK?

By the way, using the word "simple" in the same sentence as "SOAP"? You have got to read this :)
 

MrCrispy

macrumors member
Original poster
Apr 10, 2008
71
0
Jacksonville, Florida
I did verify that the service was working. When I tried using the ksoapwhatevers protocol thingy (those are the official technical terms), it kept throwing back even more errors that didn't show up on any google search. One of the few articles I did find said that the kxmlrpc was the best way to go when dealing with .net services. Come to think of it though, he didn't really provide any working code for his tutorial either.

According to those articles you posted, I should give up all hope and just buy a hampster. Admittedly, that's becoming a more and more enticing option.

The geek in me won't let it go, however.

P.S. After spending 3 hours trying to get the generated code working, the first person that says "just use the WSMakeStub thing. Its sooooo easy" gets a really dirty look from me
 

MrCrispy

macrumors member
Original poster
Apr 10, 2008
71
0
Jacksonville, Florida
Ok, an update...

I think I'm getting a bit closer. I'm getting a response from my service now.
I made a few changes to the code. First, I went to the kWSSOAP2001Protocol and added a string to put in the appropriate headers.

Still missing something though. Its not passing my value for the key X and so my service is kicking back the error code. At the same time, I'm having a bit of trouble figuring out how to pull just the result code out instead of the whole return string.

So here's the latest code:
Code:
- (IBAction)fetchResults:(id)sender
{
	NSString *ddlValue = [ddlNumb stringValue];
	
	NSLog(@"DDLValue = %@", ddlValue);
	
	WSMethodInvocationRef rpcCall;
	NSURL *rpcURL = [NSURL URLWithString: @"http://test.dragonden.net/service.asmx"];
	NSString *methodName = @"BringIt";
	NSDictionary *params = [NSDictionary dictionaryWithObject: ddlValue forKey: @"x"];
	NSDictionary *result;
	NSDictionary *headers = [NSDictionary	dictionaryWithObject:@"http://test.dragonden.net/BringIt" forKey:@"Soapaction"];
	
	rpcCall = WSMethodInvocationCreate((CFURLRef) rpcURL, (CFStringRef) methodName, kWSSOAP2001Protocol);
	
	
	WSMethodInvocationSetParameters (rpcCall, (CFDictionaryRef) params, NULL);
	WSMethodInvocationSetProperty(rpcCall, (CFStringRef) kWSHTTPExtraHeaders, (CFTypeRef) headers);
	
	
	
	result = (NSDictionary *) (WSMethodInvocationInvoke(rpcCall));
	
	NSLog(@"result = %@", result);
	
	if (WSMethodResultIsFault ((CFDictionaryRef) result)) {
		NSLog(@"result = %@", [result objectForKey: (NSString *) kWSFaultString]);
		[lblResult setStringValue: @"Error"];
	} else {
		[lblResult setStringValue: [result objectForKey: (NSString *) kWSMethodInvocationResult]];
	}
		
}

And the result I'm getting back now. I'm trying to just get the "dude, wtf" instead of the whole "{ bringit result etc"

Code:
2008-07-03 14:18:51.019 WebService[2184:10b] result = {
    "/Result" =     {
        BringItResult = "dude, wtf?";
    };
    "/kWSHTTPResponseMessage" = <NSCFType: 0x128640>;
    BringItResult = "dude, wtf?";
}

So close and yet so far...
 

luckylefty01

macrumors member
Apr 8, 2008
32
0
I think you've actually got it. That response ( dictname { key = value} ) is just how %@ prints a dictionary (which is what result currently is).

So I think all you need to do is

Code:
[[result objectForKey:@"/Result"] objectForKey:@"BringItResult"];

or whatever to get the response out of the nested dictionaries.
 

mcgomer

macrumors newbie
Feb 3, 2008
16
0
Never give up!!

I was more trying to let you know that others have tried and were having the same problems you were. Just to show that it wasnt a simple thing that you are doing wrong.

Coming from .Net also, with Web Services being so easy to do there, I also would have thought that these things should be no brainers.

Paul
 

Sayer

macrumors 6502a
Jan 4, 2002
981
0
Austin, TX
Apple initially made a lot of changes to AppleScript to support SOAP/RPC. The procedural api called Web Services Core is the actual implementation that sits under AppleScript's support I guess.

What is lacking is a higher-level Cocoa-type framework to wrap up all the nitty-gritty mechanics.

I guess it's just not much of a priority at Apple any more, and you can easily set up a web server to handle similar XML-based requests using any backend scripting/programming language you want.

This is actually an area I am working in now with my new job. A Mac app talks to an embedded web server in a device that sends back straight XML (most of the time) and we parse out the bits we need. All done using standard http POST and GET functions.
 

MrCrispy

macrumors member
Original poster
Apr 10, 2008
71
0
Jacksonville, Florida
Well, I've got it talking, reading the returned value. The problem is that regardless of what I've done, my program doesn't seem to be sending the value I'm setting for x. I'm spinning my wheels here now. I've a bunch of stuff I found on developer.apple.com but it doesn't seem to be helping anything. I am getting the "Dude, wtf" error message from the web service but at least its the one from the service. That's what's generated when it doesn't get the number 1, 2 or 3.

Here's the latest code:

Code:
- (IBAction)fetchResults:(id)sender
{
	NSString *ddlValue = [ddlNumb stringValue];
	
	NSLog(@"DDLValue = %@", ddlValue);
	
	WSMethodInvocationRef rpcCall;
	NSURL *rpcURL = [NSURL URLWithString: @"http://test.dragonden.net/service.asmx"];
	NSString *methodName = @"BringIt";
	NSString *nameSpace = @"http://test.dragonden.net/";
	NSDictionary *params = [NSDictionary dictionaryWithObject:@"2" forKey: @"x"];
	NSArray *paramOrder = [NSArray arrayWithObject:@"param"];
	NSDictionary *result;
	NSDictionary *headers = [NSDictionary dictionaryWithObject:@"http://test.dragonden.net/BringIt" 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);
	
	
	
	
	result = (NSDictionary *) (WSMethodInvocationInvoke(rpcCall));
	
	NSLog(@"result = %@", result);
	
	if (WSMethodResultIsFault ((CFDictionaryRef) result)) {
		NSLog(@"result = %@", [result objectForKey: (NSString *) kWSFaultString]);
		[lblResult setStringValue: @"Error"];
	} else {		
		[lblResult setStringValue: [result objectForKey:@"BringItResult"]];
	}
		
}
 

MrCrispy

macrumors member
Original poster
Apr 10, 2008
71
0
Jacksonville, Florida
Wow...420+ views and still no suggestions on the last of my problems (at least my problems with this code. Gonna take a shrink to take care of the others).

I spent the whole weekend working on this and let's just hope the hair I've pulled out will grow back.

The problem seems to be with these two lines:
Code:
NSDictionary *params = [NSDictionary dictionaryWithObject:@"2" forKey: @"x"];

and
Code:
WSMethodInvocationSetParameters (rpcCall, (CFDictionaryRef) params, (CFArrayRef)paramOrder);


Something in there isn't sending the 2 along with the rest of the stuff.
 

jimothyGator

macrumors 6502
Jun 12, 2008
411
1,355
Atlanta, GA
SOAP Client

There's a proposed renaming for SOAP, as it's now realized that's it's neither simple nor object-oriented. The proposed new name is Complex Remote Access Protocol, or CRAP.

In the meantime, though, SOAP Client, a native Mac OS X client for SOAP testing, is now open source. I used this about two years ago when I was testing SOAP services, and it's pretty handy.

http://ditchnet.org/soapclient/
 

MrCrispy

macrumors member
Original poster
Apr 10, 2008
71
0
Jacksonville, Florida
There's a proposed renaming for SOAP, as it's now realized that's it's neither simple nor object-oriented. The proposed new name is Complex Remote Access Protocol, or CRAP.

In the meantime, though, SOAP Client, a native Mac OS X client for SOAP testing, is now open source. I used this about two years ago when I was testing SOAP services, and it's pretty handy.

http://ditchnet.org/soapclient/

I agree with the name change. I've been using the soap client and everything seems to work under that which has only served to frustrate me more.
 

luckylefty01

macrumors member
Apr 8, 2008
32
0
Wow...420+ views and still no suggestions on the last of my problems (at least my problems with this code. Gonna take a shrink to take care of the others).

I spent the whole weekend working on this and let's just hope the hair I've pulled out will grow back.

The problem seems to be with these two lines:
Code:
NSDictionary *params = [NSDictionary dictionaryWithObject:@"2" forKey: @"x"];

and
Code:
WSMethodInvocationSetParameters (rpcCall, (CFDictionaryRef) params, (CFArrayRef)paramOrder);


Something in there isn't sending the 2 along with the rest of the stuff.

Are you sure that param order is supposed to contain "params" and not the names of the actual params you're sending in order? i.e.

Code:
paramOrder = [NSArray arrayWithObject:@"x"];

I did a bit of messing around with SOAP on OS X a while back, and that's more my recollection. I think it has to do with services needing the parameters specified in order, but NSDictionaries have no inherent order (thus one must be supplied somehow).
 

MrCrispy

macrumors member
Original poster
Apr 10, 2008
71
0
Jacksonville, Florida
Are you sure that param order is supposed to contain "params" and not the names of the actual params you're sending in order?

You Sir win the Internet! I KNEW it was something flat out stupid I was doing! A thousand thanks to you and everyone else who helped me through this exorcise in self torture.

Here's the final code for anyone who was playing along at home:
Code:
- (IBAction)fetchResults:(id)sender
{
	NSString *ddlValue = [ddlNumb stringValue];
	
	NSLog(@"DDLValue = %@", ddlValue);
	
	WSMethodInvocationRef rpcCall;
	NSURL *rpcURL = [NSURL URLWithString: @"http://test.dragonden.net/service.asmx"];
	NSString *methodName = @"BringIt";
	NSString *nameSpace = @"http://test.dragonden.net/";
	NSDictionary *params = [NSDictionary dictionaryWithObject:ddlValue forKey: @"x"];
	NSArray *paramOrder = [NSArray arrayWithObject:@"x"];
	NSDictionary *result;
	NSDictionary *headers = [NSDictionary dictionaryWithObject:@"http://test.dragonden.net/BringIt" 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);
	
	
	
	
	result = (NSDictionary *) (WSMethodInvocationInvoke(rpcCall));
	
	NSLog(@"result = %@", result);
	
	if (WSMethodResultIsFault ((CFDictionaryRef) result)) {
		NSLog(@"result = %@", [result objectForKey: (NSString *) kWSFaultString]);
		[lblResult setStringValue: @"Error"];
	} else {		
		[lblResult setStringValue: [result objectForKey:@"BringItResult"]];
	}
		
}
 

luckylefty01

macrumors member
Apr 8, 2008
32
0
You Sir win the Internet! I KNEW it was something flat out stupid I was doing! A thousand thanks to you and everyone else who helped me through this exorcise in self torture.

Can you ship it to me via UPS?

Glad I could help. I did a bit of messing around with that a couple weeks ago and found it rather obtuse and poorly almost undocumented myself.

If you happen to come across a good way to send complex structures (i.e. an employee structure with an NSString name and int ID members) please let me know:p. The only way I figured out was using WSSerializationOverride or whatever and basically creating the XML for the structure myself.
 

MrCrispy

macrumors member
Original poster
Apr 10, 2008
71
0
Jacksonville, Florida
Can you ship it to me via UPS?

Glad I could help. I did a bit of messing around with that a couple weeks ago and found it rather obtuse and poorly almost undocumented myself.

If you happen to come across a good way to send complex structures (i.e. an employee structure with an NSString name and int ID members) please let me know:p. The only way I figured out was using WSSerializationOverride or whatever and basically creating the XML for the structure myself.

I saw something somewhere about dumping the results into a core data thingy. That's a ways to go for me. You can bet I'll probably be back here again when I try to tackle that hellish beast.
 

HiRez

macrumors 603
Jan 6, 2004
6,265
2,630
Western US
Another option might be to just call into another language that's better suited to dealing with that sort of thing, such as Python, Ruby, or Perl (or possibly even AppleScript). Those are all built into OS X and they're quite easy to call from a Cocoa app.
 

lee1210

macrumors 68040
Jan 10, 2005
3,182
3
Dallas, TX
wsdl2java is pretty sweet for this sort of thing, too. It generates java classes for all of the types defined in the WSDL, then you can just use mutators to compose each type with primitives or other derived types. Making a particular web service call is not too much more difficult than calling anything else once you get the connection info set.

I know this doesn't help much, but if you were to consider using another language I thought i'd toss that out there.

-Lee
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.