¿Cómo implementar correctamente una class de Servicios en mi aplicación iOS?

Mi scratcher principal actual: implementando una class model específicamente para llamadas de service a mi aplicación de Rails.

Aquí está el escenario:

  • Tengo una class llamada Servicio que es una subclass de NSObject.
  • El file de implementación tiene algunos methods definidos … miremos doSignUp .
  • Estoy usando AFNetworking para comunicarse con la API.
  • Desde mi SignUpViewController , creo una instancia de mi class de service y llamo doSignUp
  • El método funciona, como se esperaba y recibo la respuesta adecuada del server.

Ahora viene la parte que no entiendo completamente:

  • AFNetworking utiliza bloques para sus llamadas de service.
  • Dentro del bloque de éxito llamo a un método auxiliar denominado handleSignUp (también en la class de service ). Este método esencialmente analiza el JSON y creo un nuevo usuario (NSObject subclass) fuera de él. El método handSignUp devuelve el object User .

En este momento tengo un nuevo object de usuario y tengo que enviar ese object a mi SignUpViewController … ¿Cómo puedo hacer eso?

  • ¿Debo intentar agregar ese object a AppDelegate y acceder a él desde SignUpViewController ? Esta solución podría funcionar para acceder a varias properties globales, pero ¿cuándo sabría el SignUpViewController cuándo acceder a él?
  • ¿Debo intentar agregar una reference al SignUpViewController en la class de service ? Eso parece contraproducente … También podría agregar el método doSignUp y handSignUp al SignUpViewController . Me parece que mi class de service no debería estar al tanto de ningún otro control de vista.

Vea a continuación ejemplos de mi código:

Servicio.h

//Service.h #import <UIKit/UIKit.h> #import "AFNetworking.h" @interface Services : NSObject - (void) doSignUp:(NSMutableDictionary*)params; @end 

Service.m

 // Service.m #import "Services.h" #import "Config.h" @implementation Services - (void) doSignUp:(NSMutableDictionary*)params { NSURL *url = [NSURL URLWithString:@"http://MYURL.COM"]; AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url]; NSURLRequest *request = [httpClient requestWithMethod:@"POST" path:@"signup.json" parameters:params]; AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { [self handleSignUp:JSON]; } failure:nil]; [operation start]; } - (User*) handleSignUp:(NSMutableArray*)json { User * user = nil; if ([[json valueForKey:@"success"] isEqualToString:@"true"]) { // create user here ... } return user; } 

SignUpViewController.h

 #import "Service.h" @interface SignUpViewController : UIViewController { Service * service; } @property (nonatomic, strong) Service * service; 

SignUpViewController.m

 #import "SignUpViewController.h" @interface SignUpViewController () @end @implementation SignUpViewController @synthesize service = __service; - (IBAction) initSignUp:(id)sender { // create params... [self.service doSignUp:params]; } 

Una vez más, todo este código hace lo que se supone que debe hacer … Solo necesito saber cómo debería comunicarse todo. ¿Cómo puedo alertar al SignUpViewController que se ha llamado a handleSignUp y que hay disponible un nuevo object de usuario ?

Gracias por tu time, Andre

Hasta donde puedo ver, no se está dando count de la naturaleza asíncrona de este process: en realidad, dado que las operaciones de networking o IO tardan mucho en completarse, preferimos preferir el acceso asíncrono, es decir: el llamante (View Controller) llama a un post en el API web a través del service y luego DETENGA UNA RESPUESTA. En realidad, el procesamiento de la networking podría realizarse en un process diferente o en un hilo o núcleo del procesador.

Ahora bien, ¿cómo PUEDE SER NOTIFICADA por el service la persona que llama cuando se completa la operación? Para que esto suceda, debemos unir los dos objects usando Delegación: creamos un protocolo (es solo una statement de posts a la que un object responderá) y lo implementamos en nuestro Controlador de Vista, de esa manera, quien lo use lo sabrá Seguro que posts están disponibles.

Luego declararemos un delegado de tipo id en nuestro service que será llamado una vez que se completen las operaciones.

