AFNetworking + SDURLCache + ETag (recherche exemple d'implémentation réellement fonctionnelle

rhizomesrhizomes Membre
novembre 2012 modifié dans Objective-C, Swift, C, C++ #1
Bonjour,



J'essaie d'implémenter proprement les caches d'expiration et d'invalidation dans un client.



1- J'instancie le SDURLCache dans mon app delegate



[/size][/color][/font]<br />
[color=#BB2CA2][font=Menlo][size=3]int[/size][/font][/color][font=Menlo] cacheSizeMemory = [/font][color=#272AD8][font=Menlo][size=3]1[/size][/font][/color][font=Menlo]*[/font][color=#272AD8][font=Menlo][size=3]1024[/size][/font][/color][font=Menlo]*[/font][color=#272AD8][font=Menlo][size=3]1024[/size][/font][/color][font=Menlo]; [/font][color=#008400][font=Menlo][size=3]// 1MB[/size][/font][/color]<br />
[font=Menlo][color=#BB2CA2][size=3]int[/size][/color] cacheSizeDisk = [color=#272AD8][size=3]32[/size][/color]*[color=#272AD8][size=3]1024[/size][/color]*[color=#272AD8][size=3]1024[/size][/color];  [color=#008400][size=3]// 32MBs[/size][/color][/font]<br />
[font=Menlo][size=3][color=#703daa]NSURLCache[/color][size=4] *sharedCache = [[[/size][color=#4f8187]SDURLCache[/color][size=4] [/size][color=#3d1d81]alloc[/color][size=4]] [/size][color=#3d1d81]initWithMemoryCapacity[/color][size=4]:cacheSizeMemory [/size][color=#3d1d81]diskCapacity[/color][size=4]:cacheSizeDisk [/size][color=#3d1d81]diskPath[/color][size=4]:[[/size][color=#4f8187]SDURLCache[/color][size=4] [/size][color=#31595d]defaultCachePath[/color][size=4]]];[/size][/size][/font]<br />
[color=#000000][font=Menlo][[/font][/color][color=#703DAA][font=Menlo][size=3]NSURLCache[/size][/font][/color][color=#000000][font=Menlo] [/font][/color][color=#3D1D81][font=Menlo][size=3]setSharedURLCache[/size][/font][/color][color=#000000][font=Menlo]:sharedCache];[/font][/color]<br />
[color=#BB2CA2][font=Menlo][size=3]




2- J'ai une classe d'opération qui étend AFJSONRequestOperation et implémente l'enregistrement des ETag dans le setCompletionBlockWithSuccess...



3- Mon client AZHTTPClient implémente requestWithMethod

[/size][/font][/color]<br />
[font=Menlo]-([/font][color=#703DAA][font=Menlo][size=3]NSMutableURLRequest[/size][/font][/color][font=Menlo]*)requestWithMethod:([/font][color=#703DAA][font=Menlo][size=3]NSString[/size][/font][/color][font=Menlo] *)method path:([/font][color=#703DAA][font=Menlo][size=3]NSString[/size][/font][/color][font=Menlo] *)path parameters:([/font][color=#703DAA][font=Menlo][size=3]NSDictionary[/size][/font][/color][font=Menlo] *)parameters{[/font]<br />
[font=Menlo][size=3][size=4]	[/size][color=#703daa]NSMutableURLRequest[/color][size=4] *r=[[/size][color=#bb2ca2]super[/color][size=4] [/size][color=#31595d]requestWithMethod[/color][size=4]:method [/size][color=#31595d]path[/color][size=4]:path [/size][color=#31595d]parameters[/color][size=4]:parameters];[/size][/size][/font]<br />
[font=Menlo][size=3][size=4]	[/size][color=#703daa]NSString[/color][size=4] *previousEtag=[[/size][color=#4f8187]_etags[/color][size=4] [/size][color=#3d1d81]valueForKey[/color][size=4]:[[/size][color=#bb2ca2]self[/color][size=4] [/size][color=#31595d]hashForRequest[/color][size=4]:r]];[/size][/size][/font]<br />
[font=Menlo]	[color=#BB2CA2][size=3]if[/size][/color](previousEtag){[/font]<br />
[font=Menlo][size=3][size=4]		[r [/size][color=#3d1d81]setValue[/color][size=4]:previousEtag [/size][color=#3d1d81]forHTTPHeaderField[/color][size=4]:[/size][color=#78492a]kIfNoneMatch[/color][size=4]];[/size][/size][/font]<br />
[font=Menlo]	}[/font]<br />
[color=#3D1D81][color=#000000]	[r [/color]setCachePolicy[color=#000000]:[/color]NSURLRequestReloadRevalidatingCacheData[color=#000000]];[/color][/color]<br />
[font=Menlo]	[color=#BB2CA2][size=3]return[/size][/color] r;[/font]<br />
[font=Menlo]}[/font]<br />
[color=#BB2CA2][font=Menlo][size=3]




4- Lors d'appel consécutifs j'obtiens bien un 304 mais il est traité en erreur.



2012-11-14 09:34:06.952 AZTestRunner[2777:1903] AZ(0):-[AZHTTPClient logResponsesDetailsFor:withError:] line:145:{

response.allHeaderFields : {

Connection = "Keep-Alive";

Date = "Wed, 14 Nov 2012 08:26:38 GMT";

"Keep-Alive" = "timeout=15, max=99";

Server = Apache;

Vary = "Accept-Encoding,User-Agent";

}

}

2012-11-14 09:34:06.954 AZTestRunner[2777:1903] AZ(0):-[AZHTTPClient logResponsesDetailsFor:withError:] line:146:{

response.statusCode : 304

}

2012-11-14 09:34:06.955 AZTestRunner[2777:1903] AZ(0):-[AZHTTPClient logResponsesDetailsFor:withError:] line:147:{

error.localizedDescription : Expected status code in (200-299), got 304

}

2012-11-14 09:34:06.956 AZTestRunner[2777:907] AZ(0):__block_global_1 line:57:{

ERROR : Expected status code in (200-299), got 304

}



Avez vous des exemples d'implémentation réellement fonctionnelle ?

Réponses

  • AliGatorAliGator Membre, Modérateur
    Bonjour rhizomes et bienvenue,
    1. Pense à  aller te présenter dans la section "Présentation des membres" pour ton premier post (histoire qu'on sache qui tu es, ton niveau, ton expérience en ObjC, en POO)
    2. Il est inutile de m'envoyer un message privé pour que j'aille regarder ton post. Imagine si tout le monde faisait ça pour ses posts, bonjour ma boite de réception
    3. Merci d'éditer ton message pour y rajouter les balises CODE autour de ton code et le rendre ainsi plus lisible et donner aux membres + envie d'y répondre.
  • Merci pour les consignes AliGator.
  • AliGatorAliGator Membre, Modérateur
    novembre 2012 modifié #4
    Sinon pour ton problème de ETag et de 304 :

    - Soit tu ajoutes le code 304 aux addAcceptableStatusCode (cf doc de AFURLRequestOperation)

    - Soit, et je trouve ça mieux, tu acceptes de recevoir une erreur quand tu as un 304 comme c'est le cas actuellement, et dans ce cas tu retournes la valeur que tu as en cache en cas de NSError reçue dans ton bloc de completion. Comme ça, quelle que soit l'erreur que tu reçois, tu retournes ta valeur en cache. Que ce soit une erreur 304 (Not Modified) ou une autre erreur genre erreur réseau, erreur 500 (Server Error), ... au final si tu ne reviens pas avec un code 200 quand tu demandes la ressource, autant renvoyer la valeur en cache quel que soit la raison du NSError, non ?



    Mais bon si tu veux juste prendre en considération le cas du 304 pour retourner ton cache, addAcceptableStatusCode est là  pour ça. Quitte à  tester le statusCode dans le bloc de retour.
  • rhizomesrhizomes Membre
    novembre 2012 modifié #5
    Judicieuses remarques, merci.



    J'ai été très étonné de découvrir dans le Header <Foundations/ NSURLRequest.h> que les politiques NSURLRequestReloadRevalidatingCacheData et NSURLRequestReloadIgnoringLocalAndRemoteCacheData sont en réalité non implémentées.


    [color=#BB2CA2][font=Menlo][size=3]enum[/size][/font][/color]<br />
    [font=Menlo]{[/font]<br />
    [font=Menlo]	NSURLRequestUseProtocolCachePolicy = [color=#272AD8][size=3]0[/size][/color],[/font]<br />
    <br />
    [font=Menlo]	NSURLRequestReloadIgnoringLocalCacheData = [color=#272AD8][size=3]1[/size][/color],[/font]<br />
    [font=Menlo]	NSURLRequestReloadIgnoringLocalAndRemoteCacheData = [color=#272AD8][size=3]4[/size][/color], [color=#008400][size=3]// Unimplemented[/size][/color][/font]<br />
    [font=Menlo]	NSURLRequestReloadIgnoringCacheData = [color=#3D1D81][size=3]NSURLRequestReloadIgnoringLocalCacheData[/size][/color],[/font]<br />
    <br />
    [font=Menlo]	NSURLRequestReturnCacheDataElseLoad = [color=#272AD8][size=3]2[/size][/color],[/font]<br />
    [font=Menlo]	NSURLRequestReturnCacheDataDontLoad = [color=#272AD8][size=3]3[/size][/color],[/font]<br />
    <br />
    [font=Menlo]	NSURLRequestReloadRevalidatingCacheData = [color=#272AD8][size=3]5[/size][/color], [color=#008400][size=3]// Unimplemented[/size][/color][/font]<br />
    [font=Menlo]};[/font]<br />
    




    Le fait d'ajouter addAcceptableStatusCodes :


    [[color=#31595D][font=Menlo][size=3][color=#4f8187]AFHTTPRequestOperation[/color][color=#000000][size=4] [/size][/color]addAcceptableStatusCodes[color=#000000][size=4]:[[/size][/color][color=#703daa]NSIndexSet[/color][color=#000000][size=4] [/size][/color][color=#3d1d81]indexSetWithIndex[/color][color=#000000][size=4]:[/size][/color][color=#272ad8]304[/color][color=#000000][size=4]]];[/size][/color][/size][/font][/color]<br />
    [color=#31595D][font=Menlo][size=3]
    
    [/size][/font][/color]



    Produit l'invocation du bloc de succès mais ne retourne aucune donnée.

    Dois-je ne déduire que SDURLCache est insuffisant, et qu'il convient que j'implémente la couche de persistence ?
  • 'AliGator' a écrit:


    Sinon pour ton problème de ETag et de 304 :

    - Soit tu ajoutes le code 304 aux addAcceptableStatusCode (cf doc de AFURLRequestOperation)

    - Soit, et je trouve ça mieux, tu acceptes de recevoir une erreur quand tu as un 304 comme c'est le cas actuellement, et dans ce cas tu retournes la valeur que tu as en cache en cas de NSError reçue dans ton bloc de completion. Comme ça, quelle que soit l'erreur que tu reçois, tu retournes ta valeur en cache. Que ce soit une erreur 304 (Not Modified) ou une autre erreur genre erreur réseau, erreur 500 (Server Error), ... au final si tu ne reviens pas avec un code 200 quand tu demandes la ressource, autant renvoyer la valeur en cache quel que soit la raison du NSError, non ?



    Mais bon si tu veux juste prendre en considération le cas du 304 pour retourner ton cache, addAcceptableStatusCode est là  pour ça. Quitte à  tester le statusCode dans le bloc de retour.




    Et toi cher, Alligatoridae, as-tu réussi à  obtenir les données du cache SDURLcache dans le cas d'un 304 ?

    Si oui pourrais tu donner quelques extraits de code pour inspiration ?



    J'essaie de me conformer à  la RFC, de respecter les fondations et l'esprit de AFNet... mais là  franchement je calle.





    PS : au demeurant je pourrais tout à  fait gérer le cache d'invalidation moi-même et laisser à  SDUrlCache le cache d'expiration mais cela me semble "chameauteux"
  • rhizomesrhizomes Membre
    novembre 2012 modifié #7
    J'ai fait ça , et cela fonctionne mais je ne suis pas sûr que ce soit la bonne approche .


    <br />
    <br />
    [font=Menlo]- ([color=#BB2CA2][size=3]void[/size][/color])setCompletionBlockWithSuccess:([color=#BB2CA2][size=3]void[/size][/color] (^)([color=#4F8187][size=3]AFHTTPRequestOperation[/size][/color] *operation, [color=#BB2CA2][size=3]id[/size][/color] responseObject))success[/font]<br />
    [font=Menlo]							  failure:([color=#BB2CA2][size=3]void[/size][/color] (^)([color=#4F8187][size=3]AFHTTPRequestOperation[/size][/color] *operation, [color=#703DAA][size=3]NSError[/size][/color] *error))failure {[/font]<br />
    <br />
    [color=#4F8187][font=Menlo][size=3][color=#000000][size=4]	[/size][/color]AZHTTPClient[color=#000000][size=4] *httpClient=[[/size][/color]AZHTTPClient[color=#000000][size=4] [/size][/color][color=#31595d]sharedClient[/color][color=#000000][size=4]];[/size][/color][/size][/font][/color]<br />
    [color=#BB2CA2][font=Menlo][size=3][color=#000000][size=4]	[/size][/color]__unsafe_unretained[color=#000000][size=4] [/size][/color][color=#4f8187]AZRequestOperation[/color][color=#000000][size=4] *weakSelf = [/size][/color]self[color=#000000][size=4];[/size][/color][/size][/font][/color]<br />
    [color=#3D1D81][font=Menlo][size=3][color=#000000][size=4]	[/size][/color][color=#bb2ca2]self[/color][color=#000000][size=4].[/size][/color]completionBlock[color=#000000][size=4] = ^ {[/size][/color][/size][/font][/color]<br />
    [font=Menlo]		[color=#BB2CA2][size=3]__strong[/size][/color] [color=#4F8187][size=3]AZRequestOperation[/size][/color] *strongSelf = weakSelf;[/font]<br />
    [font=Menlo]		[color=#BB2CA2][size=3]if[/size][/color] ([strongSelf [color=#3D1D81][size=3]isCancelled[/size][/color]]) {[/font]<br />
    [font=Menlo]			[color=#BB2CA2][size=3]return[/size][/color];[/font]<br />
    [font=Menlo]		}[/font]<br />
    [font=Menlo]		[color=#BB2CA2][size=3]if[/size][/color] (strongSelf.[color=#4F8187][size=3]error[/size][/color]) {[/font]<br />
    [font=Menlo]			[color=#BB2CA2][size=3]if[/size][/color] (failure) {[/font]<br />
    [font=Menlo]				[httpClient [color=#31595D][size=3]logResponsesDetailsFor[/size][/color]:strongSelf [color=#31595D][size=3]withError[/size][/color]:strongSelf.[color=#4F8187][size=3]error[/size][/color]];[/font]<br />
    [font=Menlo][size=3][size=4]				[/size][color=#3d1d81]dispatch_async[/color][size=4](strongSelf.[/size][color=#4f8187]failureCallbackQueue[/color][size=4] ? strongSelf.[/size][color=#4f8187]failureCallbackQueue[/color][size=4] : [/size][color=#78492a]dispatch_get_main_queue[/color][size=4](), ^{[/size][/size][/font]<br />
    [font=Menlo]					failure(strongSelf, strongSelf.[color=#4F8187][size=3]error[/size][/color]);[/font]<br />
    [font=Menlo]				});[/font]<br />
    [font=Menlo]			}[/font]<br />
    [font=Menlo]		} [color=#BB2CA2][size=3]else[/size][/color] {[/font]<br />
    [font=Menlo]			[color=#BB2CA2][size=3]if[/size][/color] (success) {[/font]<br />
    [font=Menlo]				[httpClient [color=#31595D][size=3]logResponsesDetailsFor[/size][/color]:strongSelf [color=#31595D][size=3]withResponseObject[/size][/color]:strongSelf.[color=#4F8187][size=3]responseJSON[/size][/color]];[/font]<br />
    <br />
    [font=Menlo]				[color=#BB2CA2][size=3]if[/size][/color](strongSelf.[color=#4F8187][size=3]response[/size][/color].[color=#3D1D81][size=3]statusCode[/size][/color]==[color=#272AD8][size=3]304[/size][/color]){[/font]<br />
    [font=Menlo][size=3][size=4]					[/size][color=#3d1d81]dispatch_async[/color][size=4](strongSelf.[/size][color=#4f8187]successCallbackQueue[/color][size=4] ? strongSelf.[/size][color=#4f8187]successCallbackQueue[/color][size=4] : [/size][color=#78492a]dispatch_get_main_queue[/color][size=4](), ^{[/size][/size][/font]<br />
    [font=Menlo]						[color=#703DAA][size=3]NSError[/size][/color]*error=[color=#BB2CA2][size=3]nil[/size][/color];[/font]<br />
    [font=Menlo][size=3][size=4]						[/size][color=#703daa]NSData[/color][size=4] *d=[[[/size][color=#4f8187]SDURLCache[/color][size=4] [/size][color=#3d1d81]sharedURLCache[/color][size=4]] [/size][color=#3d1d81]cachedResponseForRequest[/color][size=4]:strongSelf.[/size][color=#4f8187]request[/color][size=4]].[/size][color=#3d1d81]data[/color][size=4] ;[/size][/size][/font]<br />
    [font=Menlo]						[color=#703DAA][size=3]NSString[/size][/color] *json=[[color=#703DAA][size=3]NSJSONSerialization[/size][/color] [color=#3D1D81][size=3]JSONObjectWithData:d[/size][/color] [color=#3D1D81][size=3]options[/size][/color]:[color=#BB2CA2][size=3]self[/size][/color].[color=#4f8187]JSONReadingOptions[/color] [color=#3D1D81][size=3]error[/size][/color]:&amp;error];[/font]<br />
    <br />
    [font=Menlo]						success(strongSelf,json);[/font]<br />
    [font=Menlo]					});[/font]<br />
    <br />
    [font=Menlo]				}[color=#BB2CA2][size=3]else[/size][/color]{[/font]<br />
    [font=Menlo][size=3][size=4]					[/size][color=#703daa]NSString[/color][size=4] *ETag=[strongSelf.[/size][color=#4f8187]response[/color][size=4].[/size][color=#3d1d81]allHeaderFields[/color][size=4] [/size][color=#3d1d81]objectForKey[/color][size=4]:[/size][color=#78492a]kETag[/color][size=4]];[/size][/size][/font]<br />
    [font=Menlo]					[color=#BB2CA2][size=3]if[/size][/color](ETag){[/font]<br />
    [font=Menlo]						[httpClient [color=#31595D][size=3]registerETag[/size][/color]:ETag [color=#31595D][size=3]forRequest[/size][/color]:strongSelf.[color=#4F8187][size=3]request[/size][/color]];[/font]<br />
    [font=Menlo]					}[/font]<br />
    [font=Menlo][size=3][size=4]					[/size][color=#3d1d81]dispatch_async[/color][size=4](strongSelf.[/size][color=#4f8187]successCallbackQueue[/color][size=4] ? strongSelf.[/size][color=#4f8187]successCallbackQueue[/color][size=4] : [/size][color=#78492a]dispatch_get_main_queue[/color][size=4](), ^{[/size][/size][/font]<br />
    [font=Menlo]						success(strongSelf, strongSelf.[color=#4F8187][size=3]responseJSON[/size][/color]);[/font]<br />
    [font=Menlo]					});[/font]<br />
    [font=Menlo]				}[/font]<br />
    <br />
    <br />
    [font=Menlo]			}[/font]<br />
    [font=Menlo]		}[/font]<br />
    [font=Menlo]	};[/font]<br />
    [font=Menlo]}[/font]<br />
    <br />
    
  • AliGatorAliGator Membre, Modérateur
    De mon côté je n'ai pas utilisé SDURLCache en fait.

    J'ai eu besoin de cette gestion ETag que pour certaines requêtes vers mon WebService, qui me retourne un gros JSON qui ne change pas souvent (et c'est le seul serveur avec lequel j'interagis qui supporte le système de cache par ETag).



    Donc pas de SDURLCache, je garde le NSURLCache de base (qui, de surcroit, supporte maintenant le cache fichier en plus du cache mémoire depuis les derniers iOS, ce qui fait qu'utiliser SDURLCache n'est plus forcément toujours d'actualité maintenant) que je n'ai pas modifié.



    J'ai juste un gros dictionnaire indexé par l'URL de la ressource qui m'intéresse, et pour chaque URL est associée l'ETag et le contenu en cache. Quand je fais une requête, je récupère l'entrée dans mon dico pour l'URL demandée, si elle existe. Si oui, j'ai donc le ETag ainsi que le contenu en cache correspondant à  cette URL. Je fais alors ma requête avec AFN en rajoutant le ETag dans les headers de la requête, puis si je reçois une réponse valide (et donc une mise à  jour avec nouvel ETag et nouveau contenu) alors je met à  jour mon cache pour remplacer par le nouvel ETag et le nouveau contenu, puis retourne ce nouveau contenu. Et si je reçois une erreur (soit parce que 304, soit pour une autre raison genre coupure réseau ou erreur 500) alors j'utilise la valeur stockée dans le cache.



    Donc au final, j'ai implémenté mon propre "NSURLRequestReloadRevalidatingCacheData" à  la main, d'une certaine façon ^^
  • 'AliGator' a écrit:


    Donc au final, j'ai implémenté mon propre "NSURLRequestReloadRevalidatingCacheData" à  la main, d'une certaine façon ^^




    C'est une façon optimiste de voir les choses !
  • rhizomesrhizomes Membre
    novembre 2012 modifié #10
    'AliGator' a écrit:


    Donc pas de SDURLCache, je garde le NSURLCache de base (qui, de surcroit, supporte maintenant le cache fichier en plus du cache mémoire depuis les derniers iOS, ce qui fait qu'utiliser SDURLCache n'est plus forcément toujours d'actualité maintenant) que je n'ai pas modifié.




    Tu as parfaitement raison pour le NSURLcache, il fonctionne dans tous les modes et en https.



    Voici mon implémentation qui fonctionne tout à  fait correctement :



    1- Dans l'app delegate instanciation du cache global - ce qui permet de le purger sur applicationDidReceiveMemoryWarning:


    [color=#000000][font=Menlo] [/font][/color][color=#BB2CA2][font=Menlo][size=3]int[/size][/font][/color][font=Menlo] cacheSizeMemory = [/font][color=#272AD8][font=Menlo][size=3]1[/size][/font][/color][font=Menlo]*[/font][color=#272AD8][font=Menlo][size=3]1024[/size][/font][/color][font=Menlo]*[/font][color=#272AD8][font=Menlo][size=3]1024[/size][/font][/color][font=Menlo]; [/font][color=#008400][font=Menlo][size=3]// 1MB[/size][/font][/color]<br />
    [font=Menlo]	[color=#BB2CA2][size=3]int[/size][/color] cacheSizeDisk = [color=#272AD8][size=3]32[/size][/color]*[color=#272AD8][size=3]1024[/size][/color]*[color=#272AD8][size=3]1024[/size][/color];  [color=#008400][size=3]// 32MBs[/size][/color][/font]<br />
    [font=Menlo][size=3][size=4]	[/size][color=#703daa]NSURLCache[/color][size=4] *sharedCache = [[[/size][color=#703daa]NSURLCache[/color][size=4] [/size][color=#3d1d81]alloc[/color][size=4]] [/size][color=#3d1d81]initWithMemoryCapacity[/color][size=4]:cacheSizeMemory [/size][color=#3d1d81]diskCapacity[/color][size=4]:cacheSizeDisk [/size][color=#3d1d81]diskPath[/color][size=4]:[/size][color=#78492a]kNSURLCachePath[/color][size=4]];[/size][/size][/font]<br />
    [font=Menlo][size=3][size=4]	[[/size][color=#703daa]NSURLCache[/color][size=4] [/size][color=#3d1d81]setSharedURLCache[/color][size=4]:sharedCache];[/size][/size][/font]<br />
    [color=#000000][font=Menlo]	[[/font][/color][color=#4F8187][font=Menlo][size=3]AFHTTPRequestOperation[/size][/font][/color][color=#000000][font=Menlo] [/font][/color][color=#31595D][font=Menlo][size=3]addAcceptableStatusCodes[/size][/font][/color][color=#000000][font=Menlo]:[[/font][/color][color=#703DAA][font=Menlo][size=3]NSIndexSet[/size][/font][/color][color=#000000][font=Menlo] [/font][/color][color=#3D1D81][font=Menlo][size=3]indexSetWithIndex[/size][/font][/color][color=#000000][font=Menlo]:[/font][/color][color=#272AD8][font=Menlo][size=3]304[/size][/font][/color][color=#000000][font=Menlo]]];[/font][/color]
    






    2- Dans le client AZHTTPClient qui étend AFHTTPClient


    <br />
    [font=Menlo]-([/font][color=#703DAA][font=Menlo][size=3]NSMutableURLRequest[/size][/font][/color][font=Menlo]*)requestWithMethod:([/font][color=#703DAA][font=Menlo][size=3]NSString[/size][/font][/color][font=Menlo] *)method path:([/font][color=#703DAA][font=Menlo][size=3]NSString[/size][/font][/color][font=Menlo] *)path parameters:([/font][color=#703DAA][font=Menlo][size=3]NSDictionary[/size][/font][/color][font=Menlo] *)parameters{[/font]<br />
    [font=Menlo][size=3][size=4]	[/size][color=#703daa]NSMutableURLRequest[/color][size=4] *r=[[/size][color=#bb2ca2]super[/color][size=4] [/size][color=#31595d]requestWithMethod[/color][size=4]:method [/size][color=#31595d]path[/color][size=4]:path [/size][color=#31595d]parameters[/color][size=4]:parameters];[/size][/size][/font]<br />
    [font=Menlo][size=3][size=4]	[/size][color=#703daa]NSString[/color][size=4] *previousEtag=[[/size][color=#4f8187]_etags[/color][size=4] [/size][color=#3d1d81]valueForKey[/color][size=4]:[[/size][color=#bb2ca2]self[/color][size=4] [/size][color=#31595d]hashForRequest[/color][size=4]:r]];[/size][/size][/font]<br />
    [color=#3D1D81][font=Menlo][size=3][color=#000000][size=4]	[/size][/color][color=#bb2ca2]BOOL[/color][color=#000000][size=4] isCached=([[[/size][/color][color=#703daa]NSURLCache[/color][color=#000000][size=4] [/size][/color]sharedURLCache[color=#000000][size=4]][/size][/color]cachedResponseForRequest[color=#000000][size=4]:r]&#33;=[/size][/color][color=#bb2ca2]nil[/color][color=#000000][size=4]);[/size][/color][/size][/font][/color]<br />
    [font=Menlo]	[color=#BB2CA2][size=3]if[/size][/color](previousEtag &amp;&amp; &#33;isCached){[/font]<br />
    [font=Menlo][size=3][size=4]		[[/size][color=#4f8187]_etags[/color][size=4] [/size][color=#3d1d81]removeObjectForKey[/color][size=4]:[[/size][color=#bb2ca2]self[/color][size=4] [/size][color=#31595d]hashForRequest[/color][size=4]:r]];[/size][/size][/font]<br />
    [font=Menlo]	}[/font]<br />
    [font=Menlo]	[color=#BB2CA2][size=3]if[/size][/color](previousEtag &amp;&amp; isCached){[/font]<br />
    [font=Menlo][size=3][size=4]		[r [/size][color=#3d1d81]setValue[/color][size=4]:previousEtag [/size][color=#3d1d81]forHTTPHeaderField[/color][size=4]:[/size][color=#78492a]kIfNoneMatch[/color][size=4]];[/size][/size][/font]<br />
    [color=#3D1D81][font=Menlo][size=3][color=#000000][size=4]		[r [/size][/color]setCachePolicy[color=#000000][size=4]:[/size][/color]NSURLRequestReloadRevalidatingCacheData[color=#000000][size=4]];[/size][/color][/size][/font][/color]<br />
    [font=Menlo]	}[color=#BB2CA2][size=3]else[/size][/color]{[/font]<br />
    [color=#3D1D81][font=Menlo][size=3][color=#000000][size=4]		[r [/size][/color]setCachePolicy[color=#000000][size=4]:[/size][/color]NSURLRequestUseProtocolCachePolicy[color=#000000][size=4]];[/size][/color][/size][/font][/color]<br />
    [font=Menlo]	}[/font]<br />
    [font=Menlo]	[color=#BB2CA2][size=3]return[/size][/color] r;[/font]<br />
    [font=Menlo]}[/font]<br />
    <br />
    <br />
    <br />
    [font=Menlo]-([color=#BB2CA2][size=3]void[/size][/color])registerETag:([color=#703DAA][size=3]NSString[/size][/color]*)ETag forRequest:([color=#703DAA][size=3]NSURLRequest[/size][/color]*)request{[/font]<br />
    [font=Menlo][size=3][size=4]	[[/size][color=#4f8187]_etags[/color][size=4] [/size][color=#3d1d81]setValue[/color][size=4]:ETag [/size][color=#3d1d81]forKey[/color][size=4]:[[/size][color=#bb2ca2]self[/color][size=4] [/size][color=#31595d]hashForRequest[/color][size=4]:request]];[/size][/size][/font]<br />
    [font=Menlo]}[/font]<br />
    <br />
    [font=Menlo]-([color=#703DAA][size=3]NSString[/size][/color]*)hashForRequest:([color=#703DAA][size=3]NSURLRequest[/size][/color]*)request{[/font]<br />
    [font=Menlo][size=3][size=4]	[/size][color=#703daa]NSString[/color][size=4] *s=request.[/size][color=#3d1d81]URL[/color][size=4].[/size][color=#3d1d81]absoluteString[/color][size=4];[/size][/size][/font]<br />
    [color=#3D1D81][font=Menlo][size=3][color=#000000][size=4]	[/size][/color][color=#703daa]NSString[/color][color=#000000][size=4] *h=[[/size][/color][color=#703daa]NSString[/color][color=#000000][size=4] [/size][/color]stringWithFormat[color=#000000][size=4]:[/size][/color][color=#d12f1b]@&quot;%i&quot;[/color][color=#000000][size=4],[s [/size][/color]hash[color=#000000][size=4]]];[/size][/color][/size][/font][/color]<br />
    [font=Menlo]	[color=#BB2CA2][size=3]return[/size][/color] h;[/font]<br />
    [font=Menlo]}[/font]<br />
    






    3-Dans AZRequestOperation qui étend AFJSONRequestOperation :


    <br />
    <br />
    [font=Menlo]- ([color=#BB2CA2][size=3]void[/size][/color])setCompletionBlockWithSuccess:([color=#BB2CA2][size=3]void[/size][/color] (^)([color=#4F8187][size=3]AFHTTPRequestOperation[/size][/color] *operation, [color=#BB2CA2][size=3]id[/size][/color] responseObject))success[/font]<br />
    [font=Menlo]							  failure:([color=#BB2CA2][size=3]void[/size][/color] (^)([color=#4F8187][size=3]AFHTTPRequestOperation[/size][/color] *operation, [color=#703DAA][size=3]NSError[/size][/color] *error))failure {[/font]<br />
    [color=#4F8187][font=Menlo][size=3][color=#000000][size=4]	[/size][/color]AZHTTPClient[color=#000000][size=4] *httpClient=[[/size][/color]AZHTTPClient[color=#000000][size=4] [/size][/color][color=#31595d]sharedClient[/color][color=#000000][size=4]];[/size][/color][/size][/font][/color]<br />
    [color=#BB2CA2][font=Menlo][size=3][color=#000000][size=4]	[/size][/color]__unsafe_unretained[color=#000000][size=4] [/size][/color][color=#4f8187]AZRequestOperation[/color][color=#000000][size=4] *weakSelf = [/size][/color]self[color=#000000][size=4];[/size][/color][/size][/font][/color]<br />
    [color=#3D1D81][font=Menlo][size=3][color=#000000][size=4]	[/size][/color][color=#bb2ca2]self[/color][color=#000000][size=4].[/size][/color]completionBlock[color=#000000][size=4] = ^ {[/size][/color][/size][/font][/color]<br />
    [font=Menlo]		[color=#BB2CA2][size=3]__strong[/size][/color] [color=#4F8187][size=3]AZRequestOperation[/size][/color] *strongSelf = weakSelf;[/font]<br />
    [font=Menlo]		[color=#BB2CA2][size=3]if[/size][/color] ([strongSelf [color=#3D1D81][size=3]isCancelled[/size][/color]]) {[/font]<br />
    [font=Menlo]			[color=#BB2CA2][size=3]return[/size][/color];[/font]<br />
    [font=Menlo]		}[/font]<br />
    [font=Menlo]		[color=#BB2CA2][size=3]if[/size][/color] (strongSelf.[color=#4F8187][size=3]error[/size][/color]) {[/font]<br />
    [font=Menlo]			[color=#BB2CA2][size=3]if[/size][/color] (failure) {[/font]<br />
    [font=Menlo]				[httpClient [color=#31595D][size=3]logResponsesDetailsFor[/size][/color]:strongSelf [color=#31595D][size=3]withError[/size][/color]:strongSelf.[color=#4F8187][size=3]error[/size][/color]];[/font]<br />
    [font=Menlo][size=3][size=4]				[/size][color=#3d1d81]dispatch_async[/color][size=4](strongSelf.[/size][color=#4f8187]failureCallbackQueue[/color][size=4] ? strongSelf.[/size][color=#4f8187]failureCallbackQueue[/color][size=4] : [/size][color=#78492a]dispatch_get_main_queue[/color][size=4](), ^{[/size][/size][/font]<br />
    [font=Menlo]					failure(strongSelf, strongSelf.[color=#4F8187][size=3]error[/size][/color]);[/font]<br />
    [font=Menlo]				});[/font]<br />
    [font=Menlo]			}[/font]<br />
    [font=Menlo]		} [color=#BB2CA2][size=3]else[/size][/color] {[/font]<br />
    [font=Menlo]			[color=#BB2CA2][size=3]if[/size][/color] (success) {[/font]<br />
    [font=Menlo]				[color=#BB2CA2][size=3]if[/size][/color](strongSelf.[color=#4F8187][size=3]response[/size][/color].[color=#3D1D81][size=3]statusCode[/size][/color]==[color=#272AD8][size=3]304[/size][/color]){[/font]<br />
    [font=Menlo][size=3][size=4]					[/size][color=#3d1d81]dispatch_async[/color][size=4](strongSelf.[/size][color=#4f8187]successCallbackQueue[/color][size=4] ? strongSelf.[/size][color=#4f8187]successCallbackQueue[/color][size=4] : [/size][color=#78492a]dispatch_get_main_queue[/color][size=4](), ^{[/size][/size][/font]<br />
    [font=Menlo]						[color=#703DAA][size=3]NSError[/size][/color]*error=[color=#BB2CA2][size=3]nil[/size][/color];[/font]<br />
    [font=Menlo]						[color=#703DAA][size=3]NSString[/size][/color] *json=[color=#BB2CA2][size=3]nil[/size][/color];[/font]<br />
    [font=Menlo][size=3][size=4]						[/size][color=#703daa]NSData[/color][size=4] *d=[[[/size][color=#703daa]NSURLCache[/color][size=4] [/size][color=#3d1d81]sharedURLCache[/color][size=4]] [/size][color=#3d1d81]cachedResponseForRequest[/color][size=4]:strongSelf.[/size][color=#4f8187]request[/color][size=4]].[/size][color=#3d1d81]data[/color][size=4];[/size][/size][/font]<br />
    [font=Menlo][size=3][size=4]						json=[[/size][color=#703daa]NSJSONSerialization[/color][size=4] [/size][color=#3d1d81]JSONObjectWithDat:d [/color][color=#3d1d81]options[/color][size=4]:[/size][color=#bb2ca2]self[/color][size=4].[/size][color=#4f8187]JSONReadingOptions[/color][size=4] [/size][color=#3d1d81]error[/color][size=4]:&amp;error];[/size][/size][/font]<br />
    [font=Menlo]						[httpClient [color=#31595D][size=3]logResponsesDetailsFor[/size][/color]:strongSelf [color=#31595D][size=3]withResponseObject[/size][/color]:json];[/font]<br />
    [font=Menlo]						success(strongSelf,json);[/font]<br />
    [font=Menlo]					});[/font]<br />
    [font=Menlo]				}[color=#BB2CA2][size=3]else[/size][/color]{[/font]<br />
    [font=Menlo][size=3][size=4]					[/size][color=#703daa]NSString[/color][size=4] *ETag=[strongSelf.[/size][color=#4f8187]response[/color][size=4].[/size][color=#3d1d81]allHeaderFields[/color][size=4] [/size][color=#3d1d81]objectForKey[/color][size=4]:[/size][color=#78492a]kETag[/color][size=4]];[/size][/size][/font]<br />
    [font=Menlo]					[color=#BB2CA2][size=3]if[/size][/color](ETag){[/font]<br />
    [font=Menlo]						[httpClient [color=#31595D][size=3]registerETag[/size][/color]:ETag [color=#31595D][size=3]forRequest[/size][/color]:strongSelf.[color=#4F8187][size=3]request[/size][/color]];[/font]<br />
    [font=Menlo]					}[/font]<br />
    [font=Menlo]					[httpClient [color=#31595D][size=3]logResponsesDetailsFor[/size][/color]:strongSelf [color=#31595D][size=3]withResponseObject[/size][/color]:strongSelf.[color=#4F8187][size=3]responseJSON[/size][/color]];[/font]<br />
    [font=Menlo][size=3][size=4]					[/size][color=#3d1d81]dispatch_async[/color][size=4](strongSelf.[/size][color=#4f8187]successCallbackQueue[/color][size=4] ? strongSelf.[/size][color=#4f8187]successCallbackQueue[/color][size=4] : [/size][color=#78492a]dispatch_get_main_queue[/color][size=4](), ^{[/size][/size][/font]<br />
    [font=Menlo]						success(strongSelf, strongSelf.[color=#4F8187][size=3]responseJSON[/size][/color]);[/font]<br />
    [font=Menlo]					});[/font]<br />
    [font=Menlo]				}			   [/font]<br />
    [font=Menlo]			}[/font]<br />
    [font=Menlo]		}[/font]<br />
    [font=Menlo]	};[/font]<br />
    [font=Menlo]}[/font]<br />
    




    Ca n'est pas 100% académique mais ça marche bien sans bidouille excessive.
Connectez-vous ou Inscrivez-vous pour répondre.