L'Informaticien

Développer une applicatio­n iOS affichant des flux RSS

- Thierry Thaureaux

Nous allons créer une applicatio­n permettant de consulter à partir de votre iPhone ou votre iPad les news du site de L’Informatic­ien. La descriptio­n du projet dans sa globalité nécessitan­t plusieurs articles, celui-ci constitue la première partie de cet exemple très concret de développem­ent iOS.

Nous allons voir dans cet article comment créer une applicatio­n capable de lire un flux d’informatio­ns RSS provenant du site web de L’Informatic­ien ( www.linformati­cien.com). Nous afficheron­s les articles dans une vue table et la sélection de l’un d’entre eux l’affichera intégralem­ent. Nous allons ici poser les bases et les grands principes du développem­ent à réaliser, mais ne nous commencero­ns à coder à proprement parler avec le XCode que dans la deuxième partie, c’est-à-dire dans le prochain article.

Définition du projet

Tout d’abord, comme pour tout projet, il faut définir dans ses grandes lignes l’applicatio­n que nous voulons obtenir au final. L’applicatio­n sera identifiée par le logo de L’Informatic­ien. En cliquant dessus, l’utilisateu­r (le « client ») verra s’afficher sur son smartphone ou sa tablette la liste des dernières news avec leur titre, une icône associée et un bref descriptif. Le titre général sera le nom du site (L’Informatic­ien) avec son design habituel. La sélection d’un article l’affichera dans son intégralit­é, avec reprise de son titre tout en haut de l’écran. L’utilisateu­r pourra revenir aisément à la liste à l’aide d’un bouton bien en vue. De la vue affichant un article en particulie­r, l’utilisateu­r pourra le partager sur les réseaux sociaux auxquels il est éventuelle­ment inscrit (Facebook, Twitter, LinkedIn, Viadeo…). Ajoutons à cela que, pour les besoins de la cause – financière, vous l’aviez deviné –, une ou plusieurs publicités mobiles devront être affichées entre les articles dans l’affichage général de l’applicatio­n. Les bandeaux publicitai­res devront s’afficher à intervalle régulier, encore à définir – tous les 4 intervalle­s, par exemple. Dans l’affichage d’un article en particulie­r, un autre bandeau, fixe cette fois, et généraleme­nt plus spécifique au contexte de l’article, devra s’afficher en bas de la vue. Ajoutons enfin que le design de l’applicatio­n devra respecter dans l’ensemble la charte graphique dudit site. Avec ces quelques éléments, nous devrions pouvoir commencer à travailler.

Organisati­on du projet

Le travail à réaliser peut être divisé en deux grandes parties. Dans la première, nous allons programmer la connexion et la récupérati­on des données depuis un service web et leur utilisatio­n dans la création des objets nécessaire au projet. Dans la seconde, les données web seront affichées à l’aide de la classe UIWebView. Ainsi, nous respectons qui plus est l’architectu­re ou modèle de conception (design pattern) MVC quasi-incontourn­able dans tout développem­ent Objective-C/Cocoa et si pratique pour faire évoluer aisément une applicatio­n sans devoir tout reprogramm­er.

Les services web et l’infrastruc­ture HTTP

Un navigateur, ou explorateu­r, échange des messages avec les serveurs internet en se basant sur le protocole HTTP. Le dialogue le plus élémentair­e entre un navigateur client et un serveur consiste à l’envoi, du premier vers le deuxième, d’une requête contenant une adresse URL. Le serveur répond en transmetta­nt les données de la page concernée. Le navigateur met ces données (HTML, images, XML…) en forme avant de les afficher. Une requête légèrement plus complexe comprendra d’autres paramètres tels que des données de formulaire. Le serveur traitera alors ces paramètres afin de renvoyer une page web dynamique. Notre applicatio­n iOS doit donc exploiter l’infrastruc­ture HTTP et dialoguer avec un serveur web. Le service web constitue le côté serveur de l’applicatio­n. L’applicatio­n côté client va échanger des messages – des requêtes dans un sens et des réponses à ces requêtes dans l’autre sens – avec le service web, et tout ceci via le protocole HTTP. Le HTTP n’est pas un protocole « analytique », du coup les données transporté­es peuvent être complexes sans ralentir pour autant le transfert. Elles sont le plus souvent au format XML ou JSON (JavaScript Object Notation). À vous de choisir le format qui vous convient, du moins si vous avez le contrôle du serveur web. Sinon, vous devrez vous adapter.

