This is an 'iffy' subject in general.  I can tell you that my approach is not 
what it appears that most have done.  

The first problem for me, most of the WebService is deal with are written in 
.NET (C#) and as such the WSMakeStubs doesn't actually work (if you have 
parameters).

The second, as you have discovered is that WSMakeStubs classes are not iPhoneOS 
usable.

In order to solve the problem, I went away from the generator tools (though I 
plan to create my own at some point) and do it myself.  I created a base class 
that is the SOAP request. 

//
//  SASoapRequest.h
//
//  Created by Andy Satori on 2/4/10.
//  Copyright 2010 druware software designs. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <libxml/xmlmemory.h>

@interface SASoapRequest : NSObject {
        // service properties
        NSString *methodName;
        NSString *nameSpace;
        NSString *serviceUrl;
        NSMutableArray *parameters;
        
        NSMutableString *soapRequestXml;
        NSMutableString *soapResponseXml;
        NSData *soapResponseData;
}

@property(nonatomic, retain) NSString *methodName;
@property(nonatomic, retain) NSString *nameSpace;
@property(nonatomic, retain) NSString *serviceUrl;

-(BOOL)createSoapRequest;
-(BOOL)postSoapRequest;

-(void)setParameters:(NSArray *)value;
-(NSArray *)parameters;
-(long)addParameter:(NSDictionary *)dictionary;

-(NSString *)soapRequestXml;
-(NSString *)soapResponseXml;
-(NSData *)soapResponseData;

@end

The implementation of this 'base' class is fairly straightforward:

//
//  SASoapRequest.m
//  TWHQMobile
//
//  Created by Andy Satori on 2/4/10.
//  Copyright 2010 druware software designs. All rights reserved.
//

#import "SASoapRequest.h"


@implementation SASoapRequest

@synthesize methodName;
@synthesize nameSpace;
@synthesize serviceUrl;

