OHURLLoader : utiliser les blocks pour charger des URLs

AliGatorAliGator Membre, Modérateur
avril 2011 modifié dans Objective-C, Swift, C, C++ #1
Hello all,

Suite à  ce post, je viens de publier sur mon GitHub une nouvelle classe nommée OHURLLoader.

Cette classe a pour but d'avoir du code plus simple que l'implémentation de NSURLConnection pour faire du download d'une NSURLRequest.
Elle a également pour but de vous montrer l'avantage d'utiliser les blocks de iOS4 et OSX 10.6 pour faire du code plus propre et plus sympa à  lire quand il s'agit de gérer les "callbacks".


En effet, normalement on doit se définir comme delegate, implémenter les méthodes de delegate de NSURLConnection, prévoir un NSMutableData qu'on doit remplir, etc. (en plus le code est quasi toujours le même)... En plus si on doit exécuter plusieurs NSURLRequest dans une même classe, on ne peut pas utiliser le même delegate facilement (car on ne pourra pas différencier pour quelle requête les méthodes de delegate ont été appelées à  chaque fois)...

Au contraire de cela, OHURLLoader vous permet d'avoir du code bien plus simple et de tout grouper au même endroit :
NSURL* url = ...<br />NSURLRequest* req = [NSURLRequest requestWithURL:url];<br /><br />OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];<br />[loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {<br />	NSLog(@&quot;Download of %@ done (statusCode:%d)&quot;,url,httpStatusCode);<br />	if (httpStatusCode == 200) /* OK */ {<br />		outputTextView.text = loader.receivedString; // commodity accessor : NSString built from data, using [self.response textEncodingName]<br />	} else { /* unexpected status code */<br />		outputTextView.text = [NSString stringWithFormat:@&quot;HTTP Status code: %d&quot;,httpStatusCode];<br />	}<br />} errorHandler:^(NSError *error) {<br />	NSLog(@&quot;Error while downloading %@: %@&quot;,url,error);<br />	outputTextView.text = [error localizedDescription];<br />}];<br />
Et plus besoin d'aucune "delegate methods", y'a rien d'autre à  implémenter !

L'autre avantage d'utiliser les blocks c'est que, comme vous pouvez le constater dans l'exemple, on peut utiliser dans les variables déclarées à  l'extérieur des blocks, comme dans notre cas la NSURL (ou même 'loader')... et ce malgré le fait que tout cela soit asynchrone, et que donc les codes dans les blocks "completion" et "errorHandler" sont exécutés de façon asynchrone
(ça parait pas comme ça mais si on implémentait ça avec les méthodes de delegate de NSURLConnection, on ne pourrait pas ! A moins de prévoir une variable d'instance pour cela, mais de toute façon on ne pourrait alors ne gérer qu'une seule NSURL, et pas plusieurs téléchargements parallèles, etc... alors que avec OHURLLoader et les blocks, aucun problème pour ça)

----

Voilà , en espérant " vu que c'est un exemple type d'usage des blocks " que ça vous donnera une meilleure idée de l'intérêt d'utiliser cette nouveauté que sont les blocks, et le code sexy qu'on peut faire avec (bien plus lisible que d'utiliser les delegate et avec des avantages supplémentaires en plus) :D
Bon l'inconvénient étant que ce code n'est compatible que OSX 10.6 et iOS 4.0 minimum, mais bon

Enjoy !

Réponses

  • muqaddarmuqaddar Administrateur
    11:34 modifié #2
    Terrible.

    T'aurais pu la publier y'a 3 mois avant que je fasse ma synchro !  :P

    Bon, à  vrai dire, je me demande quel est le mécanisme interne (en fait le contenu de ta classe) et ce qui se passe entre les blocs et les delegates... Tu vas pas me faire croire que ça marche tout seul ?  ;)

    C'est clair que pour le multi requests dans une seule classe, ça doit bien aider !
  • AliGatorAliGator Membre, Modérateur
    11:34 modifié #3
    Bah pour le fonctionnement interne, tu peux regarder le contenu de mon .m sur github si tu es curieux, mais c'est pas bien compliqué (*).

    En fait en interne j'utilise comme habituellement un objet NSURLConnection, et ma classe contient également un NSMutableData en interne aussi. Sauf que c'est ma classe qui est utilisée comme delegate du NSURLConnection et qui implémente ses méthodes de delegate... en appelant le block correspondant.
    Par exemple, dans OHURLLoader.m, l'implémentation de [tt]connectionDidFinishLoading:[/tt] consiste principalement en l'appel au block [tt]_completionBlock()[/tt] (variable d'instance qui garde une copie du block passé au paramètre "completion" de start...), ce qui va ainsi appeler/exécuter le bloc de code défini par l'utilisateur lors de l'appel à  [tt]startRequestWithCompletion:errorHandler:[/tt].

    *Bon il y a quelques subtilités sur l'utilisation des blocs en interne, genre penser à  appeler "copy" sur des blocks que l'on veut garder si on veut les utiliser hors du contexte dans lesquels ils sont définis, pour les blocks un "retain" ne suffit pas, je l'expliquerai peut-être un jour dans un article mais bon), mais à  part ça rien de sorcier sur le principe de fonctionnement

    Si vous souhaitez mieux comprendre comment c'est implémenté, n'hésitez pas à  lire le .m, il n'est pas spécialement compliqué à  comprendre (même si la syntaxe concernant les blocks peut vous dérouter mais bon justement, l'intérêt d'avoir publié ça c'est pour aussi vous permettre de comprendre comment ça marche et comment vous pouvez faire si vous souhaitez utiliser les blocks dans vos classes perso aussi)
  • CeetixCeetix Membre
    11:34 modifié #4
    Super Ali .
    Je vais lire le .m et essayer de comprendre tout ça. Si j'ai des questions je reviens :)
  • :-) D'une manière générale, la plupart des API d'Apple sont à  reprendre avec des blocks c'est beaucoup plus sympa lorsque l'on doit faire une application compatible OS X 10.6 ou plus / iOS 4 ou plus.



    On trouve beaucoup d'UIAlertView avec des blocks sur le Web par exemple.
  • Merci Ali, encore une fois c'est top image/smile.png' class='bbc_emoticon' alt=':)' />
  • Pile poil ce que je cherchais pour accéder à  mon webservice. J'ai d'ailleurs profité de l'occasion pour découvrir ton autre lib concernant la traduction des textes dans les xib. image/thumbsup.gif' class='bbc_emoticon' alt='' />
Connectez-vous ou Inscrivez-vous pour répondre.