Ce tutoriel explique comment récupérer un fichier de données XML (un liste de capitales), traiter son contenu et afficher les différents éléments dans une liste défilante. Au clic sur un élément de la liste, on accèdera à la carte du monde avec les points correspondants aux capitales.
Classes:
VoyagesAppDelegate
RootViewController
MapViewController
Place
XMLParser
Interfaces visuelles:
MainWindow
RootView
MapView
Frameworks spécifiques:
MapKit.framework
Etape 1: créer un nouveau projet
Lancez Xcode, puis :
File > New project > Navigation-based Application
Donner un nom au projet (Voyages dans notre cas), spécifiez l'emplacement et poursuivez.

Test simulateur. Vous avez une application avec une liste vide, une barre de navigation vide.
Déclaration du tableau des capitales
Ajouter dans l'interface une nouvelle propriété.
NSMutableArray *places;
Donnez l'accès à cette propriété depuis l'extérieur de la classe mère.
@property (nonatomic, retain) NSMutableArray *places;
Dans la classe VoyagesAppDelegate, autorisez l'appel à la propriété "places" en ajoutant au début du fichier :
@synthesize places;
Cela vous évite de définir les méthodes get et set pour votre propriété.
Le tableau des capitales sera accessible depuis l'instance du délégué de l'application (VoyagesAppDelegate) et par conséquent pourra être modifié et lu depuis les autres classes de l'application.

