Main Contents

[Objective-c] iPhone et Webservices SOAP

juillet 19, 2009

Voilà un tutorial qu’on m’a souvent demandé. Nous allons voir comment réaliser une application qui va utiliser un WebService simple, envoyer un message SOAP, recevoir la réponse et la parser pour afficher le résultat.


Il va y avoir 4 étapes :

  • Creation du message SOAP
  • Envoie du message SOAP
  • Réception de la réponse
  • Parser la réponse et l’afficher

I) Le WebService

J’ai crée un petit Webservice qui attend une chaîne et qui renvoie « Hello votre-chaine ». Un simple « HelloWorld » en quelque sorte. Il est accessible à l’url : http://www.jkraft.fr/ws/serveur.php

II) Interface Builder

Première étape on crée notre interface graphique. On part d’un modèle « View-based-application », je l’ai appelé « Soap ». On ajoute 3 UILabel, un UITextField et un UIButton pour que ça ressemble à ça .

SoapInterface

III) Xcode

Ensuite on ajoute notre code dans SoapViewController.h.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@interface SoapViewController : UIViewController {
	IBOutlet UITextField *maSaisie;
	IBOutlet UILabel *monLabel;
	NSMutableData *webData;
	NSMutableString *soapResults;
	NSXMLParser *xmlParser;
	BOOL xmlResults;
}
 
@property(nonatomic, retain) IBOutlet UITextField *maSaisie;
@property(nonatomic, retain) IBOutlet UILabel *monLabel;
@property(nonatomic, retain) NSMutableData *webData;
@property(nonatomic, retain) NSMutableString *soapResults;
@property(nonatomic, retain) NSXMLParser *xmlParser;
 
-(IBAction)envoyer;
@end

On n’oublie pas de retourner dans Interface Builder pour lier le Label à son IBOutlet et notre bouton à son action.

Ensuite on attaque le gros morceau SoapViewController.m.

On ajoute les synthetize qui vont bien :

@synthesize maSaisie, monLabel, webData, soapResults, xmlParser;

Puis on s’occupe de la fonction envoyer. C’est elle qui va créer le message soap et l’envoyer au Webservice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
-(IBAction)envoyer
{
	xmlResults = FALSE;
 
	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://www.jkraft.com/\">\n"
			 "<nom>%@</nom>\n"
			 "</Hello>\n"
			 "</soap:Body>\n"
			 "</soap:Envelope>\n", maSaisie.text ];
	NSLog(soapMessage);
 
	NSURL *url = [NSURL URLWithString:@"http://www.jkraft.fr/ws/serveur.php"];
	NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
	NSString *msgLength = [NSString stringWithFormat:@"%d", [soapMessage length]];
 
	[theRequest addValue: @"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
	[theRequest addValue: @"http://www.jkraft.com/Hello" forHTTPHeaderField:@"SOAPAction"];
	[theRequest addValue: msgLength forHTTPHeaderField:@"Content-Length"];
	[theRequest setHTTPMethod:@"POST"];
	[theRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];
	NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
	if( theConnection )
	{
		webData = [[NSMutableData data] retain];
	}
	else
	{
		NSLog(@"theConnection is NULL");
	}
	[maSaisie resignFirstResponder];	
}

Donc on créer notre message Soap ligne 5. De la ligne 18 à 26 on prépare la connexion, en définissant l’url et très important en préparant les bonnes entêtes (Content-type, SOAPAction).
Ligne 27 on lance la connexion, attention au delegate=self il implique de gérer la connexion et ses retours.
C’est pour cela qu’on ajoute ces fonctions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
	[webData setLength: 0];
}
 
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
	[webData appendData:data];
}
 
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
	NSLog(@"ERROR with theConkenction");
	[connection release];
	[webData release];
}
 
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
	NSLog(@"DONE. Received Bytes: %d", [webData length]);
	NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
	NSLog(theXML);
	[theXML release];
 
	if( xmlParser )
	{
		[xmlParser release];
	}
 
	xmlParser = [[NSXMLParser alloc] initWithData: webData];
	[xmlParser setDelegate: self];
	[xmlParser setShouldResolveExternalEntities: YES];
	[xmlParser parse];
 
	[connection release];
	[webData release];
}

Celle qui nous interesse le plus est connectionDidFinishLoading. Car une fois reçu la réponse, c’est ici qu’on va la traiter et dans notre cas parser le xml reçu.
Les lignes 21-22-23 ne servent que pour l’affichage dans les logs. Ligne 30 à 33 on parse notre réponse qui est webData. Attention encore une fois au [xmlParser setDelegate: self], il faut définir quelques fonctions supplémentaires qui font le boulot.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *)qName
   attributes: (NSDictionary *)attributeDict
{
	if( [elementName isEqualToString:@"Result"])
	{
		if(!soapResults)
		{
			soapResults = [[NSMutableString alloc] init];
		}
		xmlResults = YES;
 
	}
}
 
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
	if( xmlResults )
	{
		[soapResults appendString: string];
	}
}
 
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
	if( [elementName isEqualToString:@"Result"])
	{
		xmlResults = FALSE;
		monLabel.text = soapResults;
		[soapResults release];
		soapResults = nil;
	}
}

La ça se complique un peu. C’est 3 fonctions ouvrent chaque balise XML (ligne 1 à 13) regarde le nom de la balise (ligne 15 à 21) et s’il correspond à celui recherché (ici c’est Result) alors elle sauvegarde le résultat (ligne 19) et l’affiche ( ligne 28).
Attention à la gestion de la mémoire, ne pas oublier de release les objets devenus inutiles.
On peut maintenant compiler et tester l’application. Dans les logs vous devriez voir l’appel et la réponse en XML, et le résultat s’afficher dans le label.

Result

Les sources sont téléchargeables : soap.zip

Catégorie(s): Développement, Iphone, Objective-C | Comments (19)

19 Comments