Subscribe ( )

iPhone Programming Tutorials

 

Forum

If you're new here, you may want to subscribe to my RSS feed. Thanks for visiting!

You must be logged in to post Login Register

Search 

Web Service || SOAP || Tutorial

User Post

7:18 pm
October 10, 2008


Noob

posts 2

1

OK, so I'm working on only my 2nd iPhone app ever and really needed the ability to use WebServices. Unfortunately I did not see any helper classes for SOAP, which my web services will be using, so I had to do some research and found out I would have to do it, what I would call, manually.


I come from a C# programming environment and really .NET spoils you. They make everything soooo easy. So easy maybe it's bad. Anyways, this statement is also a warning. I'm very new to Objective C and Mac programming so I may make some mistakes. Please don't take what you read as how it NEEDS to be done because maybe it's not. It's what I found and it works. I also may have some memory leaks. I'm getting better but I'm use to a garbage collection type system. If you see an error, please point it out so I can learn from mistakes!


So lets get started:


First, lets browse to a Webservice I created specifically for this tutorial:

You will notice a “Hello” method that is clickable. Go ahead and click on hello. You are now seeing the SOAP message POSTS and RESULTS for SOAP 1.1 and 1.2. I'm doing a request using SOAP 1.1. Keep this page handy as we will be directly using it to create our message to POST to the above web address.


Second,

Here some coding YOU need to do. Create a View base application and add a Label, Textbox, and Button. Make sure you make all your connections and make sure to create a click method for your button. In your Controller header file add:


NSMutableData *webData;

NSMutableString *

NSXMLParser *xmlParser;

BOOL *recordResults;


@property( nonatomic, retain) NSMutableData *webData;

@property( nonatomic, retain) NSMutableString *webResults;

@property( nonatomic, retain) NSXMLParser *xmlParser;

@property( assign ) BOOL *recordResults;


Now lets do some coding to our ClickButton Method:

**Don't forget this: @synthesize name, greeting, webData, webResults, xmlParser, recordResults;


//Button Click Method

- (IBAction) buttonClick: (id) sender

{

recordResults = FALSE;

/*Setup our soap xml message. Remeber to use escape char for quotes. So when you need a quotation

//mark in a string do: \”

//list of char needing to be escaped:

//Read basics of SOAP:

I'm also adding NewLines \n, I doubt this is necessary but easier to understand and look ad when logging.


First we will be creating the SOAP message. This is the XML code that is located in the POST message for SOAP 1.1 in the web page we recently browsed to.

We will change <name>string</string> to <name>%@</name>. This basically says it is requesting an input of a string in that element.

We will also use NSString stringWithFormat, and pass in the var name.text which is the name of my textbox.

*/

NSString *soapMessage = [NSString stringWithFormat:

@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"

"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"

"<soap:Body>\n"

"<Hello xmlns=\"http://viium.com/\">\n"

"<name>%@</name>\n"

"</Hello>\n"

"</soap:Body>\n"

"</soap:Envelope>\n", name.text

];


//Log it so we can see it in debug mode

NSLog(soapMessage);


//Web service I setup to do this test application. 

//Plug for my website which isn't much:

NSURL *url = [NSURL URLWithString:@"http://viium.com/WebService/HelloWorld.asmx"];

//Setup the web request with the url we created

NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];

//need length of the soap xml for the header in the html file

NSString *msgLength = [NSString stringWithFormat:@"%d", [soapMessage length]];

/*

This is where we add our header info for an HTML message. This is required so that the service knows you are sending a SOAP message.

POST /WebService/HelloWorld.asmx HTTP/1.1

  Host: viium.com

  Content-Type: text/xml; charset=utf-8

  Content-Length: length

  SOAPAction: “http://viium.com/Hello”

add our htm headers which is a must for a WebService*/


[theRequest addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];

    [theRequest addValue:@"http://viium.com/Hello" forHTTPHeaderField:@"SOAPAction"];

[theRequest addValue:msgLength forHTTPHeaderField:@"Content-Length"];

