Cómo realizar un sondeo / extracción periódica desde una interfaz REST utilizando AFNetworking en iOS

Estoy creando una aplicación de "monitoreo" en mi iPhone. Estoy usando AFNetworking-2.0. Tengo un server de background que expone una interfaz RESTful escrita en Python3 / tornado.

Dependiendo del nivel de ViewController que estoy, quiero sondear diferentes datos con diferentes consultas (el foco de la aplicación ajusta el foco de las consultas). En interés de "Make it Work", he configurado lo siguiente:

 #pragma mark - Pull Loop - (void) forkPull { NSString* uri = [NSString stringWithFormat: @"%@/valves", Site.current.serialID]; [[HttpConnection current] GET: uri parameters: @{} success:^(NSURLSessionDataTask* task, id responseObject){ [Site.current performSelectorOnMainThread: @selector(fromDoc:) withObject:responseObject waitUntilDone:YES]; NSTimeInterval delay = 60; // default poll period // attempt to hone in if we have valid lastTouch info if (Site.current.touched != nil) { NSDate *futureTick = [Site.current.touched dateByAddingTimeInterval: 65]; if ([futureTick compare: [NSDate date]] == NSOrdenetworkingDescending) { delay = futureTick.timeIntervalSinceNow; } } [self performSelector: @selector(forkPull) withObject:nil afterDelay:delay]; NSLog(@"%@ forkPull again in %f", self, delay); } failure:^(NSURLSessionDataTask* task, NSError* error){ NSLog(@"%@ forkPull error: %@ (uri=%@)", self, error, uri); [self performSelector: @selector(forkPull) withObject:nil afterDelay:60]; } ]; } - (void) stopPull { [NSObject cancelPreviousPerformRequestsWithTarget: self]; } #pragma mark - View Management -(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear: animated]; .... [self forkPull]; // start up polling while I'm visible } -(void) viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self stopPull]; // I'm going away, so shut down the pull loop? ... } 

Básicamente, cuando aparece la vista del controller, envía una consulta REST (cuando regresa asíncronamente, actualizará el model en los methods fromDoc: el controller tiene relaciones KVO configuradas, lo que provocará cambios en la interfaz de usuario. Una vez completada la actualización, es capaz de aproximarse sobre cuándo debería hacer el siguiente pull y progtwigr eso con performSelector:withObject:afterDelay: Cuando otro controller toma el centro del escenario, el método viewWillDisappear: intenta detener cualquier forkPull s que se haya puesto en queue.

Mientras esto funciona un poco. Estoy bastante seguro de que no pasa la testing "Hazlo bien". Soy ingenuo acerca de cómo funcionan todas las tareas y el background, pero me parece que AFNetworking agrega su propio nivel de ellas, por lo que mi stopPull podría no ser efectivo. He visto alguna evidencia de eso con mi salida NSLog , donde parece que los controlleres que no están en la parte superior, todavía tienen loops en ejecución.

Pero estoy seguro de que otros han hecho este tipo de patrón antes. Me encantaría saber cómo arquitecto mejor / implementar esto. Estoy buscando a alguien para compartir el patrón que han utilizado para hacer las consultas REST semidérmicas, que ha sido revisado y funciona bien.

Use Grand Central Dispatch:

 @property (strong, nonatomic) dispatch_source_t timer; - (void)startTimer { if (!self.timer) { self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); } if (self.timer) { dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 60ull*NSEC_PER_SEC, 10ull*NSEC_PER_SEC); dispatch_source_set_event_handler(_timer, ^(void) { [self tick]; }); dispatch_resume(_timer); } } - (void)tick { // Do your REST query here } 

Esto invocará su método de tick cada 60 segundos.

Para suspender y reanudar su timer, use dispatch_suspend y dispatch_resume:

 dispatch_suspend(self.timer); dispatch_resume(self.timer); 

Puede invocar dispatch_source_set_timer en cualquier momento posterior para progtwigr ticks antes o retrasarlos hasta más tarde:

 // Fire sooner than 60 seconds, but resume 60s fires after that unsigned long long delaySeconds = arc4random() % 60; dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, delaySeconds * NSEC_PER_SEC), 60ull*NSEC_PER_SEC, 10ull*NSEC_PER_SEC); 

Consulte la Guía de progtwigción de concurrency de Apple para get documentation completa sobre esto.