UISearchResultsUpdating w / ReactiveCocoa

Estoy construyendo un UISearchController en el que los usuarios escribirán un nombre de usuario, y la aplicación obtendrá los resultados de un service web.

Quiero acelerar las requestes para networkingucir las llamadas de networking mientras el usuario escribe. Usando ReactiveCocoa, ¿cómo se podría implementar esto?

class SearchResultsUpdater: NSObject, UISearchResultsUpdating { func updateSearchResultsForSearchController(searchController: UISearchController) { let text = searchController.searchBar.text let dataSource = searchResultsController.tableView.dataSource as! ... } } 

En realidad es un muy buen enfoque para resolver este problema con ReactiveCocoa.

Desea get el text mientras el usuario escribe o en la palabra 'Reactivo' desea "Transmitir" el text de input. Si su usuario es un typer rápido, desea realizar la request al server solo si el text de búsqueda no cambia por un corto cantidad de time, puede hacerlo usted mismo (usando Delegate, NSTimer), pero con ReactiveCocoa es realmente simple y legible.

 @property (weak, nonatomic) IBOutlet UISearchBar *searchBar; [[textSignal throttle:0.4]subscribeNext:^(NSString* searchText) { [[SearchService shanetworkingInstance]search:searchText completed:^(NSString *searchResult, NSError *error) { NSLog(@"searchResult: %@",searchResult); }]; }]; 

Digamos que su class SearchService devuelve el text searchText y searchText después de 2.5 segundos.

  @implementation SearchService typedef void(^CompletedResults)(NSString *searchResult, NSError *error); - (void)search:(NSString *)text completed:(CompletedResults)handler { NSString *retVal = [NSString stringWithFormat:@"%@ = %@", text, @([text length])]; // Here you should to do your network call and and return the response string dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [NSThread sleepForTimeInterval:2.5]; if (handler){ handler(retVal, nil); } }); } 

Y con solo una línea de código, acelera el text de input.

en realidad, ReactiveCocoa no proporciona una categoría de UISearchBar, pero no es tan complicado de implementar (puede encontrar el alquiler de categoría UISearchBar (RAC))

Lo importante que desea preguntarse es: ¿qué sucederá si ya ha enviado una request al server y antes de recibir la respuesta, el usuario continúa escribiendo? Probablemente quiera cancelar la request anterior (y liberar todos los resources) y enviar una nueva request al server con el nuevo text de búsqueda. otra vez puede hacerlo usted mismo, pero con ReactiveCocoa es muy simple, si acaba de empezar a pensar en las cosas como señales.

Debe envolver su service de búsqueda que devuelva el "flujo" de resultados del server.

 @implementation SearchService - (RACSignal *)search:(NSString *)text { return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [self search:text completed:^(NSString *searchResult, NSError *error) { [subscriber sendNext:searchResult]; [subscriber sendCompleted]; }]; return nil; }]; } 

Ahora todo lo que tiene que hacer es asignar cada text de búsqueda a la señal del resultado del server y llamar a switchToLatest.

 [[[[textSignal throttle:0.4] map:^id(NSString* searchText) { return [[SearchService shanetworkingInstance]search:searchText]; }] switchToLatest] subscribeNext:^(NSString* searchResult) { NSLog(@"searchResult: %@",searchResult); }]; 

Y una cosa más, probablemente cuando obtenga la respuesta del server que desea actualizar la interfaz de usuario. Y tienes que hacerlo en el hilo principal. También aquí con ReactiveCocoa es realmente simple, simplemente agregue deliverOn: RACScheduler.mainThreadScheduler.

 [[[[[textSignal throttle:0.4] map:^id(NSString* searchText) { NSLog(@"Get Text after throttle"); return [[SearchService shanetworkingInstance]search:searchText]; }] switchToLatest] deliverOn:RACScheduler.mainThreadScheduler] subscribeNext:^(NSString* searchResult) { if ([NSThread isMainThread]){ NSLog(@"is MainThread"); } else{ NSLog(@"is not MainThread"); } NSLog(@"searchResult: %@",searchResult); }]; 

Buena suerte 🙂

Si escribe su código con Swift, eche un vistazo a ReactiveSwift GitHub: extensiones reactivas para Swift, inspirado por ReactiveCocoa

Lo siento, no estoy muy familiarizado con la API de RAC Swift, pero esto es posible en la versión Objective-C de RAC llamando al bufferWithTime:onScheduler: método en un RACSignal , por lo que sin duda tendrá una contraparte de Swift.

Ejemplo:

 double sampleRate = 2.0; [[textField.rac_textSignal bufferWithTime:sampleRate onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(RACTuple * x) { NSLog(@"%@", x.last); //Prints the latest string in the tuple. }]; 

Incorporando esto con UISearchController:

 double sampleRate = 2.0; [[[self rac_signalForSelector:@selector(searchBar:textDidChange:) fromProtocol:@protocol(UISearchBarDelegate)] bufferWithTime:sampleRate onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^id(RACTuple * x) { NSLog(@"%@", x.last); }]; 

Aquí hay una publicación en el blog sobre cómo otorgarle a UISearchController una opción rac_textSignal , para que usted no tenga que implementar una function de delegado, mientras que con el código anterior, necesitará tener una searchBar:textDidChange: vacía searchBar:textDidChange: function en SearchResultsUpdater .

Esto puede no ser lo que está buscando, pero puede ayudarlo a llegar allí. Utilicé la extensión NSTimer en esta esencia: https://gist.github.com/natecook1000/b0285b518576b22c4dc8

 let (keySignal, keySink) = Signal<String, NoError>.pipe() func createIntervalSignal(interval: Double) -> Signal<(), NoError> { return Signal { sink in NSTimer.schedule(repeatInterval: interval) { timer in sendNext(sink, ()) } return nil } } func textFieldChanged(sender:UITextField) { sendNext(keySink, sender.text) } let sendNetworkRequestSignal = keySignal |> sampleOn(createIntervalSignal(1.0)) let disposeThis = sendNetworkRequestSignal |> observe(next: { stringVal in }) //send requests in the closure