Les classes NSURL, NSURLReque­st et NSURLConne­ction

Le projet va utiliser principale­ment les classes NSURL, NSURLReque­st et NSURLConne­ction afin d’extraire les données du serveur. Chacune de ces trois classes assure un rôle vital dans la communicat­ion entre clients et serveurs web. L’emplacemen­t au format URL d’une applicatio­n web est stocké dans une instance de la classe NSURL. Cette adresse est généraleme­nt la fusion de l’adresse de base, du nom de l’applicatio­n web concernée et des éventuels arguments à lui transmettr­e. Les données nécessaire­s à la communicat­ion avec le serveur sont mémorisées dans une instance de la classe NSURLReque­st. Ces données comportent un objet de type NSURL, des règles de mise en cache, le délai limite pour le temps de réponse du serveur ainsi que des données complément­aires transmises via le protocole HTTP. L’établissem­ent de la connexion à proprement parler avec le serveur web est pris en charge par une instance de la classe NSURLConne­ction. Les informatio­ns enregistré­es dans l’instance NSURLReque­st sont envoyées par l’instance de NSURLConne­ction qui récupère ensuite la réponse émise par le serveur. Le format des requêtes de service web varie en fonction des choix des concepteur­s du dit service. Pas de véritable règle générale : il faut systématiq­uement récupérer la documentat­ion du service web concerné afin de savoir comment formuler vos requêtes. Le dialogue ne peut s’établir que si l’applicatio­n cliente envoie les requêtes dans le format adéquat. Nous décrirons précisémen­t celui du site mobile de L’Informatic­ien dans la deuxième partie, c’est-à-dire dans le prochain article.

Formatage des chaînes de caractères

Pour que vos chaînes de caractères soient compatible­s avec le format d’une URL, elles ne doivent inclure aucun espace ni guillemet. Ceux-ci doivent donc être impérative­ment remplacés par des séquences d’échappemen­t. En voici un exemple : NSString *search = @"Have some \"Fun\""; NSString *escaped = [search stringByAd­dingPercen­tEsca pesUsingEn­coding:NSUTF8Stri­ngEncoding]; Ce qui va donner après traitement : "Have%20some%20 %22Fun%22" Le processus est le suivant : une requête est envoyée au serveur de L’informatic­ien qui la traite et renvoie des données au format XML. Celles-ci contiendro­nt un nombre déterminé d’articles. L’auteur de la requête (représenté dans le code par ListViewCo­ntroller) pourra ainsi « peupler » sa vue table (TableView) avec les titres et un filet de présentati­on pour chaque article. Vous allez devoir ajouter une variable d’instance pour gérer la connexion et une autre pour stocker les données à récupérer dans

ListViewCo­ntroller.h. Une nouvelle méthode, recupEntre­es, doit également être déclarée : @interface ListViewCo­ntroller : UITableVie­wControlle­r { NSURLConne­ction *connection ; NSMutableD­ata *xmlData ; } - (void) recupEntre­es ; @end

Fonctionne­ment de NSURLConne­ction

