5

J'ai plusieurs vues qui font la même NSURLRequest/NSURLConnection request. Idéalement, pour obtenir une réutilisation de code, j'aimerais disposer d'une sorte de "proxy" qui effectue tout le travail sous-jacent de création/exécution de la requête/connexion (asynchrone), de la configuration de toutes les méthodes déléguées, etc. , donc je ne dois pas copier tous ces gestionnaires de méthode de délégué NSURLConnection dans chaque vue. Tout d'abord, cette approche de conception est-elle raisonnable? Deuxièmement, comment pourrais-je faire quelque chose comme ça? Pour une petite information d'arrière-plan, j'ai essayé ceci et je l'ai mis au «travail», cependant, il ne semble pas exécuter de façon asynchrone. J'ai créé un fichier Proxy.h/m qui a des méthodes d'instance pour les différents appels de service Web (et contient également les méthodes de délégué NSURLConnection):NSURLConnection Proxy NSURLRequest pour les appels de service Web asynchrones

@interface Proxy : NSObject { 

    NSMutableData *responseData; 
    id<WSResponseProtocol> delegate; 
} 

- (void)searchForSomethingAsync:(NSString *)searchString delegate:(id<WSResponseProtocol>)delegateObj; 

@property (nonatomic, retain) NSMutableData *responseData; 
@property (assign) id<WSResponseProtocol> delegate; 

@end 

Le WSResponseProtocol est défini comme tel:

@protocol WSResponseProtocol <NSObject> 

@optional 
- (void)responseData:(NSData *)data; 
- (void)didFailWithError:(NSError *)error; 

@end 

Pour l'utiliser, le contrôleur de vue doit simplement se conformer au protocole WSResponseProtocol, pour intercepter les réponses. Faire l'appel de service Web est fait comme si:

Proxy *p = [[Proxy alloc] init]; 
[p searchForSomethingAsync:searchText delegate:self]; 
[p release]; 

je peux fournir plus de code, mais le reste peut supposer. Avant d'appeler, je "commence à animer" un spinner UIActivityIndicatorView. Mais le fileur ne tourne jamais. Si je place simplement les méthodes de délégué NSURLConnection directement dans le contrôleur de vue, alors le spinner tourne. Donc, cela me fait penser que mon implémentation ne s'exécute pas de manière asynchrone. Des pensées/idées ici?

Répondre

22

Votre approche est raisonnable, cependant, je ne sais pas pourquoi vous créez votre propre protocole. Ce n'est pas nécessaire. Tout ce dont vous avez besoin pour l'implémenter est dans le documentation on NSURLConnection d'Apple. Si vous prenez le code de cette page où le paramètre NSURLConnection est instancié et si vous créez une connexion ivar au lieu de simplement la créer en tant que variable locale, vous pouvez ensuite comparer les objets de connexion dans chacune des méthodes de rappel et répondre en conséquence. Par exemple, prenez ce code de la documentation et changer l'objet de connexion à un Ivar:

// create the request 
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com/"] 
         cachePolicy:NSURLRequestUseProtocolCachePolicy 
        timeoutInterval:60.0]; 
// create the connection with the request 
// and start loading the data 
theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self]; 
if (theConnection) { 
    // Create the NSMutableData that will hold 
    // the received data 
    // receivedData is declared as a method instance elsewhere 
    receivedData=[[NSMutableData data] retain]; 
} else { 
    // inform the user that the download could not be made 
} 

La variable theConnection est notre Ivar. Ensuite, vous pouvez le vérifier comme ceci:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{ 
    if (connection == theConnection) 
    { 
     // do something with the data object. 
     [connectionSpecificDataObject appendData:data]; 
    } 
} 

Vous pouvez certainement le mettre en œuvre la création de votre propre protocole que vous proposez et rappeler au Délégué conforme à votre protocole, mais vous pouvez être mieux juste instancier votre objet en utilisant un sélecteur de réussite et d'échec que vous pouvez vérifier. Quelque chose comme ceci:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{ 
    if (connection == theConnection) 
    { 
     if (delegate && [delegate respondsToSelector:successSelector]) 
      [delegate performSelector:successSelector 
          withObject:connectionSpecificDataObject]; 
    } 
    [connection release]; 
} 

Où dataDidDownloadSelector est une variable d'instance SEL que vous avez défini lors de la création de votre délégué de téléchargement où tout ce code est contenu - votre objet Proxy. Quelque chose comme ceci:

Proxy *p = [[Proxy alloc] init]; 
[p searchForSomethingAsync:searchText 
        delegate:self 
      successSelector:@selector(didFinishWithData:) 
       failSelector:@selector(didFailWithError:)]; 

vos sélecteurs Mettre en oeuvre comme ceci:

- (void)didFinishWithData:(NSData*)data; 
{ 
    // Do something with data 
} 

- (void)didFailWithError:(NSError*)error 
{ 
    // Do something with error 
} 

Cela est devenu une réponse plus longue que je voulais. Faites-moi savoir si cela n'a pas de sens et je peux essayer de clarifier.

Meilleures salutations,

+0

Ah - très bien! Je vois ce que vous dites - votre solution éliminerait le besoin d'un protocole. Beaucoup plus simple! Merci pour ça! Je pense, cependant, que je commence à m'échauffer à la solution suggérée par Kendal, c'est-à-dire avoir des NSURLConnections synchrones enveloppées dans une NSOperation et jetées dans une file d'attente. Cette solution peut être une solution plus simple au problème de la création d'un cadre d'appels de service Web asynchrones, bien que mon approche + votre analyse soit toujours viable. – tbehunin

+0

Rétablissement de mon commentaire plus tôt. Cette approche est l'approche que je vais utiliser avec NSURLConnections synchrones dans une NSOperation. NSURLConnection fournit déjà le thread dont j'ai besoin sans le mettre dans une NSOperation. De plus, après avoir lu plus de blogs, l'authentification NSURLConnections + synchrone ne fournit pas autant de "hooks" que les appels asynchrones. – tbehunin

+0

Où \ comment receiveDataData est-il déclaré? La documentation ne cesse de dire qu'elle est déclarée ailleurs mais ne donne aucune information sur la façon de le faire. –

1

Votre code tel qu'il est, je pense ne rien faire - vous relâchez le proxy juste après l'initialisation, il ne fonctionne même.

Une approche que j'aime utiliser est des appels NSURLConnection synchrones, à l'intérieur d'un NSOperation qui est à son tour géré par une NSOperationQueue. Je fais que l'objet que la file d'attente habite dans un singleton, donc j'accède juste à l'instance de n'importe où et le dis quand j'ai besoin d'une nouvelle connexion commencée.

+0

Nice! Je n'ai jamais pensé à cette approche des appels NSURLConnection synchrones enveloppés dans un NSOperation. Je pense que ce sera la solution que je cherche. Cependant, avec cette solution, vous avez besoin d'un moyen de récupérer des informations. Voici un lien vers une autre réponse SO à ce problème: http://stackoverflow.com/questions/1297733/cocoa-return-information-from-nsoperation – tbehunin

+0

Pour transmettre des résultats, je stocke généralement le résultat des données quelque part par l'intermédiaire d'un singleton, et/ou renvoyer l'objet à toute personne intéressée enveloppée dans une notification (vous pouvez envelopper n'importe quel objet dans le dictionnaire de notification userInfo). –