AFNetworking HTTPClient sottoclasse con XMLParser
-
26-10-2019 - |
Domanda
Sto scrivendo un piccolo iOS app che interroga un webservice REST XML. Il quadro di rete in uso è AFNetworking .
Situazione
Per interrogare il webservice I sottoclasse AFHTTPClient:
@interface MyApiClient : AFHTTPClient
e nell'attuazione faccio che disponibile come 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;
}
e in initWithBaseURL dico AFNetworking aspettarsi contenuto XML:
[self registerHTTPOperationClass:[AFXMLRequestOperation class]];
Ora posso chiamare getPatch sul singleton dal mio ViewController e nel blocco di successo Inizia il parsing di XML restituito la mia. In NSXMLParserDelegate metodi nella ViewController posso quindi prendere le parti del XML Sono interessato a fare cose e con esso.
Problema
Io voglio avere metodi nella mia HTTPClient Singleton che maniglia tutto ciò che riguarda i modelli di dati webservice e ritorno o elenco dei modelli, invece di XML.
Per esempio io voglio fare qualcosa di simile:
ServerModel *status = [[MyApiClient sharedClient] getServerStatus];
L'ApiClient sarebbe quindi chiamare internamente il webservice, analizzare il codice XML e restituire il modello. Come posso fare ciò? Normalmente avrei usato un delegato che viene chiamata una volta l'XML viene analizzato, ma a causa della natura del Singleton ApiClient ci potrebbero essere più delegati?
La speranza che qualcuno possa fare luce su questo, grazie!
Soluzione
(Ci scusiamo in anticipo per questo "tipo-di" risposta, ma stiamo lavorando per una soluzione migliore ...)
È necessario fare un passo indietro e pensare a vostro disegno con attenzione.
Stai avendo problemi perché hai un'idea che qualcosa nelle vostre esigenze di progettazione per essere un Singleton, ma in entrambi:
1) che non è effettivamente necessario,
2) qualcosa potrebbe essere già esistente che fa quel lavoro per voi (ad esempio HTTP lib si sta utilizzando),
o
3) Stai facendo la cosa sbagliata un Singleton, o non si è porzionato il vostro disegno nelle parti appropriate di lavorare bene con l'idea Singleton
Quindi, mi puoi dire in modo esplicito il motivo per cui si sta andando per un approccio Singleton? E 'solo per garantire che solo una richiesta di rete può succedere in una volta? C'è qualche nozione di statefulness nel vostro oggetto Singleton? Poi io aggiornare questa risposta o un commento, ecc.
( Digressione: Vorrei anche aggiungere che in alcuni casi ci potrebbe essere una vera e propria necessità di un ' forte ' Singleton - e con questo intendo che ci sia davvero una sola istanza possibile e che il meccanismo è cotto direttamente nel tuo oggetto, come si sta facendo - ma questo non è che l'alternativa è un ' debole ' Singleton, con cui intendo l'oggetto principale. che in realtà il lavoro ha un metodo init
normale come al solito, ma l'accesso condiviso a un oggetto comune va via un altro oggetto, che è una sorta di semplice 'fabbrica' che istanzia / tiene l'istanza condivisa. il vantaggio di questa debole idea singoletto è che il codice è più riutilizzabile in contesti diversi - ad esempio, si potrebbe decidere di fare richieste multiple HTTP / sessioni contemporaneamente in un secondo momento - e rende a volte la scrittura di test meno problematici)
Altri suggerimenti
Utilizzare i blocchi invece di delegati.
Dalla mia 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);
}];
}
Ora posso utilizzarlo come:
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);
}];