Soap calls, WSMethodInvocationCreate

i want to make soap calls to web services and I found some objective C code that I translated to applescriptObjc.
I found a service that I could use to test the application (http://www.ripedevelopment.com/webservices/ZipCode.asmx)

I have a text field to input the city name and a button to trigger fetchResults_(sender)

The problem is that the following error occurs :

*** WSMethodInvocationCreate: unable to set argument 0 - the type {__CFURL=} is not supported.

The class of argument 0 is an NSURL so I don’t understand the error message.

Every help is welcome.


script testSoapAppDelegate
	property parent : class "NSObject"
	property cityName : "New York" -- bound value
	
	on applicationWillFinishLaunching_(aNotification)
		-- Insert code here to initialize your application before any files are opened 
	end applicationWillFinishLaunching_
	
	on applicationShouldTerminate_(sender)
		-- Insert code here to do any housekeeping before your application quits 
		return current application's NSTerminateNow
	end applicationShouldTerminate_
	
	on fetchResults_(sender)
		set cityNameStr to current application's NSString's stringWithString_(my cityName as string)
		
		set soapURL to current application's NSURL's URLWithString_("http://www.ripedevelopment.com/webservices/ZipCode.asmx")
		log soapURL
		
		set methodName to current application's NSString's stringWithString_("CityToZipCode")
		log methodName
		
		set nameSpace to current application's NSString's stringWithString_("http://www.ripedev.com/")
		log nameSpace
		
		set params to current application's NSDictionary's dictionaryWithObject_forKey_(cityNameStr, "City")
		log params
		
		set paramOrder to current application's NSArray's arrayWithObject_("City")
		log paramOrder
		
		set reqHeaders to current application's NSDictionary's dictionaryWithObject_forKey_("http://www.ripedev.com/CityToZipCode", "Soapaction")
		log reqHeaders
		
		set soapRequest to createSOAPRequest_({soapURL, methodName, nameSpace, params, paramOrder, reqHeaders})
		
		if soapRequest is not missing value then
			set theresult to current application's WSMethodInvocationInvoke(soapRequest)
			log "result = " & theresult
			
			if ((current application's WSMethodResultIsFault(theresult)) as boolean) then
				log "result = " & theresult's objectForKey_(current application's kWSFaultString)
			else
				-- 
			end if
		else
			log "ERROR  in createSOAPRequest_"
		end if
	end fetchResults_
	
	
	on createSOAPRequest_({soapURL, methodName, nameSpace, params, paramOrder, reqHeaders})
		log "=======================createSOAPRequest_========================="
		try
			set soapReq to current application's WSMethodInvocationCreate(soapURL, methodName, current application's kWSSOAP2001Protocol)
			
			-- set SOAP params
			current application's WSMethodInvocationSetParameters(soapReq, params, paramOrder)
			
			-- set method namespace
			current application's WSMethodInvocationSetProperty(soapReq, current application's kWSSOAPMethodNamespaceURI, nameSpace)
			
			-- Add HTTP headers (with SOAPAction header)
			current application's WSMethodInvocationSetProperty(soapReq, current application's kWSHTTPExtraHeaders, reqHeaders)
			
			-- for good measure, make the request follow redirects.
			current application's WSMethodInvocationSetProperty(soapReq, current application's kWSHTTPFollowsRedirects, current application's kCFBooleanTrue)
			-- set debug props
			current application's WSMethodInvocationSetProperty(soapReq, current application's kWSDebugIncomingBody, current application's kCFBooleanFalse)
			current application's WSMethodInvocationSetProperty(soapReq, current application's kWSDebugIncomingHeaders, current application's kCFBooleanFalse)
			current application's WSMethodInvocationSetProperty(soapReq, current application's kWSDebugOutgoingBody, current application's kCFBooleanFalse)
			current application's WSMethodInvocationSetProperty(soapReq, current application's kWSDebugOutgoingHeaders, current application's kCFBooleanFalse)
			return soapReq
		on error errmsg
			log "createSOAPRequest    " & errmsg
			return missing value
		end try
	end createSOAPRequest_
	