Etape 2: parser le XML
Créez une nouvelle classe. Ctrl-Clic sur le dossier "Classes"
Add… > New file… et choisissez "Objective-C class".
Donnez lui le nom de "XMLParser". Le ficher ".h" sera automatiquement créé puis validez.
Dans le fichier ".h", créez un nouvel objet qui servira à retenir le noeud courant lu lors du parcage du XML.
NSMutableString *currentElementValue;
Donnez également les signatures des méthodes d'instance qui seront utilisées:
- (XMLParser *) initXMLParser; - (NSString *) cleanString:(NSString *)dirtyString;
La première méthode va initialiser le parseur XML et la seconde permettre de nettoyer les éléments lus (sauts de ligne, tabulations…).
Passez maintenant dans la classe XMLParser et créez la méthode initXMLParser.
- (XMLParser *) initXMLParser {
[super init];
return self;
}
Cette classe va être utilisée en tant que déléguée pour le parsage du XML. On doit donc implémenter les méthodes basique du parsage.
Commence par l'élément, lit l'élément, termine par l'élément.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
// Balise d'ouverture, lire les attributs présents
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
//Lecture du contenu
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
// actions à faire lors de la fin de la lecture d'un élément. Par ex enregistrer les données.
}
Utilisez le code suivant dans la classe pour nettoyer les chaines de caractères:
- (NSString *) cleanString:(NSString *)dirtyString {
NSString *cleanString = [NSString alloc];
cleanString = [dirtyString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
cleanString = [cleanString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
cleanString = [cleanString stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
cleanString = [cleanString stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]];
cleanString = [cleanString stringByTrimmingCharactersInSet:[NSCharacterSet nonBaseCharacterSet]];
return cleanString;
}
Nous devons maintenant utiliser notre parseur.
Dans le contrôleur de la vue principale (RootViewController), importer la classe XMLParser
#import "XMLParser.h"
Dans le "viewDidLoad", créer un nouvel objet de parseur XML:
NSXMLParser *leParseur;
Créez également un objet qui contiendra l'URL du fichier XML.
NSString *site = @";http://maps.google.fr/maps/ms?ivps=1&jsv=193c&msa=0&output=kml &msid=115881855563115194500.00046cc43e4b487c6faff";; NSURL *url = [[NSURL alloc] initWithString:site];
Allouez de la mémoire à votre objet
leParseur = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease];
Initialisez votre parseur personnel pour devenir le délégué du parseur.
XMLParser * leParseurDelegue = [[XMLParser alloc] initXMLParser];
Indiquez au parseur principal que certaine méthodes sont définies dans le délégué.
[leParseur setDelegate:leParseurDelegue];
Lancez le parcage du XML. Si le parcage peut être effectué, la méthode renverra TRUE sinon en cas d'erreur FALSE.
BOOL success = [leParseur parse];
Effectuez un test pour savoir si le parsage est correctement effectué.
if(success) {
NSLog(@";XML: End Parsing > OK";);
} else {
NSLog(@";XML: End Parsing > ERROR";);
}
La méthode NSLog(@"string") permet d'afficher un message dans la console. C'est l'équivalent de trace() en Actionscript ou System.out.println() en Java.
Test simulateur. Vous devez voir en console si votre XML est parse correctement ou non.

Etape 3: création de la classe Place
Nous allons créer une classe qui nous permettra de stocker les informations de notre capitale (nom, pays, coordonnées géographiques).
Comme précédemment, créez une nouvelle classe appelée "Place".
Créez une nouvelle classe. Ctrl-Clic sur le dossier "Classes"
Add… > New file… et choisissez "Objective-C class".
Nommez la classe "Place".
Dans l'interface de la classe, déclarez deux variables :
NSString *title; NSString *subtitle;
puis :
@property (nonatomic, retain) NSString *title; @property (nonatomic, retain) NSString *subtitle;
Nous devons déclarer une troisième variable qui sera utilisée pour contenir les coordonnées géographiques du point.
CLLocationCoordinate2D coordinate;
Comme la propriété "coordinate" est de type CLLocationCoordinate, qui n'est pas une classe instanciable mais une structure d'objet, on ne doit pas mettre d'étoile * devant son nom. Cette structure d'objet n'est pas prise en charge nativement. Elle est déclarée dans le framework MapKit. Nous devons donc importer le framework dans le projet (Dossier Frameworks, clic droit > Add > Existing framework...) et dans le fichier.
#import <MapKit/MapKit.h>
Il n'est pas recommandé de déclarer une structure d'objet en tant que variable en dehors de l'interface (avec "@property"). Il est donc nécessaire de créer une méthode d'instance qui permettra d'affecter une valeur à notre propriété "coordinate". Rappelons que dans le fichier ".h", seule la signature des méthodes est déclarée. Ainsi :
- (void) addCoordinates:(CLLocationCoordinate2D)c;
Dans le fichier de la classe, Place.m, il faut indiquer que les variables d'instance "title, subtile et coordinate" seront utilisées :
@synthesize title, subtitle, coordinate;
Déclarons aussi la méthode vue dans la classe :
- (void) addCoordinates:(CLLocationCoordinate2D)c {
coordinate = c;
}
Avec la méthode "addCoordinates", on associe alors à la variable de l'instance de Place la valeur de l'objet "c" passé en paramètre.
Etape 4: configuration du parseur délégué
De retour dans notre XMLParser, ajoutons les quelques lignes de code qui permettent de créer nos objets "Place".
Dans la décaration de l'interface, fichier ".h", ajoutez un ligne pour déclarer un objet temporaire de type "Place".
Il faut importer notre classe "Place" et le framework MapKit qui sera utilisé plus tard.
#import <MapKit/MapKit.h> @class Place
puis :
Place *aPlace;
Etudiez la structure du fichier XML à parser, disponible à l'adresse suivante : ici (ouvrez le fichier dans le Bloc Notes ou similaire)
Premier noeud à traiter : "Document"
A la détection de la balise ouvrante de "Document", il est nécessaire d'initialiser le tableau des lieux de l'application.
Ainsi dans le "didStartElement", il faut ajouter les lignes suivantes :
if([elementName isEqualToString:@";Document";]) {
monapp.places = [[NSMutableArray alloc] init];
}
remarque: le test sur des chaines de caractères est différent par rapport à ActionScript par exemple. On ne peut utiliser :
if(elementName == @";Document";)
On doit obligatoirement faire appel à une méthode de la classe NSString pour faire ce test:
Boolean egal = [maChaine isEqualToString:@";mon texte";];
Note: "monapp" est une référence au Singelton VoyagesAppDelegate.
Il faut réaliser les imports et les déclarations associées dans l'interface et la classe XMLParser.
XMLParser.h
@class VoyagesAppDelegate, Place;
@interface … { VoyagesAppDelegate *monapp; };
XMLParser.m
#import ";VoyagesAppDelegate.h";
#import ";Place.h";
- (XMLParser *) initXMLParser {
[super init];
monapp = (VoyagesAppDelegate *)[[UIApplication sharedApplication] delegate];
return self;
}
Revenons à notre XMLParser. Dans la méthode "didStartElement" on doit ajouter le code suivant pour initialiser un objet "Place" temporaire qui sera libéré plus tard.
if([elementName isEqualToString:@";Placemark";]) {
aPlace = [Place alloc];
}
Une fois qu'une balise est ouverte, il faut lire le contenu de cette balise. On utilise donc la méthode "foundCharacters".
Le code suivant permet d'initialiser l'objet "currentElementValue" s'il ne l'est pas et ensuite de "nettoyer la chaine trouvée avant de lui associer.
if(!currentElementValue) {
currentElementValue = [[NSMutableString alloc] initWithString:[self cleanString:string]];
} else {
[currentElementValue appendString:[self cleanString:string]];
}
Dernière partie du parseur, il faut détecter la fin d'une balise pour pouvoir écrire son contenu stocké dans "currentElementValue" avec la méthode "didEndElement". On utilise les mêmes principes de tests des chaines de caractères. Ainsi pour le titre et le sous-titre :
if([elementName isEqualToString:@";name";]) {
aPlace.title = currentElementValue;
}
if([elementName isEqualToString:@";description";]) {
aPlace.subtitle = currentElementValue;
}
Pour les coordonnées, le format récupéré est de type longitude, latitude, zoom.
On doit donc découper la chiante de caractères dans un tableau et en récupérer les deux premiers éléments pour créer un objet répondant à la méthode "addCoordinates" de notre objet "Place".
if([elementName isEqualToString:@";coordinates";]) {
NSMutableString *values = currentElementValue;
NSArray *array = [values componentsSeparatedByString:@";,";];
CLLocationCoordinate2D crd;
crd.latitude = [[array objectAtIndex:1] floatValue];
crd.longitude = [[array objectAtIndex:0] floatValue];
[aPlace addCoordinates:crd];
}
Une fois qu'un lieu est terminé de parser, il faut libérer les objets de la mémoire et ajouter le lieu au tableau dans le délégué de l'application.
if([elementName isEqualToString:@";Placemark";]) {
[monapp.places addObject:aPlace];
[aPlace release];
aPlace = nil;
}
Toujours dans la méthode "didEndElement", libérer "currentElementValue" :
[currentElementValue release]; currentElementValue = nil;
Le parseur est désormais terminé. Testez l'application pour vérifier les éventuelles erreurs de compilation.
Buvez un bon rafraichissant et poursuivez...
Etape 5: ajouter les capitales à la liste
De retour dans notre "RootViewController", nous devons dans un premier temps vérifier que notre tableau de lieux est bien complété.
Dans la partie du code qui sera exécutée en cas de succès, nous allons ajouter le code suivant qui nous informera en console du nombre d'objets dans notre tableau de lieux.
NSLog(@";Lieux: %i";, [monapp.places count]);
Testez l'application et vérifiez en console le nombre de lieux chargés ("0" est une mauvaise réponse).
Afin de gérer la vue liste des lieux, il est nécessaire d'implémenter dans notre classe les méthodes de gestion de la liste définies dans le délégué (UITableViewDelegate).
Pour récupérer le nombre de sections du tableau. Visuellement, les sections sont définies généralement en fond gris comme par exemple les Lettres dans l'application Contacts. Ici on utilise une seul section (entête non visible)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
Pour récupérer le nombre d'éléments (qui est le nombre de lignes) qui doivent être affichés dans la liste pour une section.
On doit alors compter le nombre de lieux présents dans notre tableau de lieux de notre délégué d'application.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(@";%i pays";, [monapp.places count]);
return [monapp.places count];
}
Permet de personnaliser l'apparence d'une cellule
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @";Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:";CellIdentifier";] autorelease];
}
Place *thatPlace = [monapp.places objectAtIndex:indexPath.row];
cell.textLabel.text = thatPlace.title;
cell.detailTextLabel.text = thatPlace.subtitle;
return cell;
}
Action effectuée au clic de la cellule par l'utilisateur
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// charge la vue de la carte (voir ci-après)
MapViewController *controleurCarte = [[MapViewController alloc] initWithNibName:@";MapView"; bundle:nil];
[self.navigationController pushViewController:controleurCarte animated:YES];
[controleurCarte release];
}
Etape 6: créer la vue pour la carte
Dans le dossier "Classes", ajouter un nouveau fichier > "UIViewController subclass" et cocher les "With XIB for user interface". Nommer la classe "MapViewController".
Renommer le fichier ".xib" en "MapView" et le placer dans le dossier "Ressources".
Dans le fichier ".m" :
Importez la classe associée au framework MapKit (expliqué plus haut) et la classe "VoyagesAppDelegate".
Nous allons créer cette vue avec la carte entièrement dans le code. Nous n'ouvrirons pas le fichier de vue "MapView".
Dans la méthode "viewDidLoad", ajoutez le code suivant :
titre de la vue (affiché dans le contrôleur) et utilisation de tableau des lieux dans le délégué de l'application.
VoyagesAppDelegate *monapp = (VoyagesAppDelegate *)[[UIApplication sharedApplication] delegate]; self.title = @"Carte";
Initilisation de la carte avec la classe MKMapView.
MKMapView *laCarte = [[MKMapView alloc] init]; laCarte.frame = self.view.frame; [self.view addSubview:laCarte];
Création d'une région qui spécifie le point de centre de la carte et le niveau de zoom affiché par défaut.
CLLocationCoordinate2D centreCarte; centreCarte.latitude = 47.322414; centreCarte.longitude = 5.04048; MKCoordinateSpan zoom; zoom.latitudeDelta = 32; zoom.longitudeDelta = zoom.latitudeDelta; MKCoordinateRegion region; region.center = centreCarte; region.span = zoom; [laCarte setRegion:region animated:TRUE];
Ajout des stations répondant au protocole MKAnnotation sur la carte directement depuis le tableau de lieux
[laCarte addAnnotations:monapp.places];
Si vous avez réussi, vous n'aurez sans doute pas besoin du Voyages.
Remerciements à mes camarades de jeu pour le travail conséquent et leur courage à s'attaquer à l'iPhone SDK (ils ne sont pas morts, je vous rassure): Julien GRASSWILL, Claire MUGNIER et Maxime WOERLY. (tutoriel HelloWorld)
[...] Voyages ou comment faire après les 3 précédents et se prendre la tête par Benoit Deldicque: sujet, code source Quelques raccourcis pratiques si vous débutez : Pour un crochet [ : Alt + Maj + 5 ou [...]
Héhé 
Nous avons réussi l’application Hello World !!!!
Vous avez rendu ça très intéressant !!
Bravo !!
very very good. Merci
Marco
merci bcp pour ce site;les tutos sont génial;
merci infinement et bonne continuation;
SVP si vous avez d’autres tutorials en langue françaises concernant le daveloppement Iphone;merci de me les envoyer par mail.
la plpart sur le web sont en englais
je serai trés reconnaisant
Bonjour et merci pour ce petit tuto.
J’ai un souci… [leparseur parse] renvoie faux direct (j’ai umis un NSLog(@ »%@ »,elementName) dans le didStartElement… et rien ne s’affiche ce qui prouve que je n’atteinds meme pas le premier tag.
Bonjour,
Plusieurs pistes :
- Avez-vous bien assigné le « delegate » du parseur NSXML ?
- L’URL entrée est-elle valide et renvoie-t-elle un fichier XML correctement formaté ?
- Essayez de faire une sortie console (NSLog) dans le « initXMLParser ».
Bonne continuation !
Tout d’abord un grand merci pour ce tuto.
j’ai suivi a la lettre les indications tout marchent mais lorsque que je créait ma propre carte sur google map plus rien ne s’affiche.
J’ai alors voulu tester avec le fichier .kml mais je ne sais pas comment l’héberger de façon a le récupérer automatiquement.
Cela doit être surement évident mais je débute ^^
Merci D’avance
Ben,
Pour héberger le fichier KML il faut simplement le stocker sur un serveur web (celui de ton site par exemple). Ensuite, il suffit juste de connaitre l’adresse URL du fichier pour l’intégrer dans l’application.
Pour ta propre carte qui ne fonctionne pas, essaie de revoir pas à pas les différentes étapes de ce projet en les appliquant au tiens (attention aux noms des variables, aux liaisons avec les vues dans Interface Builder entre autres…).
Bon courage et a très vite !
Bonjour, j’ai un soucis au niveau de mon programme.
Lorsque je compile, il me dit que ‘monapp’ n’est pas déclarée (au niveau de « NSLog(@ »Lieux: %i », [monapp.places count]); » qui se trouve dans le fichier rootViewController.m).
Je suis pourtant certain de bien avoir fait tout ce qu’il fallait faire, mais ça ne fonctionne pas, et je ne comprends pas pourquoi.
Auriez-vous une solution pour moi s’il vous plait?
Au pire des cas, est-ce que vous auriez les sources de votre application s’il vous plaît?
Merci d’avance.
Bonjour Math !
Si « monapp » n’est pas déclarée, il faut s’assurer de la déclarer…
Soit de façon générale avec dans le .h :
TonAppDelegate *monapp;
et dans le .m :
monapp = (TonAppDelegate *)[[UIApplication sharedApplication] delegate];
En espérant t’avoir débloqué (code source dispo à la fin de l’article) !