//We are doing a HTML POST

[theRequest setHTTPMethod:@"POST"];

//set the HTML Body, which is the XML Soap message we created above. Needs to be UTF8 encoding

//Needs to be of type Data

[theRequest setHTTPBody: [soapMessage dataUsingEncoding: NSUTF8StringEncoding]];


// Our Message is now fully created and can now be sent to the WebService. Create the connection with the request

// and start loading the data, also set delegate to self

NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

if (theConnection) 

{

// Create the NSMutableData that will hold

webData=[[NSMutableData data] retain];

else 

{

// inform the user that the download could not be made

NSLog(@”ERROR”);

}

//Make keyboard go away!!!

[name resignFirstResponder];

}


/* Once the connection is made and no errors occur, it will start hitting the NSMutableURLRequest Delegates. We will now set this up */


(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response

{

    // this method is called when the server has determined that it

    // has enough information to create the NSURLResponse

//clear any data that may be around.

[webData setLength:0];

}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

{

    // append the new data to the webData

    // webData is declared in the header file

    [webData appendData:data];

}

- (void)connection:(NSURLConnection *)connection

  didFailWithError:(NSError *)error

{

    // release the connection, and the data object if error occurs

    [connection release];

    [webData release];

    // inform the user

    NSLog(@”Connection failed! Error - %@ %@”,

          [error localizedDescription],

          [[error userInfo] objectForKey:NSErrorFailingURLStringKey]);

}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection

{

    //We are done receiving the results from the Web Service.

//Manipulate data here and release Data nd Connection

NSLog(@”Succeeded! Received %d bytes of data\n”,[webData length]);

NSString *theXml = [[NSString alloc]initWithBytes:[webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

    NSLog(theXml);

[theXml release];


//release if xml is already init

if( xmlParser )

{

[xmlParser release];

}

//Parse received xml from web service

//xmlParse declared in the header


xmlParser= [[NSXMLParser alloc] initWithData: webData];

[xmlParser setDelegate:self];

    [xmlParser setShouldResolveExternalEntities:YES];

[xmlParser parse];

// release the connection, and the data object

    [connection release];

    [webData release];

}


/*Once the Request is done getting a response the data, if no error occurs, is passed into the XMLParser. We allocate it with the webData and set the delegate to

self. Now we need to setup the XML Parser delegates methods which to the actual parsing of the xml. In order to parse it correctly we have to go back to the web service 

web page and look at what the response xml will look like:

<?xml version=”1.0″ encoding=”utf-8″?>

<soap:Envelope xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope/”>

  <soap:Body>

    <HelloResponse xmlns=”http://viium.com/”>

      <HelloResult>string</HelloResult>

    </HelloResponse>

  </soap:Body>

</soap:Envelope>

From this we can tell what element we are looking for, <HelloResult> and the type of data it returns: string


- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict 

{

    //We check the results XML for HelloResult element. Once we find it, we set recordResults to TRUE

//We do this so foundCharacters method know is needs to start appending the data.

if ( [elementName isEqualToString:@"HelloResult"]) 

{

        if (!webResults)

{

//webResults is declared in header file

webResults = [[NSMutableString alloc] init];

}

recordResults = TRUE;

        return;

    }

}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string 

{

    //if at the HelloResult element, record the data to NSMutableString

if( recordResults )

{

[webResults appendString:string];

}

}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {

    

//We set our label, release our objects, and set record Results back to False since we hit the 

//End Element of Hello Results

//Release xmlParser in the dealloc method

//greeting is the label we setup.

if ( [elementName isEqualToString:@"HelloResult"] ) 

{

greeting.text = webResults;

[webResults release];

webResults = nil;

recordResults = FALSE;

return;

}

}


//release our xmlParser

- (void)dealloc 

{

[xmlParser release];

[super dealloc];

}

Download run-able source here: http://viium.com/WebService/HelloWorld.zip

2:09 pm
October 11, 2008


ThomasJaeger

Noob

posts 2

2

Thanks for posting this. The download link to the zip file does not work. Can you fix that? Thanks again.

9:10 pm
October 11, 2008


Noob

posts 2

3

Opps. Won't let me edit the post so here you go: 


Excuse all the grammar mistakes as well. I wrote this up just before bed and when re-reading through it…..well it's bad. There must be a time limit on when you can edit posts. Kinds sucks for me but O well.

3:25 pm
October 13, 2008


ThomasJaeger

Noob

posts 2

4

Thanks for your effort. I'll try it out and see if it can help me with my stab at iPhone and .Net webservices. 

11:27 am
October 14, 2008


Admin

brandontreb

posts 87

5

Clarke,


If you do a complete writeup of this tutorial with screenshots (or a screencast), I would love to post them on iCodeBlog as a tutorial.  I will post it from your username (Clarke76) and provide links to your website in the tutorial.


Consider it and let me know.  You can email me . Thanks for contributing this to the community.

3:54 am
October 20, 2008


AdeC

iCoder

posts 17

6

I agree with Brandon. Posts like this little gem shouldn't be 'hidden away' in the forum. We need these visible in the Tut section.

Please consider Brandons offer. It can only serve to make this a better site for everyone.

Like you, Im from a .Net background (recently switched to Mac and iPhone). Stuff like this is invaluable.

Thanks

1:09 am
February 18, 2009


iCoder

Gent

posts 6

7

Is there no way to do web services without building your HTTP request in strings and then posting that manually? It seems so raw and ugly. Surely there are WS libraries for this?

12:38 pm
February 24, 2009


rpl

Noob

posts 3

8

I tried to receive data from a webservice which is almost the 

 same as shown in the example. But I have an issue when coming 

 to parsing the data from the response from the server.


 The response looks like this:


 < ?xml version=”1.0″ encoding=”utf-8″? >

 < soap:Envelope xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” 

 xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope/” >

 < soap:Body >

 < GetStopsSuggestionsResponse xmlns=”http://www.blabla.se/” >

 < GetStopsSuggestionsResult>string</GetStopsSuggestionsResult >

 < /GetStopsSuggestionsResponse >

 < /soap:Body >

 < /soap:Envelope >


 The issue in this case is that the string that is returned from the 

 server is another XML file, where the format is not as it should be. 

 The < bracket is shown as &lt; and> is &gt;

 When trying to parse this string with the NSXMLParser function, I am 

 not able to parse it as I want to due to the brackets. Looked for a 

 solution for this, but only found something called 

 ”CFXMLCreateStringByUnescapingEntities”. But this give me another 

 problem, something called CFStringRef. I do not know how I can use 

 this function to get the XML format from the string, so it is 

 possible to parse the data and present it in the application.


 As I understand the NSXMLParser needs a NSObject, not a string, and 

 my question is then, how can I use the unescaping entities function 

 and use it in the parser?


Thanks in advance.

1:17 am
February 25, 2009


iCoder

Gent

posts 6

9

rpl:

Firstly, terminology matters. Strings can be many things; you can have c-strings (arrays of chars), you can have NSString objects and CFStringRef’s.

Now, obviously NSString’s are objects, as they are subclasses of NSObject.

So, if you look in the docs for NSString and check the description, you’ll notice the following interesting paragraph:

NSString is “toll-free bridged” with its Core Foundation counterpart, CFString (see CFStringRef). This means that the Core Foundation type is interchangeable in function or method calls with the bridged Foundation object. Therefore, in a method where you see an NSString * parameter, you can pass a CFStringRef, and in a function where you see a CFStringRef parameter, you can pass an NSString instance (you cast one type to the other to suppress compiler warnings). This also applies to your concrete subclasses of NSString. See Interchangeable Data Types for more information on toll-free bridging.

Which basically means, if you have a CFStringRef, you basically have an NSString* under a different name; so just cast it to (NSString*).

That should get you what you need using CFXMLCreateStringByUnescapingEntities.

Also notice how NSXMLParser has a method called “shouldResolveExternalEntities”.

4:34 am
February 25, 2009


rpl

Noob

posts 3

10

lhunath:

This is my code for the moment:

-(void)connectionDidFinishLoading:(NSURLConnection *)connection

{

NSLog(@”DONE. Received Bytes: %d”, [webData length]);

NSString *theXML = [[NSString allocinitWithBytes: [webDatamutableByteslength:[webData lengthencoding:NSUTF8StringEncoding];

// NSString* sI = (NSString*)CFXMLCreateStringByUnescapingEntities(NULL, (CFStringRef)theXML, NULL);


NSLog(theXML);

// NSLog(sI);


[theXML release];

//[sI release];


ifxmlParser )

{

[xmlParser release];

}


xmlParser = [[NSXMLParser allocinitWithDatawebData];

[xmlParser setDelegateself];

[xmlParser setShouldResolveExternalEntitiesYES];

[xmlParser parse];


[connection release];

[webData release];

}


As you can see I am using the CFXMLCreateStringByUnescapingEntities, and I have also tried to cast it so the sI is accepted in 

xmlParser = [[NSXMLParser allocinitWithData: ………….];

But it doesn't work. You also see in the code that I am using the

[xmlParser setShouldResolveExternalEntitiesYES];

but how do I use the ShouldResolveExternalEntities method?


The reponse from the server looks like this:


<?xml version=”1.0″ encoding=”utf-8″?>

<string xmlns=”http://www.blabla.se/”>&lt;?xml version=”1.0″ encoding=”utf-16″?&gt;

&lt;root&gt;

  &lt;suggestions&gt;

    &lt;items&gt;

      &lt;item stop_id=”02001070″ shortcut=”" stop_type=”H”&gt;

        &lt;friendly_name&gt;&lt;![CDATA[Centralstationen, GÖTEBORG (Hållplats)]]&gt;&lt;/friendly_name&gt;

        &lt;stop_name&gt;&lt;![CDATA[Centralstationen]]&gt;&lt;/stop_name&gt;

        &lt;county&gt;&lt;![CDATA[GÖTEBORG]]&gt;&lt;/county&gt;

      &lt;/item&gt;

    &lt;/items&gt;

  &lt;/suggestions&gt;

&lt;/root&gt;</string>

I assume that the foundCDATA method can be used if the string can be interpret in a correct way?!
Thanks in advance.

3:20 am
March 2, 2009


rpl

Noob

posts 3

11

Please help me! Is there more info you need regarding my problem? 

3:51 pm
March 25, 2009


linwe

Noob

posts 2

12

Hey rpl, 

I see you are doing an extra cast to NSStringRef whilst your object is already an NSString, won't it work without the extra cast ?

It's that I had exactly the same problem, my soap service reply looked like

&lt;/test>howdy!</test>


Difference, mine seemed to only have problems on the first “<”. I added the following line which resolves that issue
theXML = CFXMLCreateStringByUnescapingEntities(NULL, theXML, NULL); 

I see that your result from NSLogging is only your own XML message response by your webservice.  The reply I'm getting from my service is slightly different, it puts my response xml within a soap message and I don't want that!

However, when I call the exact same method from within Java, the xml comes out just fine.  (Now, might be that jax parsed the soapmessage away, not sure tho)  Is there any way to have the sdk do this for me ?  Cause I'm missing that….

1:05 pm
May 6, 2009


icodeguru

Noob

posts 2

13

Hello,

I am also having same issue. 

The issue is that the string that is returned from webservice call is in XML file, where the format is not as it should be. The < bracket is shown as &lt; and> is &gt;

Any suggestions would be greatly appreciated.


About the iCodeBlog forum

Currently Online:

1 Guest

Maximum Online: 44

Forums:

Groups: 2

Forums: 6

Topics: 325

Posts: 768

Members:

There are 705 members

There are 1 guests


Brandon has made 87 posts

Top Posters:

bobcubsfan - 54

crazyiez - 30

VertigoSol - 26

Uhu - 17

AdeC - 17

Administrator: Brandon | Moderators: VertigoSol


© - Version 3.1.4 (Build 357)