-(BOOL)createSoapRequest
{       
        soapRequestXml = [[NSMutableString alloc] init];
        [[soapRequestXml retain] autorelease];
        
        // document
        [soapRequestXml appendString:@"<?xml version=\"1.0\" 
encoding=\"utf-16\"?>\n"];

        // envelope
        [soapRequestXml appendString:@"<soap:Envelope "];
        [soapRequestXml 
appendString:@"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"; "];
        [soapRequestXml 
appendString:@"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"; "];
        [soapRequestXml 
appendString:@"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\";>\n"];
        
        // body
        [soapRequestXml appendString:@"\t<soap:Body>\n"];
        
        // method
        [soapRequestXml appendFormat:@"\t\t<%@ xmlns=\"%...@\">\n", 
self.methodName, self.nameSpace];
        
        // parameters
        
        //int i;
        //for (i = 0; i < [parameters count]; i++)
        for (NSDictionary *parameter in parameters)
        {
                // parameter = [parameters objectAtIndex:i] ;
                NSString *paramName = [parameter objectForKey:@"name"];
                NSString *paramValue = [parameter objectForKey:@"value"];
                
                [soapRequestXml appendFormat:@"\t\t\t<%@>%@</%@>\n", paramName, 
paramValue, paramName];
        }

        // close the tags
        [soapRequestXml appendFormat:@"\t\t</%@>\n", self.methodName];
        [soapRequestXml appendString:@"\t</soap:Body>\n"];
        [soapRequestXml appendString:@"</soap:Envelope>\n"];
        
        return ((soapRequestXml != nil) && ([soapRequestXml length] > 0));
}

-(BOOL)postSoapRequest
{
        NSURL *soapRequestUrl = [NSURL URLWithString:self.serviceUrl];
        
    // Use the private method setAllowsAnyHTTPSCertificate:forHost:
    // to not validate the HTTPS certificate.  This is used if you are using a 
        // testing environment and have a sample SSL certificate set up, this 
is not
        // a published api and will need to be removed before submission to the 
app store.
        [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[soapRequestUrl 
host]];
        

        NSMutableURLRequest *request = [NSMutableURLRequest 
requestWithURL:soapRequestUrl];
        NSString *soapAction = [NSString stringWithFormat:@"\"%@/%...@\"", 
self.nameSpace, self.methodName];
        
    [request addValue:@"text/xml; charset=utf-8" 
forHTTPHeaderField:@"Content-Type"];
    [request addValue:soapAction forHTTPHeaderField:@"SOAPAction"];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:[soapRequestXml 
dataUsingEncoding:NSUTF8StringEncoding]];
        
    // Create the connection
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request 
delegate:self];
        
        NSMutableData *myMutableData;

    // Check the connection object
    if(conn)
    {
                myMutableData=[[NSMutableData data] retain];
    }
        
    // Make this class the delegate so that the other connection events fire 
here.
        [NSURLConnection connectionWithRequest:request delegate:self];
        
    NSError *WSerror;
    NSURLResponse *WSresponse;
        
    // Call the xml service and return response into a MutableData object
    myMutableData = [[NSMutableData alloc] initWithData:[NSURLConnection 
sendSynchronousRequest:request returningResponse:&WSresponse error:&WSerror]];
        soapResponseData = [[NSData alloc]  initWithData:myMutableData];
        [soapResponseData retain];
        [myMutableData release];
        
        // parse the results and place them into an NSDictionary *
        
        NSMutableArray *soapResponse;
        soapResponse = [[NSMutableArray alloc] init];
        [soapResponse retain];
        
        soapResponseXml = [[NSString alloc] initWithData:soapResponseData 
encoding:NSUTF8StringEncoding];
        [soapResponseXml retain];
        xmlDocPtr doc = xmlParseMemory([soapResponseXml UTF8String], 
                                                                   
[soapResponseXml lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
        xmlNodePtr rootNode = xmlDocGetRootElement(doc);
        
        NSLog(@"%s", rootNode->name); // Envelope
        
        // loop all of the children ( there is only one, the Body Element )
        xmlNodePtr bodyNode = rootNode->children;
        
        NSLog(@"%s", bodyNode->name); // Body
        
        xmlNodePtr responseNode = bodyNode->children;
        
        NSLog(@"%s", responseNode->name); // Response
        
        // confirm that this the expected node MethodNameReponse
        NSString *expectedNodeName = [NSString 
stringWithFormat:@"%...@response", self.methodName];
        NSString *responseNodeName = [NSString stringWithFormat:@"%s", 
responseNode->name];
        
        if ([expectedNodeName compare:responseNodeName] == NSOrderedSame)
        {
                // iterate the result elements.
                xmlNodePtr resultNode = responseNode->children;
                
                while (resultNode != nil)
                {
                        NSLog(@"%s", resultNode->name); // Result
                        NSString *nodeValue = [[NSString alloc] 
initWithFormat:@"%s", resultNode->content];
                        NSMutableDictionary *dict = [[NSMutableDictionary 
alloc] init];
                        [dict setValue:nodeValue forKey:responseNodeName];
                        [soapResponse addObject:dict];
                        
                        resultNode = resultNode->next;
                }
        }
        
        return ([soapResponseData length] > 0); 
}

-(void)setParameters:(NSArray *)value
{
        if (parameters != nil)
        {
                [parameters release];
                parameters = nil;
        }
        
        parameters = [[NSMutableArray alloc] initWithArray:value];
        [parameters retain];
}

-(NSArray *)parameters
{
        return [[[NSArray alloc] initWithArray:parameters] autorelease];
}

-(long)addParameter:(NSDictionary *)dictionary
{
        long currentCount = 0;
        if (parameters == nil)
        {
                parameters = [[NSMutableArray alloc] init];
                [parameters retain];
        }
        
        currentCount = [parameters count];
        [parameters addObject:dictionary];
        if (currentCount = [parameters count]) 
        {
                return -1;
        }
        return [parameters count];
}


-(NSString *)soapRequestXml
{       
        if (soapRequestXml == nil)
        {
                return nil;
        }
        return [NSString stringWithString:soapRequestXml];
}

-(NSString *)soapResponseXml
{       
        if (soapResponseXml == nil)
        {
                if (soapResponseData == nil)
                {
                        return nil;
                }               
                
                soapResponseXml = [[NSString alloc] 
initWithData:soapResponseData encoding:NSUTF8StringEncoding];
        }
        return [NSString stringWithString:soapResponseXml];
}

-(NSData *)soapResponseData
{       
        if (soapResponseData == nil)
        {
                return nil;
        }
        return [NSData dataWithData:soapResponseData];
}

@end

This is boilerplate code, that is then inherited by the actual class that 
represents the SOAP service:

The following is an example class that implements the above:


//
//  TWHQLogin.h
//  TWHQMobile
//
//  Created by Andy Satori on 2/4/10.
//  Copyright 2010 druware software designs. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "SASoapRequest.h"


@interface TWHQLogin : SASoapRequest {
        // parameters
        NSString *userId;
        NSString *password;
        NSNumber *crmUserId;
        NSString *sessionId;
        
        BOOL      isValid;
        
        NSMutableString *inprocessString;
}

@property(nonatomic, retain) NSString *userId;
@property(nonatomic, retain) NSString *password;
@property(nonatomic, retain) NSNumber *crmUserId;
@property(nonatomic, retain) NSString *sessionId;
@property(readonly) BOOL isValid;

-(BOOL)validateUserId;
-(BOOL)validateSessionId;

@end


implemented as

//
//  TWHQLogin.m
//  TWHQMobile
//
//  Created by Andy Satori on 2/4/10.
//  Copyright 2010 druware software designs. All rights reserved.
//

#import "TWHQLogin.h"
#import <CommonCrypto/CommonDigest.h>


@implementation TWHQLogin

@synthesize userId;
@synthesize password;
@synthesize crmUserId;
@synthesize sessionId;
@synthesize isValid;

-(id)init
{
        self = [super init];
        if (self == nil) { return nil; }
        
        inprocessString = nil;
        
        isValid = NO;
        self.nameSpace = @"http://my.host.url/services/login/user.asmx";;
        self.serviceUrl = @"http://my.host.url/services/login/user.asmx";;
        
        return self;
}

-(BOOL)validateUserId
{
        isValid = NO;
        
        self.methodName = @"ValidateLogin";
        
        // set the parameters
        NSMutableDictionary *param;
        param = [[NSMutableDictionary alloc] init];
        [param setObject:@"sUserName" forKey:@"name"];
        [param setObject:self.userId forKey:@"value"];
        [param setObject:@"NSString" forKey:@"type"];
        [self addParameter:param];

        param = [[NSMutableDictionary alloc] init];
        [param setObject:@"sPassword" forKey:@"name"];
        [param setObject:self.password forKey:@"value"];
        [param setObject:@"NSString" forKey:@"type"];
        [self addParameter:param];
        
        param = [[NSMutableDictionary alloc] init];
        [param setObject:@"iCRMUserId" forKey:@"name"];
        [param setObject:@"-1" forKey:@"value"];
        [param setObject:@"NSString" forKey:@"type"];
        [self addParameter:param];

        // create the request
        [self createSoapRequest];
        
        // submit the reuqest
        if (![self postSoapRequest]) 
        {
                //      the login failed.
        }
        
        // review the results
        NSXMLParser *resultParser;
        resultParser = [[NSXMLParser alloc] initWithData:soapResponseData];
        [resultParser setDelegate:self];
        
        [resultParser parse];
        [resultParser release];
        
        
        if (isValid)
        {
                // build the session id
                NSString *sourceString = [[NSString alloc] 
initWithFormat:@"%@/%@", userId, password];
                sessionId = [self generateMD5Hash:sourceString];
        }

        return self.isValid;
}

-(BOOL)validateSessionId
{
        isValid = NO;

        self.methodName = @"ValidateSessionId";
        
        // set the parameters
        NSMutableDictionary *param;
        param = [[NSMutableDictionary alloc] init];
        [param setObject:@"sSessionId" forKey:@"name"];
        [param setObject:self.sessionId forKey:@"value"];
        [param setObject:@"NSString" forKey:@"type"];
        [self addParameter:param];
        
        // create the request
        [self createSoapRequest];
        
        // submit the reuqest
        if (![self postSoapRequest]) 
        {
                //      the login failed.
        }
        
        // review the results
        NSXMLParser *resultParser;
        resultParser = [[NSXMLParser alloc] initWithData:soapResponseData];
        [resultParser setDelegate:self];
        
        [resultParser parse];
        [resultParser release];
        
        if (isValid)
        {
                // build the session id
                NSString *sourceString = [[NSString alloc] 
initWithFormat:@"%@/%@", userId, password];
                sessionId = [self generateMD5Hash:sourceString];
        }       
        
        self.methodName = @"ValidateSessionId";

        return NO;
}

- (NSString *)generateMD5Hash:(NSString *)inputString
{
        const char rgbDigits[] = "0123456789abcdef";
        
        const char *cStr = [inputString 
cStringUsingEncoding:NSWindowsCP1252StringEncoding];
        unsigned char result[CC_MD5_DIGEST_LENGTH];
        CC_MD5( cStr, strlen(cStr), result );
        
        NSMutableString *sWork = [[NSMutableString alloc] init];
        for (long i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
        {
                [sWork appendFormat:@"%c%c", rgbDigits[result[i] >> 4],
                                         rgbDigits[result[i] & 0xf]];
        }
        return [NSString stringWithString:sWork];
}

#pragma mark -
#pragma mark Delegate calls

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    attributes:(NSDictionary *)attributeDict
{
        BOOL isElementOfNote = NO;
        
        // if this is not the response, go ahead and get out
        if ([elementName rangeOfString:@"ValidateLoginResult"].location != 
NSNotFound) 
        { 
                isElementOfNote = YES; 
        }
        if ([elementName rangeOfString:@"iCRMUserId"].location != NSNotFound) 
        { 
                isElementOfNote = YES; 
        }
        
        
        if (isElementOfNote) 
        {
                NSLog(@"starting Element: %@", elementName);
                
                if (inprocessString != nil)
                {
                        [inprocessString release];
                        inprocessString = nil;
                }
                inprocessString = [[NSMutableString alloc] init];
        }
}

- (void)parser:(NSXMLParser *)parser
 didEndElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI
 qualifiedName:(NSString *)qName
{
        BOOL isElementOfNote = NO;
        
        
        if ([elementName rangeOfString:@"ValidateSessionIdResult"].location != 
NSNotFound) 
        { 
                isValid = [inprocessString isEqualToString:@"true"];
                isElementOfNote = YES; 
        }
        
        
        if ([elementName rangeOfString:@"ValidateLoginResult"].location != 
NSNotFound) 
        { 
                isValid = [inprocessString isEqualToString:@"true"];
                isElementOfNote = YES; 
        }
        
        if ([elementName rangeOfString:@"iCRMUserId"].location != NSNotFound) 
        { 
                crmUserId = [[NSNumber alloc] initWithInteger:[inprocessString 
integerValue]];
                isElementOfNote = YES; 
        }
    
        if (isElementOfNote) 
        {
                NSLog(@"ending Element: %@", elementName);

        // Clear the text and key
        [inprocessString release];
        inprocessString = nil;
    }
}

// This method can get called multiple times for the
// text in a single element
- (void)parser:(NSXMLParser *)parser
foundCharacters:(NSString *)string
{
        NSLog(@"found characters: %@", string);
    [inprocessString appendString:string];
}

@end


This is more or less code that is in use, but I know that it hasn't been 
profiled or really put under the microscope for performance or memory leaks, 
but hopefully it will give you something you can work with.

The short version is that XML is a little painful on the iPhoneOS.  So we 
generate the Xml post in a string and then use the NSXMLParser on the back end 
to quickly break down the results.  A lot of the ideas and concepts here came 
from looking at how other people tackled the problems, but in the end, this was 
the solution I used.  It works, but I do not know if it is necessarily the best 
answer.


I apologize in advance for any formatting issues, this is composed on an iPad, 
and well, I am still getting used to it :-).

Andy 'Dru' Satori

On Apr 9, 2010, at 11:00 AM, Joanna Carter <cocoa...@carterconsulting.org.uk> 
wrote:

> Hi folks
> 
> Well, I've just spent the last few days, getting to grips with consuming a 
> web service.
> 
> I though I would try things out in an OS X app first, then move to the 
> iPhone, which is the end target.
> 
> So, I decided to use WSMakeStubs on the WSDL - it did a reasonable job, apart 
> from one or two minot bugs and niggles. Eventually, I got a test app working 
> to prove that I could read the service.
> 
> Then I thought I would move the app to iPhone, only to discover that the 
> classes generated by WSStubMaker would not compile and that I would have to 
> find another way to create the stub classes for an iPhone app.
> 
> Can I ask what is considered the best route to go?
> 
> Joanna
> 
> --
> Joanna Carter
> Carter Consulting
> 
> _______________________________________________
> 
> Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)
> 
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
> 
> Help/Unsubscribe/Update your Subscription:
> http://lists.apple.com/mailman/options/cocoa-dev/dru%40druware.com
> 
> This email sent to d...@druware.com
> 
_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to