end script





Model: MacBook Pro
AppleScript: Xcode 4.2.1 Build 4D502
Browser: Safari 534.48.3
Operating System: Mac OS X (10.7)

Why don’t you use the XML-RPC/Soap library in the standard additions? I’m asking because I use XML-RPC from standard addition when I write in AppleScript and use WSMethodInvocation when I’m in Objective-C.

Hi DJ Bazzie Wazzie,

Which XML-RPC/Soap library of standard additions do you mean?

I code mainly in Xcode and use ApplescriptObjc and would like to keep all my code under the same environment .
When possible I should like to use functionality offered by the frameworks.
It is also a challenge to me to have it work (when possible) under ApplescriptObjc.

How can I put the objectiveC code in the application and call it from ApplescriptObjc?

thanks,

I believe DJ means the built-in AS support for soap: developer.apple.com/library/mac/#documentation/AppleScript/Conceptual/soapXMLRPC/chapter3/soapXMLRPC_scripts.html#//apple_ref/doc/uid/TP30001126-//apple_ref/doc/uid/20001723-BAJBFJBD

I’ve never used it, but I seem to recall it had some serious limitations.

I indeed used it in the past and did not get the expected results.
That’s one of the reasons I should like to have a better solution (if it exists!!).

But I am still wondering why I get the error :
*** WSMethodInvocationCreate: unable to set argument 0 - the type {__CFURL=} is not supported.

Thanks,

Although NSURL and CFURLRef are toll-free bridged, I’ve found that functions sometimes don’t seem to like being passed the NS equivalents. I don’t have a clue why. Also, keep in mind that not all functions are bridged.

Thanks Shane,
I think you are right.
I built the class in objective C and call it in ApplescriptObjC passing the needed parameters and that works just fine, even with my complete lack of programming in objective C (first time I tried!!).
It’s a pity that these issues occur, but we hope for a better future.

I haven’t used soap much but for XML-RPC there are no limitations.

I’d like to know how you got the ObjectiveC method to work. I’m trying to do a similar thing and getting a “unrecognized selector sent to object” error.
I’ve created the Webservice method using WSMakeStubs. I’ve copied them and the WSGeneratedObjects classes into my project. I’ve tried both ways of binding the applescript app delegate to the ObjectiveC classes as described in Shanes book but I still get the error. Problem is now I don’t know what is wrong. Is it the classes created by WSMakeStubs or is it the way I’m calling the web service classes.

Here is the Objective C code:
Interface file (.h)


//
//  SoapCallClass.h
//  testSoap
//
//  Created by kader on 09-01-12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface SoapCallClass : NSObject {
    NSString *eindpointpath;
    NSString *mymethodName;
    NSString *mynameSpace;
    NSString *myparams;
    NSArray *myparamsOrder;
    NSDictionary *myreqHeaders;
}

-(void) setEindpointpath: (NSString *) thepath;
-(void) setMethodName: (NSString *) methodName;
-(void) setNameSpace: (NSString *) nameSpace;
-(void) setParams: (NSString *) params;
-(void) setParamsOrder: (NSArray *) paramsOrder;
-(void) setReqHeaders: (NSDictionary *) reqHeaders;

-(NSString *) eindpointpath;
-(NSString *) methodName;
-(NSString *) nameSpace;
-(NSString *) params;
-(NSArray *) paramsOrder;
-(NSDictionary *) reqHeaders;

-(NSString *) getSoapXML;
    
@end


Implementation file (.m)


//
//  SoapCallClass.m
//  testSoap
//
//  Created by kader on 09-01-12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "SoapCallClass.h"

@implementation SoapCallClass

// Setters
-(void) setEindpointpath: (NSString *) thepath{
    eindpointpath = thepath;
}

