ReactiveCocoa	
@iacisclo
Programación imperativa.
• Describe detalladamente los
pasos a seguir.
• Continuos cambios de
estado.
• El orden de ejecución es vital.
• Muchos condicionales.
Programación declarativa
• Tu describes directamente los
resultados.
• El orden de ejecución no
importa.
• Sin apenas condicionales.
• No hay que estar pendiente
del estado.
ReactiveCocoa
• Compatible desde iOS 5.0+ y OS X 10.7+.
• Versión actual 2.2.4.
• Combina declarativa y funcional.
• Compatible con código no RAC.
¿Como?	
NSString *name = @"Belén";
NSString *surname = @“_Esteban”;
NSString *completeName = [name stringByAppendingString:surname];
surname = @“_Esteban, háztelo mirar”;
NSLog(@"%@",completeName);
El resultado sería “Belén_Esteban” pero con programación reactiva
sería “Belén_Esteban, háztelo mirar”.
[self.textField.rac_textSignal subscribeNext:^(NSString *value) {
self.lblValue.text = value;
}];
// Imperative
!
for (NSString *device in devices)
if ([device isEqualToString:@"iPod"]) {
self.name = device;
}
}
!
// Reactive
RAC(self, name) = [devices.rac_sequence.signal filter:^BOOL(NSString *device) {
return [device isEqualToString:@"iPod"];
}];
Clases
• RACStream
• RACSignal
• RACSubscriber
• RACSubject
• RACCommand
• RACMulticastConnection
• RACSequence
• RACDisposable
• RACSheduler
RACStream
• El padre de Todos.
• Es un conjunto de valores.
• Las señales contienen RACStreams.
• Los valores se recuperan de forma secuencial.
RACCommand
• Crea y se subscribe a una señal, generalmente
iniciada por UI.
self.button.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id _) {
self.lblValue.text = @"Button tapped";
return [RACSignal empty];
}];
RACCommand *createAccount = [[RACCommand alloc]initWithEnabled:formValid signalBlock:^RACSignal *(id input) {
BOOL success = [IACClient logggin];
return [RACSignal return:@(success)];
}];
!
self.createButton.rac_command = createAccount;
RACSignal
• Envian 3 tipos de eventos: NEXT, ERROR, COMPLETED.
[signal subscribeNext:^(id x) {
NSLog(@"en la subscripción");
} error:^(NSError *error) {
NSLog(@"ha ocurrido un error.");
} completed:^{
NSLog(@"señal completa");
}];
RACSignal
RACSignal *executing = [racCommand.executing deliverOn:RACScheduler.mainThreadScheduler];
!
RACSignal *fieldTextColor = [executing map:^(NSNumber *x) {
return x.boolValue ? [UIColor lightGrayColor] : [UIColor blackColor];
}];
RACSignal *signal = @[@"iPhone",@"iPad",@"iMac",@"iPod"].rac_sequence.signal;
[[signal filter:^BOOL(NSString *value) {
return [value isEqualToString:@"iPad"];
}]subscribeNext:^(NSString *filterValue) {
self.lblValue.text = filterValue;
}];
RACSignal
!
RACSignal *formValid = [RACSignal
combineLatest:@[
self.firstNameField.rac_textSignal,
self.lastNameField.rac_textSignal,
self.emailField.rac_textSignal,
]
reduce:^(NSString *firstName,
NSString *lastName,
NSString *email){
return @(firstName.length > 0 &&
lastName.length > 0 &&
email.length > 0);
}];
RACSubject
• Señal que se puede controlar.
RACSubject *letters = [RACSubject subject];
[letters subscribeNext:^(NSString *letter) {
NSLog(@"%@",letter);
} completed:^{
NSLog(@"complete");
}];
[letters sendNext:@"A"];
[letters sendNext:@"B"];
[letters sendCompleted];
rac_liftSelector:withSignals:
!
self.delegate = [RACSubject subject];
!
[self.navigationController rac_liftSelector:@selector(pushViewController:animated:)
withSignals:self.delegate,[RACSignal return:@YES], nil];
!
/////
!
IACDetailViewController *detailVC = [self.storyboard
instantiateViewControllerWithIdentifier:@"IACDetailViewController"];
!
[self.delegate sendNext:detailVC];
!
RACSubscriber
• Todo objeto que espera o es capaz de
subscribirse a una señal
[[self saveImage:[UIImage imageNamed:@"image1"] withName:@"image1"]then:^RACSignal *{
NSLog(@"image guardada correctamente");
return nil;
}];
!
/////////
!
-(RACSignal *)saveImage:(UIImage *)image withName:(NSString *)name{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:@"savedImage.png"];
NSData *imageData = UIImagePNGRepresentation(image);
BOOL error = [imageData writeToFile:savedImagePath atomically:NO];
if (error) {
[subscriber sendError:nil];
}else{
[subscriber sendCompleted];
}
return nil;
}];
}
RACSequence
• Colecciones, señales guiadas.
RACSequence *devices = @[@"iPhone",@"iPad",@"iPod"].rac_sequence;
RACSequence *computers = @[@"iMac",@"Mac Mini",@"Macbook Air",@"Macbook
Pro"].rac_sequence;
RACSequence *concatenated = [devices concat:computers];
[concatenated.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
RACSequence
• Colecciones, señales guiadas.
RACSignal *devices = @[@"iPhone",@"iPad",@"iPod"].rac_sequence.signal;
RACSignal *sequenced = [[devices
doNext:^(NSString *device) {
NSLog(@"trabajamos con el objeto %@", device);
}]
then:^{
return @[@"iMac",@"Mac Mini",@"Macbook
Air",@"Macbook Pro"].rac_sequence.signal;
}];
[sequenced subscribeNext:^(NSString *computer) {
NSLog(@"%@",computer);
}];
do…
RACSignal *sequenced = [[[[devices
doNext:^(NSString *device) {
!
NSLog(@“antes de la subscripción %@“,device);
!
}]doError:^(NSError *error) {
!
NSLog(@"antes del error: %@", error);
!
}]doCompleted:^{
!
NSLog(@"señal antes de ser completada”);
!
}]then:^{
!
NSLog(@"señal ya completada”);
!
}];
RACDisposable
• Baja o limpieza de señales.
RACDisposable *subscription =
[self.textField.rac_textSignal
subscribeNext:^(NSString *text) {
self.lblValue.text = text;
}];
// en un futuro;
[subscription dispose];
Delegados
• Podemos sustituir delegados facilmente
self.textView.delegate = self;
[[self rac_signalForSelector:@selector(textViewDidChange:)fromProtocol:
@protocol(UITextViewDelegate)]subscribeNext:^(RACTuple *arguments) {
UITextView *textview = arguments.first;
}];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Alert"
message:@"message"
delegate:self
cancelButtonTitle:@"cancel"
otherButtonTitles:@"ok", nil];
[alert show];
[alert.rac_buttonClickedSignal subscribeNext:^(NSNumber *index) {
NSLog(@"%@",index);
}];
Eventos de control
• UIControlEvetTouchDown
• UIControlEventTouchDownRepeat
• UIControlEventTouchDragInside
• UIControlEventTouchDragOutside
• UIControlEventTouchDragEnter
• UIControlEventTouchDragExit
• UIControlEventTouchUpInside
• UIControlEventTouchUpOutside
• UIControlEventTouchCancel
• UIControlEventValueChanged
• UIControlEventEditingDidBegin
• UIControlEventEditingChanged
• UIControlEventEditingDidEnd
• UIControlEventEditingDidEndOnExit
• UIControlEventAllTouchEvents
• UIControlEventAllEditingEvents
• UIControlEventApplicationReserved
• UIControlEventSystemReserved
Eventos de control
[[stepper rac_signalForControlEvents:UIControlEventAllTouchEvents]
subscribeNext:^(UIStepper *stepper) {
self.lblValue.text = [NSString stringWithFormat:@"%f",stepper.value];
}];
RAC(self.textField, textColor) = [[self.textField rac_signalForControlEvents:
UIControlEventAllEvents]map:^id(UITextField *textfield) {
if (textfield.text.length >3) {
return [UIColor blackColor];
}else{
return [UIColor redColor];
}
}];
RACSheduler
• GCD en RAC.
[[self.textField.rac_textSignal deliverOn:[RACScheduler mainThreadScheduler]]
subscribeNext:^(NSString *value) {
self.lblValue.text = value;
}];
RACScheduler *scheduler = [RACScheduler
schedulerWithPriority:RACSchedulerPriorityBackground];
[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
return nil;
}]subscribeOn:scheduler];
RACSheduler
//////
!
[[[self signalForLoadingImage:tweet.profileImageUrl]
deliverOn:[RACScheduler mainThreadScheduler]]
subscribeNext:^(UIImage *image) {
cell.twitterAvatarView.image = image;
}];
!
///////
!
-(RACSignal *)signalForLoadingImage:(NSString *)imageUrl {
RACScheduler *scheduler = [RACScheduler
schedulerWithPriority:RACSchedulerPriorityBackground];
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)
{
NSData *data = [NSData dataWithContentsOfURL:[NSURL
URLWithString:imageUrl]];
UIImage *image = [UIImage imageWithData:data];
[subscriber sendNext:image];
[subscriber sendCompleted];
return nil;
}]subscribeOn:scheduler];
}
Memoria
__weak ViewController *bself = self;
[[self.searchText.rac_textSignal
map:^id(NSString *text) {
return [self isValidSearchText:text] ?
[UIColor whiteColor] : [UIColor yellowColor];
}]
subscribeNext:^(UIColor *color) {
bself.searchText.backgroundColor = color;
}];
Memoria
#import "RACEXTScope.h"
!
!
@weakify(self)
[[self.searchText.rac_textSignal
map:^id(NSString *text) {
return [self isValidSearchText:text] ?
[UIColor whiteColor] : [UIColor yellowColor];
}]
subscribeNext:^(UIColor *color) {
@strongify(self)
self.searchText.backgroundColor = color;
}];
RACObserve(TARGET, KEYPATH)
@weakify(self)
[RACObserve(self, modelArray) subscribeNext:^(id x) {
@strongify(self)
[self.collectionView reloadData];
}];
[[[RACObserve(photoModel, imageData)filter:^BOOL(id value) {
return value != nil;
}]map:^id(id value) {
return [UIImage imageWithData:value];
}]setKeyPath:@keypath(self.imageView, image)
onObject:self.imageView];
RAC(TARGET, …)
RAC(self,dateAdded) = [RACObserve(self.model,dateAdded)map:^(NSDate*date){
return [[ViewModel dateFormatter] stringFromDate:date];
}];
!
RAC(self, name) = self.textField.rac_textSignal;
return;

