AFNetworking HTTPClient sous-classe avec XMLParser
-
26-10-2019 - |
Question
Je suis en train d'écrire une petite application iOS qui interroge un XML REST webservice. Le cadre de réseau utilisé est AFNetworking .
Situation
Pour interroger le webservice je AFHTTPClient sous-classé:
@interface MyApiClient : AFHTTPClient
et dans la mise en œuvre que je fais que disponible en singleton:
+ (MyApiClient *)sharedClient {
static MySharedClient *_sharedClient = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedClient = [[self alloc] initWithBaseUrl:[NSUrl URLWithString:@"http://url.to.the.webservice"]];
});
return self;
}
et initWithBaseURL je dis AFNetworking d'attendre du contenu XML:
[self registerHTTPOperationClass:[AFXMLRequestOperation class]];
Maintenant, je peux appeler getPatch sur le singleton de mon ViewController et dans le bloc de succès commence mon analyse syntaxique XML retourné. Dans les méthodes NSXMLParserDelegate dans le ViewController je peux alors choisir les parties du XML que je suis intéressé et faire des choses avec elle.
Problème
Je veux avoir des méthodes dans mon HTTPClient singleton que tout poignée en rapport avec les modèles de données webservice et retour ou liste des modèles au lieu de XML.
Par exemple, je veux faire quelque chose comme ceci:
ServerModel *status = [[MyApiClient sharedClient] getServerStatus];
Le ApiClient alors appeler en interne le webservice, analyser le XML et retourner le modèle. Comment puis je faire ça? Normalement, je voudrais utiliser un délégué qui est appelé une fois que le XML est analysé, mais en raison de la nature singleton du ApiClient il pourrait y avoir plusieurs délégués?
quelqu'un L'espoir peut faire la lumière sur ce sujet, merci!
La solution
(Toutes mes excuses à l'avance pour ce « tri de » réponse, mais nous travaillons à une meilleure solution ...)
Vous devez prendre un pas en arrière et pensez à votre conception avec soin.
Vous avez des problèmes parce que vous avez une idée que quelque chose dans vos besoins de conception à un singleton, mais soit:
1) qui n'est pas vraiment nécessaire,
2) quelque chose pourrait déjà exister qui le fait travail pour vous (par exemple, le HTTP lib que vous utilisez),
ou
3) Vous faire la mauvaise chose un singleton, ou vous ne l'avez pas portionné votre conception dans les parties appropriées pour fonctionner avec l'idée singleton
Alors, pouvez-vous me dire explicitement pourquoi vous allez pour une approche singleton? Est-il juste d'assurer qu'une demande de réseau ne peut se produire à la fois? Y at-il de la notion statefulness dans votre objet singleton? Ensuite, je mettrai à jour cette réponse ou un commentaire, etc.
( Digression: Je voudrais aussi ajouter que dans certains cas, il pourrait y avoir un véritable besoin d'un forte 'singleton - je veux dire qu'il ya vraiment une seule instance possible, et ce mécanisme est cuit directement dans votre objet, comme vous le faites - mais ce n'est pas l'alternative est un ' faible singleton, je veux dire par votre objet principal. qui fait réellement le travail a un avantage de cette faible idée singleton méthode init
simple comme d'habitude, mais l'accès partagé à un objet commun passe par un autre objet, qui est une sorte de simple « usine » qui instancie / détient l'instance partagée. est que votre code est plus réutilisable dans des contextes différents - par exemple, vous pouvez décider de faire plusieurs requêtes HTTP / sessions en même temps à une date ultérieure - et il fait parfois des tests d'écriture moins problématiques)
Autres conseils
Utiliser des blocs au lieu des délégués.
De ma classe ApiClient:
- (void)getPath:(NSString *)path
parameters:(NSDictionary *)parameters
success:(void (^)(id response))success
failure:(void (^)(NSError *error))failure
{
NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters];
[self enqueueHTTPOperationWithRequest:request success:success failure:failure];
}
-(void)fetchAllUsersSuccess:(void (^)(id))success
failure:(void (^)(NSError *))failure
{
[self getPath:@"/api/mobile/user/"
parameters:nil
success:^(id response) {
if([response isKindOfClass:[NSXMLParser class]]){
//parse here to new dict
success(newDict);
} else
success(response);
} failure:^(NSError *error) {
failure(error);
}];
}
Maintenant, je peux l'utiliser comme:
ServiceApiClient *apiClient = [ServiceApiClient sharedClient];
[apiClient fetchAllUsersSuccess:^(id dict) {
for (NSDictionary *object in [dict objectForKey:@"objects"]) {
[ServiceUser addUserFromDictionary:object
inContext:self.managedObjectContext];
}
NSError *error= nil;
[self.managedObjectContext save:&error];
if (error) {
NSLog(@"%@", error);
}
} failure:^(NSError * error) {
NSLog(@"%@", error);
}];