Es casi como importar su ViewController.h en la class de service y dar al service un puntero al controller de vista, pero hacerlo de la manera correcta (sin references circulares y respetando los principios de SOLID)

Ahora algún código:

 //service.h @protocol RemoteApiProtocol <NSObject> @requinetworking -(void) UserLoggingIn; -(void) UserLoggedIn:(User*)user; -(void) UserLoginError:(NSError *)error; @end 

El protocolo funciona como una pequeña interfaz, es un contrato entre dos classs / objects. Luego en su service puede declarar campo y propiedad para el protocolo:

 //Service.h #import <UIKit/UIKit.h> #import "AFNetworking.h" @interface Services : NSObject{ __weak id<RemoteApiProtocol> delegate; } @property (nonatomic, assign) id<RemoteApiProtocol> delegate; - (void) doSignUp:(NSMutableDictionary*)params; @end 

En este punto, implementas el protocolo en tu controller de vista, integrando el contrato que acabas de crear. De esta forma, el server sabrá que el delegado siempre podrá responder a los posts del protocolo.

 //SignUpViewController.h #import "Service.h" @interface SignUpViewController : UIViewController <RemoteApiProtocol> { Service * service; } @property (nonatomic, strong) Service * service; 

Después de implementar el protocolo puede asignar su controller de vista como delegado para el server, de esta forma el server, después de llamar a la aplicación, podrá llamar al delegado con el resultado de la llamada. Para que esto suceda, implementará los posts de protocolos que llamará el service:

 //SignUpViewController.m #import "SignUpViewController.h" @implementation SignUpViewController @synthesize service = __service; - (IBAction) initSignUp:(id)sender { //create the service with params //... //assign self as the delegate self.service.delegate = self; [self.service doSignUp:params]; } #pragma mark - RemoteApiProtocol -(void) UserLoggingIn { //login started, //you can show a spinner or animation here } -(void) UserLoggedIn:(User*)user { //user logged in , do your stuff here } -(void) UserLoginError:(NSError *)error { NSLog(@"error: %@",error); } @end 

Y finalmente, usted llama a los posts en su service, después de llamar a la API en el éxito y el bloque de error:

 // Service.m #import "Services.h" #import "Config.h" @implementation Services - (void) doSignUp:(NSMutableDictionary*)params { NSURL *url = [NSURL URLWithString:@"http://MYURL.COM"]; AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url]; NSURLRequest *request = [httpClient requestWithMethod:@"POST" path:@"signup.json" parameters:params]; AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { [self handleSignUp:JSON]; } failure:nil]; //Signaling the start of the signup operation to the delegate if(self.delegate){ [self.delegate UserLoggingIn]; } [operation start]; } - (User*) handleSignUp:(NSMutableArray*)json { User * user = nil; if ([[json valueForKey:@"success"] isEqualToString:@"true"]) { // create user here ... //call the delegate (view controller) passing the user if(self.delegate){ [self.delegate UserLoggedIn:user]; } }else{ //error handling if(self.delegate){ //NSError *error = ... //[self.delegate UserLoginError:error]; } } //return user; } @end 

Es posible que desee consultar Delegation y / o un Data Source . Consulte mi respuesta aceptada en esta publicación para ver un ejemplo y haga reference a Apple Docs sobre el tema.

Vista del objective C a la comunicación del controller

Espero que esto ayude.

Agregue parameters de locking de callback al doSignUp (probablemente deba cambiarse el nombre a signUpWithParams:success:failure: , de modo que el controller pueda responder al logging que tiene éxito o falla, y tener el context disponible para presentar información relevante para el usuario.

El método de delegación definitivamente funcionaría. Básicamente, podría agregar un delegado a la class de service y establecer el delegado igual al controller de vista antes de llamar a doSignup.

Sin embargo, lo implementaría usando bloques. Agregue un parámetro al método doSignUp que es un bloque de finalización, que se declara en el SignUpViewController. Para ver un ejemplo de cómo pasar bloques como parameters, simplemente eche un vistazo a la fuente desde AFNetworking.