-(void) setMethodName: (NSString *) methodName {
     mymethodName =  methodName ;
}
-(void) setNameSpace: (NSString *) nameSpace {
   mynameSpace =  nameSpace;
}
-(void) setParams: (NSString *) params {
   myparams = params;
}
-(void) setParamsOrder: (NSArray *) paramsOrder {
    myparamsOrder = paramsOrder;
}
-(void) setReqHeaders: (NSDictionary *) reqHeaders {
     myreqHeaders = reqHeaders ;
}

// Getters
-(NSString *) eindpointpath {
    return eindpointpath;
}
-(NSString *) methodName {
    return mymethodName;
}
-(NSString *) nameSpace {
    return mynameSpace;
}
-(NSString *) params {
    return myparams;
}
-(NSArray *) paramsOrder {
    return myparamsOrder;
}
-(NSDictionary *) reqHeaders {
    return myreqHeaders;
}

// Soap call method
-(NSString *) getSoapXML {
    
    NSURL *storeURL = [NSURL URLWithString:eindpointpath];
    
    WSMethodInvocationRef rpcCall;
    rpcCall = WSMethodInvocationCreate((CFURLRef) storeURL, (CFStringRef) mymethodName, kWSSOAP2001Protocol);
    
    WSMethodInvocationSetParameters (rpcCall, (CFDictionaryRef) myparams, (CFArrayRef)myparamsOrder);
	WSMethodInvocationSetProperty(rpcCall, (CFStringRef) kWSHTTPExtraHeaders, (CFTypeRef) myreqHeaders);
	
	// for good measure, make the call follow redirects.
    WSMethodInvocationSetProperty(rpcCall,    kWSHTTPFollowsRedirects, kCFBooleanTrue);
	
	WSMethodInvocationSetProperty(rpcCall, kWSSOAPMethodNamespaceURI, (CFStringRef) mynameSpace);
	
    // set debug props
    WSMethodInvocationSetProperty(rpcCall, kWSDebugIncomingBody,     kCFBooleanTrue);
    // WSMethodInvocationSetProperty(rpcCall, kWSDebugIncomingHeaders, kCFBooleanTrue);
    // WSMethodInvocationSetProperty(rpcCall, kWSDebugOutgoingBody,     kCFBooleanTrue);
    //  WSMethodInvocationSetProperty(rpcCall, kWSDebugOutgoingHeaders, kCFBooleanTrue);
    
    NSDictionary *result;
    result = (NSDictionary *) (WSMethodInvocationInvoke(rpcCall));
    
    // get HTTP response from SOAP request so we can see the status code
    CFHTTPMessageRef res = (CFHTTPMessageRef) [result objectForKey:(id)kWSHTTPResponseMessage];
    
    long resStatusCode = CFHTTPMessageGetResponseStatusCode(res);
    NSLog(@"Status Code %ld\n", resStatusCode);
    
    
    //CFHTTPMessageRef headers = (CFHTTPMessageRef)CFDictionaryGetValue (result,kWSHTTPResponseMessage);
    // NSLog(@"%@\n", res);
    // CFDictionaryRef  headerDict = CFHTTPMessageCopyAllHeaderFields(res);
    // NSLog(@"%@\n", headerDict);
    //  NSLog(@"%@\n",  [result objectForKey:@"/WSDebugInBody"]);
    
    if (WSMethodResultIsFault ((CFDictionaryRef) result)) {
        // NSLog(@"result = %@", [result objectForKey: (NSString *) kWSFaultString]);
        NSLog(@"\nERROR = %@\n\n",   [result objectForKey:@"/FaultString"]);
		// [result setStringValue: @"Error"];
        return  [result objectForKey:@"/FaultString"];
    } else {		
        // NSLog(@"result = %@\n",  [result objectForKey:@"GetOrderInfoboxResult"]);
    }
    return [result objectForKey:@"/WSDebugInBody"];
}

@end


ApplescriptObjc Code:


script testSoapAppDelegate
	property parent : class "NSObject"
	property orderNr : "New York" -- bound value
    
	on applicationWillFinishLaunching_(aNotification)
		-- Insert code here to initialize your application before any files are opened 
	end applicationWillFinishLaunching_
	
	on applicationShouldTerminate_(sender)
		-- Insert code here to do any housekeeping before your application quits 
		return current application's NSTerminateNow
	end applicationShouldTerminate_

	on test1_(sender)
        set starttime to time of (current date)
        set theOptions to ((current application's NSXMLDocumentTidyXML) as integer) + ((current application's NSXMLNodePrettyPrint) as integer) + ((current application's NSXMLNodePreserveWhitespace) as integer) + ((current application's NSXMLNodePreserveEmptyElements) as integer)

		set soapCall to current application's SoapCallClass's alloc()'s init()
       soapCall's setEindpointpath_("http://www.ripedevelopment.com/webservices/ZipCode.asmx")
        soapCall's setMethodName_("CityToZipCode")
        soapCall's setNameSpace_("http://www.ripedev.com/")
       	soapCall's setParams_({City:my orderNr})
        soapCall's setParamsOrder_({"City"})
       soapCall's setReqHeaders_({Soapaction:"http://www.ripedev.com/CityToZipCode"})
				
		set thexml to soapCall's getSoapXML()
		set myNSXMLDocument to current application's NSXMLDocument's alloc()'s initWithXMLString_options_error_(thexml, theOptions, missing value)
		
		set query_1 to "//*"
		set result_1 to  getQuery_xmlPath_(query_1, myNSXMLDocument)
		set startNode to missing value
		repeat with anode in result_1
			set node_name to (anode's |name|()) as string
			if node_name is "CityToZipCodeResult" then --Search for the start xml result  node
				set startNode to anode
				exit repeat
			end if
		end repeat
		if startNode is not missing value then
			set node_value to startNode's objectValue() as string
			if node_value is "" then
				log "not found"
			else
				set myNSXMLDocument to startNode
				-- use the xml
			end if
		else
			log "Error <CityToZipCodeResult> not found in XML"
		end if
        
           set totaltime to time of (current date)-starttime
        log "Total time = "&totaltime
	end test1_
	    

    on getQuery_xmlPath_(theQuery, myNSXMLDocument)
		set theOptions to ((current application's NSXMLDocumentTidyXML) as integer) + ((current application's NSXMLNodePrettyPrint) as integer) + ((current application's NSXMLNodePreserveWhitespace) as integer) + ((current application's NSXMLNodePreserveEmptyElements) as integer)
		-- Apply the contents of the XQuery text field to the current document
		set {theresult, theError} to myNSXMLDocument's objectsForXQuery_constants_error_(theQuery, missing value, reference)
		return theresult
	end getQuery_xmlPath_

end script



I hope this code can help you figure it out.

Yes that was really helpful. Got it working. Thanks

Has anybody been able to solve it for a life after 10.8? Since the methods are deprecated…

Hi Luc,

Here a bit of Objective C code. Hope it will get you the right way!.


	NSURL *theurl = [NSURL URLWithString:eindpointpath];
	NSString *action = [nameSpace stringByAppendingString:methodName];
	NSMutableURLRequest *req = [NSMutableURLRequest  requestWithURL:theurl
											cachePolicy: NSURLRequestReloadIgnoringCacheData
											timeoutInterval:timeInterval];
		
	[req setHTTPMethod: @"POST"];
	[req setHTTPBody: [requestBody dataUsingEncoding: NSUTF8StringEncoding]];
	[req addValue: [NSString stringWithFormat: @"\"%@\"", action] forHTTPHeaderField: @"SOAPAction"];
	[req addValue: @"text/xml; charset=utf-8" forHTTPHeaderField: @"Content-Type"];
	[req setCachePolicy: NSURLRequestReloadIgnoringCacheData];
		
	//NSString *errormsg = @"";
	NSURLResponse *response = nil;
	NSError *err = nil;
	NSData *resultsData = [NSURLConnection sendSynchronousRequest: req
									returningResponse: &response
									error: &err];