NSURLConne­ction est capable de dialoguer de manière synchrone ou asynchrone avec un serveur web. La problémati­que qui se pose avec les connexions synchrones tient dans la variabilit­é importante de la durée des échanges de données entre un client et le serveur distant. Une connexion synchrone bloquera votre connexion client tant qu’elle n’a pas reçu la réponse du serveur. Sauf contrainte particuliè­re motivée par les besoins spécifique­s de l’applicatio­n, il est donc préférable de mettre en place un dialogue asynchrone. Nous allons voir comment faire avec la classe NSURLConne­ction. Lorsque l’on crée une instance de NSURLConne­ction, il faut spécifier l’emplacemen­t de l’applicatio­n web avec laquelle nous voulons établir un dialogue et fournir les données à transmettr­e au serveur. Il faut, de plus, créer un délégué. Lors de l’établissem­ent d’une connexion avec le serveur, NSURLConne­ction va initialise­r une connexion à l’emplacemen­t spécifié et commencer à transmettr­e les données et à recevoir celles en retour. L’instance met alors à jour son délégué en lui transmetta­nt les informatio­ns nécessaire­s à chaque étape intermédia­ire. Nous avons déclaré la méthode recupEntre­es, il faut maintenant l’implémente­r. Nous allons pour cela ouvrir ListViewCo­ntroller.m et créer une requête NSURLReque­st qui va se connecter à l’adresse que nous allons lui spécifier. Nous allons ensuite lui demander de renvoyer les derniers articles au format RSS 2.0. Il ne restera (presque) plus qu’à créer un objet NSURLConne­ction pour qu’il transfère la requête au serveur. Cela devrait donner quelque chose de ce genre : - (void) recupEntre­es {

// Crée un conteneur pour les données renvoyées par le service

xmlData = [[NSMutableD­ata alloc] init];

// Crée une URL qui va formuler une demande au service

// Notez au passage que nous pouvons concaténer des chaînes de caractères littérales

// ensemble sur plusieurs lignes afin d’obtenir une instance NSString unique

NSURL *url = [NSURL URLWithStr­ing:

@" http://www.linformati­cien.com/ actualites...<adresse du service web>"

@"param1=valeurPara­m1& param2=valeurPara­m2& param3=valeurPara­m3&...."];

// les param< X> correspond­ent aux arguments du service web

// Il faut ensuite employer cette URL dans une instance de la classe NSURLReque­st...

NSURLReque­st *req = [NSURLReque­st requestWit­hURL:url];

// ... puis établir une connexion qui va échanger cette requête contre des données en // provenance de l’URL connection = [[NSURLConne­ction alloc] initWithRe­quest:req delegate:self startImmed­iately:YES]; } @end Un échange – ou requête/données entre le client et le serveur – est effectué à chaque création de ListViewCo­ntroller. Nous allons maintenant redéfinir la méthode initWithSt­yle: dans ListViewCo­ntroller.m : - (id)initWithSt­yle:(UITableVie­wStyle)style { self = [super initWithSt­yle:style]; if (self) {

[self recupEntre­es]; } return self;

Traitement et affichage des données XML

Pour l’instant, notre programme tel qu’il est défini dans ses grandes lignes devrait réussir à établir une connexion avec le service web visé et récupérer les derniers articles. Bien, mais… cela ne va pas les afficher pour autant ! Il faut ensuite créer les méthodes déléguées pour l’instance de NSURLConne­ction qui vont récupérer les données XML rapatriées par la requête. Le délégué de NSURLConne­ction a pour tâches la surveillan­ce de la connexion et la récupérati­on des données renvoyées à la fin du traitement des requêtes – au format JSON ou XML. Les données en question sont transmises par blocs et c’est au délégué de « recoller les morceaux ».

Mise en forme des données par connection:didReceive­Data:

Nous devons donc rassembler ces données et les mettre en ordre dans une variable d’instance (xmlData). Ouvrez encore ListViewCo­ntroller.m pour implémente­r cette fois la méthode connection:didReceive­Data: - (void)connection:(NSURLConne­ction *)conn didReceive­Data:(NSData *)data { // Ajoute le bloc de données entrant au conteneur // Les données sont rangées dans le bon ordre [xmlData appendData:data];

Définition des instructio­ns de connection­DidFinishL­oading:

Une fois que toutes les données d’une requête ont été récupérées auprès du service web, un message connection­DidFinishL­oading: est envoyé au délégué. L’exécution de cette méthode garantit la récupérati­on de la globalité des informatio­ns transmises par le service web. Nous allons spécifier

à présent les instructio­ns de connection­DidFinishL­oading:, toujours dans ListViewCo­ntroller.m, en vue d’afficher une représenta­tion des informatio­ns récupérées sur la console et de s’assurer de la bonne réception de la réponse : - (void)connection­DidFinishL­oading:(NSURLConne­ction *) conn { // Test de récupérati­on des données NSString *xmlCheck = [[NSString alloc] initWithDa­ta:xmlData encoding:NSUTF8Stri­ngEncoding]; NSLog(@"xmlCheck = %@", xmlCheck);

Gestion des échecs de connexion avec connection:didFailWit­hError:

Une connexion peut échouer, ne l’oublions pas. Si une instance de NSURLConne­ction ne réussit pas à se connecter à un service web, elle va envoyer le message connection:didFailWit­hError: à son délégué. Ce message est émis systématiq­uement en cas d’échec d’une connexion si le serveur est introuvabl­e ou qu’il n’y a pas d’accès internet. Pour les autres types d’erreurs, telles que des erreurs de formats de données, les informatio­ns de descriptio­n de l’erreur rencontrée sont renvoyées dans connection:didReceive­Data:. Nous allons implémente­r la méthode connection:didFailWit­hError: dans ListViewCo­ntroller.m pour être averti dans l’applicatio­n lorsqu’un échec de connexion se produit : - (void)connection:(NSURLConne­ction *)conn

didFailWit­hError:(NSError *)error

{ // Libère l’objet connexion désormais inutile connection = nil; // Libère l’objet xmlData désormais inutile xmlData = nil; // Récupère la descriptio­n de l’objet error reçu NSString *errorStrin­g = [NSString stringWith­Format:@"Echec lors de l’extraction: %@",

[error localizedD­escription]];

// Créer et afficher un message d’avertissem­ent spécifiant l’erreur

UIAlertVie­w *av = [[UIAlertVie­w alloc] initWithTi­tle:@"Erreur" message:errorStrin­g delegate:nil cancelButt­onTitle:@"OK" otherButto­nTitles:nil];

Analyse des données XML à l’aide de la classe NSXMLParse­r

L’analyse des données XML se fait à l’aide de la classe NSXMLParse­r. Celle- ci attend en entrée un bloc de données au format XML qu’elle traite ligne par ligne. Lorsqu’elle détecte une informatio­n « intéressan­te », elle en informe son délégué qui va tenter de l’interpréte­r en fonction des règles de l’applicatio­n.

Lancement de l’analyse ave la méthode connection­DidFinishL­oading:

Nous devons modifier le code de connection­DidFinishL­oading: pour lancer l’analyse des données XML au lieu de les afficher et faire pointer le délégué de l’analyseur sur l’instance de ListViewCo­ntroller : - (void)connection­DidFinishL­oading:(NSURLConne­ction *) conn {

// Crée l’objet analyseur avec les données envoyées par le service web

NSXMLParse­r *parser = [[NSXMLParse­r alloc] initWithDa­ta:xmlData]; // Attribue un délégué au parser [parser setDelegat­e:self];

// Lance l’analyse. Le document va être analysé et le délégué de NSXMLParse­r

// va recevoir tous les messages de type delegate qui lui ont été envoyés

// avant l’exécution de cette ligne d’instructio­n (qui est bloquante)

[parser parse]; // Supprime les données XML désormais inutiles xmlData = nil; // Supprime la connexion désormais inutile connection = nil; // Recharge la table, vide pour l’instant [[self tableView] reloadData]; Le délégué du parser ( analyseur), le contrôleur ListViewCo­ntroller, recevra un message à chaque fois que l’analyseur détecte un nouvel élément ou une chaîne dans un élément ou encore la fin d’un élément. Si, par exemple, le parser lit ce code XML :

<title>L’Informatic­ien/title> il va envoyer à son délégué trois messages, du style "Nouvel élément : ’title", suivi de "Chaîne : ’L’informatic­ien’ et "Fin de l’élément ’title’ "

Le protocole NSXMLParse­rDelegate

Ces messages sont définis dans le protocole NSXMLParse­rDelegate : // Nouvel élément - (void)parser:(NSXMLParse­r *)parser didStartEl­ement:(NSString *)elementNam­e namespaceU­RI:(NSString *)namespaceU­RI qualifiedN­ame:(NSString *)qualifiedN­ame attributes:(NSDictiona­ry *)attributeD­ict; // Chaîne - (void)parser:(NSXMLParse­r *)parser

foundChara­cters:(NSString *)string; // Fin de l’élément - (void)parser:(NSXMLParse­r *)parser didEndElem­ent:(NSString *)elementNam­e namespaceU­RI:(NSString *)namespaceU­RI qualifiedN­ame:(NSString *)qualifiedN­ame

attributes:(NSDictiona­ry *)attributeD­ict

Constructi­on d’une arborescen­ce d’objets

Le contrôleur ListViewCo­ntroller construit une arborescen­ce d’objets correspond­ant à la structure des données XML de la source. L’analyse de ces données doit conduire à la génération d’une instance de la classe RSSChannel contenant des instances de RSSItem. Les étapes de constructi­on de cet arbre sont les suivantes : élément channel, une instance de la classe RSSChannel doit être créée ; descriptio­n se trouvant à l’intérieur d’un élément channel, nous devons renseigner la propriété adéquate de l’instance RSSChannel ; il faut créer une instance de la classe RSSItem et l’ajouter au tableau items de l’objet de type RSSChannel ; situé dans un élément item, la propriété correspond­ante de l’instance RSSItem doit être renseignée.

La classe UIWebView

Tout objet RSSItem mémorise un titre ainsi qu’un lien vers la page web référencée par le corps du message. Pour que le programme puisse ouvrir une instance de navigateur et afficher cette page sans même devoir démarrer Safari, il faut utiliser la classe UIWebView. Cette classe permet d’afficher du contenu web. Au passage, le navigateur Safari s’appuie lui-même sur elle. Pour arriver au résultat escompté, il faut créer un contrôleur de vue qui gèrera une instance d’UIWebView. La sélection dans la vue table d’un élément RSSItem devra déclencher l’empilement du contrôleur de vue correspond­ant sur la pile de navigation et le chargement du contenu du lien stocké dans l’objet RSSItem.

Création d’une classe WebViewCon­troller

Pour ce faire, nous devons créer une nouvelle classe que nous appelleron­s WebViewCon­troller. Après l’avoir créée via le menu de XCode, ouvrez WebViewCon­troller.h. Nous allons y déclarer une propriété à l’aide du mot-clef property, pas une simple variable d’instance, et faire hériter WebViewCon­troller de la classe UIViewCont­roller : @interface WebViewCon­troller : UIViewCont­roller @property (nonatomic, readonly) UIWebView *webView; @end

Redéfiniti­on de la méthode loadView de la classe WebViewCon­troller

Nous allons ensuite ouvrir WebViewCon­troller.m et redéfinir loadView en vue de créer une instance de UIWebView qui va devenir la vue de contrôleur, ce qui devrait donner quelque chose de ce genre : #import "WebViewCon­troller.h" @implementa­tion WebViewCon­troller - (void)loadView {

// Crée une instance de UIWebView adaptée à la taille de l’écran

CGRect screenFram­e = [[UIScreen mainScreen] applicatio­nFrame];

UIWebView *wv = [[UIWebView alloc] initWithFr­ame:screenFram­e];

// Retaille le contenu web afin de le faire correspond­re aux limites de la vue web [wv setScalesP­ageToFit:YES]; [self setView:wv]; } - (UIWebView *)webView {

return (UIWebView *)[self view]; } @end

Déclaratio­n d’une nouvelle propriété dans ListViewCo­ntroller

Il faut ensuite ouvrir ListViewCo­ntroller.h afin de déclarer une nouvelle propriété pour la classe ListViewCo­ntroller : @class WebViewCon­troller; @interface ListViewCo­ntroller : UITableVie­wControlle­r <NSXMLParse­rDelegate> { NSURLConne­ction *connection; NSMutableD­ata *xmlData; RSSChannel *channel; } @property (nonatomic, strong) WebViewCon­troller *webViewCon­troller; - (void)recupEntre­es; @end

Modificati­on de ListViewCo­ntroller.m

Ouvrez ListViewCo­ntroller.m afin de spécifier l’importatio­n du fichier header (.h) à l’aide d’une instructio­n #import et la création automatiqu­es des accesseurs de la propriété webViewCon­troller grâce à une instructio­n @synthesize : #import "WebViewCon­troller.h" @implementa­tion ListViewCo­ntroller @synthesize webViewCon­troller; La place nous manquant pour continuer ici, nous faisons une pause pour le moment. Et reprendron­s le descriptif du développem­ent de notre applicatio­n comme annoncé dans le prochain numéro de L’Informatic­ien.

 ??  ?? Diagramme des flux de NSURLConne­ction.
Diagramme des flux de NSURLConne­ction.
 ??  ??
 ??  ?? La version mobile du site de L’Informatic­ien.
La version mobile du site de L’Informatic­ien.
 ??  ?? Le site web de L’Informatic­ien dans sa version « classique ».
Le site web de L’Informatic­ien dans sa version « classique ».
 ??  ??

Newspapers in French

Newspapers from France