Reactive cocoa

  • 1.
  • 2.
    Programación imperativa. • Describedetalladamente los pasos a seguir. • Continuos cambios de estado. • El orden de ejecución es vital. • Muchos condicionales.
  • 3.
    Programación declarativa • Tudescribes directamente los resultados. • El orden de ejecución no importa. • Sin apenas condicionales. • No hay que estar pendiente del estado.
  • 4.
    ReactiveCocoa • Compatible desdeiOS 5.0+ y OS X 10.7+. • Versión actual 2.2.4. • Combina declarativa y funcional. • Compatible con código no RAC.
  • 5.
    ¿Como? NSString *name =@"Belén"; NSString *surname = @“_Esteban”; NSString *completeName = [name stringByAppendingString:surname]; surname = @“_Esteban, háztelo mirar”; NSLog(@"%@",completeName); El resultado sería “Belén_Esteban” pero con programación reactiva sería “Belén_Esteban, háztelo mirar”.
  • 6.
  • 7.
    // Imperative ! for (NSString*device in devices) if ([device isEqualToString:@"iPod"]) { self.name = device; } } ! // Reactive RAC(self, name) = [devices.rac_sequence.signal filter:^BOOL(NSString *device) { return [device isEqualToString:@"iPod"]; }];
  • 8.
    Clases • RACStream • RACSignal •RACSubscriber • RACSubject • RACCommand • RACMulticastConnection • RACSequence • RACDisposable • RACSheduler
  • 9.
    RACStream • El padrede Todos. • Es un conjunto de valores. • Las señales contienen RACStreams. • Los valores se recuperan de forma secuencial.
  • 10.
    RACCommand • Crea yse subscribe a una señal, generalmente iniciada por UI. self.button.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id _) { self.lblValue.text = @"Button tapped"; return [RACSignal empty]; }]; RACCommand *createAccount = [[RACCommand alloc]initWithEnabled:formValid signalBlock:^RACSignal *(id input) { BOOL success = [IACClient logggin]; return [RACSignal return:@(success)]; }]; ! self.createButton.rac_command = createAccount;
  • 11.
    RACSignal • Envian 3tipos de eventos: NEXT, ERROR, COMPLETED. [signal subscribeNext:^(id x) { NSLog(@"en la subscripción"); } error:^(NSError *error) { NSLog(@"ha ocurrido un error."); } completed:^{ NSLog(@"señal completa"); }];
  • 12.
    RACSignal RACSignal *executing =[racCommand.executing deliverOn:RACScheduler.mainThreadScheduler]; ! RACSignal *fieldTextColor = [executing map:^(NSNumber *x) { return x.boolValue ? [UIColor lightGrayColor] : [UIColor blackColor]; }]; RACSignal *signal = @[@"iPhone",@"iPad",@"iMac",@"iPod"].rac_sequence.signal; [[signal filter:^BOOL(NSString *value) { return [value isEqualToString:@"iPad"]; }]subscribeNext:^(NSString *filterValue) { self.lblValue.text = filterValue; }];
  • 13.
    RACSignal ! RACSignal *formValid =[RACSignal combineLatest:@[ self.firstNameField.rac_textSignal, self.lastNameField.rac_textSignal, self.emailField.rac_textSignal, ] reduce:^(NSString *firstName, NSString *lastName, NSString *email){ return @(firstName.length > 0 && lastName.length > 0 && email.length > 0); }];
  • 14.
    RACSubject • Señal quese puede controlar. RACSubject *letters = [RACSubject subject]; [letters subscribeNext:^(NSString *letter) { NSLog(@"%@",letter); } completed:^{ NSLog(@"complete"); }]; [letters sendNext:@"A"]; [letters sendNext:@"B"]; [letters sendCompleted];
  • 15.
    rac_liftSelector:withSignals: ! self.delegate = [RACSubjectsubject]; ! [self.navigationController rac_liftSelector:@selector(pushViewController:animated:) withSignals:self.delegate,[RACSignal return:@YES], nil]; ! ///// ! IACDetailViewController *detailVC = [self.storyboard instantiateViewControllerWithIdentifier:@"IACDetailViewController"]; ! [self.delegate sendNext:detailVC]; !
  • 16.
    RACSubscriber • Todo objetoque espera o es capaz de subscribirse a una señal [[self saveImage:[UIImage imageNamed:@"image1"] withName:@"image1"]then:^RACSignal *{ NSLog(@"image guardada correctamente"); return nil; }]; ! ///////// ! -(RACSignal *)saveImage:(UIImage *)image withName:(NSString *)name{ return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:@"savedImage.png"]; NSData *imageData = UIImagePNGRepresentation(image); BOOL error = [imageData writeToFile:savedImagePath atomically:NO]; if (error) { [subscriber sendError:nil]; }else{ [subscriber sendCompleted]; } return nil; }]; }
  • 17.
    RACSequence • Colecciones, señalesguiadas. RACSequence *devices = @[@"iPhone",@"iPad",@"iPod"].rac_sequence; RACSequence *computers = @[@"iMac",@"Mac Mini",@"Macbook Air",@"Macbook Pro"].rac_sequence; RACSequence *concatenated = [devices concat:computers]; [concatenated.signal subscribeNext:^(id x) { NSLog(@"%@",x); }];
  • 18.
    RACSequence • Colecciones, señalesguiadas. RACSignal *devices = @[@"iPhone",@"iPad",@"iPod"].rac_sequence.signal; RACSignal *sequenced = [[devices doNext:^(NSString *device) { NSLog(@"trabajamos con el objeto %@", device); }] then:^{ return @[@"iMac",@"Mac Mini",@"Macbook Air",@"Macbook Pro"].rac_sequence.signal; }]; [sequenced subscribeNext:^(NSString *computer) { NSLog(@"%@",computer); }];
  • 19.
    do… RACSignal *sequenced =[[[[devices doNext:^(NSString *device) { ! NSLog(@“antes de la subscripción %@“,device); ! }]doError:^(NSError *error) { ! NSLog(@"antes del error: %@", error); ! }]doCompleted:^{ ! NSLog(@"señal antes de ser completada”); ! }]then:^{ ! NSLog(@"señal ya completada”); ! }];
  • 20.
    RACDisposable • Baja olimpieza de señales. RACDisposable *subscription = [self.textField.rac_textSignal subscribeNext:^(NSString *text) { self.lblValue.text = text; }]; // en un futuro; [subscription dispose];
  • 21.
    Delegados • Podemos sustituirdelegados facilmente self.textView.delegate = self; [[self rac_signalForSelector:@selector(textViewDidChange:)fromProtocol: @protocol(UITextViewDelegate)]subscribeNext:^(RACTuple *arguments) { UITextView *textview = arguments.first; }]; UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Alert" message:@"message" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"ok", nil]; [alert show]; [alert.rac_buttonClickedSignal subscribeNext:^(NSNumber *index) { NSLog(@"%@",index); }];
  • 22.
    Eventos de control •UIControlEvetTouchDown • UIControlEventTouchDownRepeat • UIControlEventTouchDragInside • UIControlEventTouchDragOutside • UIControlEventTouchDragEnter • UIControlEventTouchDragExit • UIControlEventTouchUpInside • UIControlEventTouchUpOutside • UIControlEventTouchCancel • UIControlEventValueChanged • UIControlEventEditingDidBegin • UIControlEventEditingChanged • UIControlEventEditingDidEnd • UIControlEventEditingDidEndOnExit • UIControlEventAllTouchEvents • UIControlEventAllEditingEvents • UIControlEventApplicationReserved • UIControlEventSystemReserved
  • 23.
    Eventos de control [[stepperrac_signalForControlEvents:UIControlEventAllTouchEvents] subscribeNext:^(UIStepper *stepper) { self.lblValue.text = [NSString stringWithFormat:@"%f",stepper.value]; }]; RAC(self.textField, textColor) = [[self.textField rac_signalForControlEvents: UIControlEventAllEvents]map:^id(UITextField *textfield) { if (textfield.text.length >3) { return [UIColor blackColor]; }else{ return [UIColor redColor]; } }];
  • 24.
    RACSheduler • GCD enRAC. [[self.textField.rac_textSignal deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSString *value) { self.lblValue.text = value; }]; RACScheduler *scheduler = [RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground]; [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { return nil; }]subscribeOn:scheduler];
  • 25.
    RACSheduler ////// ! [[[self signalForLoadingImage:tweet.profileImageUrl] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(UIImage*image) { cell.twitterAvatarView.image = image; }]; ! /////// ! -(RACSignal *)signalForLoadingImage:(NSString *)imageUrl { RACScheduler *scheduler = [RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground]; return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]]; UIImage *image = [UIImage imageWithData:data]; [subscriber sendNext:image]; [subscriber sendCompleted]; return nil; }]subscribeOn:scheduler]; }
  • 26.
    Memoria __weak ViewController *bself= self; [[self.searchText.rac_textSignal map:^id(NSString *text) { return [self isValidSearchText:text] ? [UIColor whiteColor] : [UIColor yellowColor]; }] subscribeNext:^(UIColor *color) { bself.searchText.backgroundColor = color; }];
  • 27.
    Memoria #import "RACEXTScope.h" ! ! @weakify(self) [[self.searchText.rac_textSignal map:^id(NSString *text){ return [self isValidSearchText:text] ? [UIColor whiteColor] : [UIColor yellowColor]; }] subscribeNext:^(UIColor *color) { @strongify(self) self.searchText.backgroundColor = color; }];
  • 28.
    RACObserve(TARGET, KEYPATH) @weakify(self) [RACObserve(self, modelArray)subscribeNext:^(id x) { @strongify(self) [self.collectionView reloadData]; }]; [[[RACObserve(photoModel, imageData)filter:^BOOL(id value) { return value != nil; }]map:^id(id value) { return [UIImage imageWithData:value]; }]setKeyPath:@keypath(self.imageView, image) onObject:self.imageView];
  • 29.
    RAC(TARGET, …) RAC(self,dateAdded) =[RACObserve(self.model,dateAdded)map:^(NSDate*date){ return [[ViewModel dateFormatter] stringFromDate:date]; }]; ! RAC(self, name) = self.textField.rac_textSignal;
  • 30.