Before
This code starts to get ugly quickly, multiple repeated lines, and the need to put an if/else in my alert callback. The logic was also hard to follow.
- (IBAction)subtract:(id)sender { UIAlertView * alert = [[UIAlertView alloc] initWithTitle:self.tracker.subtractString message:@"How much:" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:self.tracker.subtractString,nil]; alert.alertViewStyle = UIAlertViewStylePlainTextInput; UITextField * alertTextField = [alert textFieldAtIndex:0]; alertTextField.keyboardType = UIKeyboardTypeNumberPad; alertTextField.placeholder = [NSString stringWithFormat:@"How much to %@",self.tracker.subtractString]; self.add=false; [alert show]; }
- (IBAction)add:(id)sender { UIAlertView * alert = [[UIAlertView alloc] initWithTitle:self.tracker.addString message:@"How much:" delegate:self cancelButtonTitle:@"Continue" otherButtonTitles:self.tracker.addString,nil]; alert.alertViewStyle = UIAlertViewStylePlainTextInput; UITextField * alertTextField = [alert textFieldAtIndex:0]; alertTextField.keyboardType = UIKeyboardTypeNumberPad; alertTextField.placeholder = [NSString stringWithFormat:@"How much to %@",self.tracker.addString]; @"Enter how much to heal"; self.add=true; [alert show]; }
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ if (alertView.cancelButtonIndex!=buttonIndex) { int num= [[[alertView textFieldAtIndex:0] text] intValue]; if (self.add) { self.tracker.currentValue+=num; } else { self.tracker.currentValue-=num; } NSError *error; [self.tracker.managedObjectContext save:&error]; //self.currentLabel.text=[NSString stringWithFormat:@"%d",self.tracker.currentValue]; } }The ability to create a block, an inline method, allows this code to be extracted into a common class, and then made FAR simpler for the receiver.
The first step was creating the alert class that can display an alert view. This class assumes that there is only one prompt active at a time.
@interface AlertButtonHelper : NSObject @property(nonatomic, copy) void (^action)(NSString *); - (void)promptForInput:(NSString *)prompt title:(NSString *)title withAcceptButton:(NSString *)acceptButton endingAction:(void (^)(NSString *))action keyboardType:(enum UIKeyboardType)keyboardType; @end @implementation AlertButtonHelper { } // The main command to prompt. - (void)promptForInput:(NSString *)prompt title:(NSString *)title withAcceptButton:(NSString *)acceptButton endingAction:(void (^)(NSString *))action keyboardType:(enum UIKeyboardType)keyboardType { UIAlertView * alert = [[UIAlertView alloc] initWithTitle:title message:prompt delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:acceptButton,nil]; alert.alertViewStyle = UIAlertViewStylePlainTextInput; UITextField * alertTextField = [alert textFieldAtIndex:0]; alertTextField.keyboardType = keyboardType; alertTextField.placeholder = [NSString stringWithFormat:prompt]; [alert show]; self.action=action; } // The standard alert callbakc. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ NSString *value = [alertView textFieldAtIndex:0].text; if (self.action!=nil) { self.action(value); } } @end
I ended up with a bunch of blocks that looked like this. Where the only difference was the one line where the number was used.
void (^actionToTake)(NSString *) = ^(NSString *string) { int num= [string intValue]; self.tracker.currentValue=num; NSError *error; [self.tracker.managedObjectContext save:&error]; };I'm using AppCode, which has a nifty extract as block feature. I used it to transform it into
- (void (^)(NSString *))getModifyFunction:(void (^)(int))modifyValue { void (^actionToTake)(NSString *) = ^(NSString *string) { int num= [string intValue]; modifyValue(num); NSError *error; [self.tracker.managedObjectContext save:&error]; }; return actionToTake; }
void (^actionToTake)(NSString *)= [self getModifyFunction:^(int num) { self.tracker.maxValue = num; }];
The final client code is:
- (IBAction)subtract:(id)sender { NSString *str=[NSString stringWithFormat:@"How much to %@",self.tracker.subtractString] ; void (^actionToTake)(NSString *)= [self getModifyFunction:^(int num) { self.tracker.currentValue -= num; }]; [self.helper promptForInput:str title:self.tracker.subtractString withAcceptButton:self.tracker.subtractString endingAction:actionToTake keyboardType:UIKeyboardTypeNumberPad]; } - (IBAction)maxSelected:(id)sender { NSString *str=[NSString stringWithFormat:@"Enter max Value"] ; void (^actionToTake)(NSString *)= [self getModifyFunction:^(int num) { self.tracker.maxValue = num; }]; [self.helper promptForInput:str title:@"Max Value" withAcceptButton:@"Set Max" endingAction:actionToTake keyboardType:UIKeyboardTypeNumberPad]; } - (void (^)(NSString *))getModifyFunction:(void (^)(int))modifyValue { void (^actionToTake)(NSString *) = ^(NSString *string) { int num= [string intValue]; modifyValue(num); NSError *error; [self.tracker.managedObjectContext save:&error]; }; return actionToTake; } - (IBAction)add:(id)sender { NSString *str=[NSString stringWithFormat:@"How much to %@",self.tracker.addString] ; void (^actionToTake)(NSString *)= [self getModifyFunction:^(int num) { self.tracker.maxValue += num; }]; [self.helper promptForInput:str title:self.tracker.addString withAcceptButton:self.tracker.addString endingAction:actionToTake keyboardType:UIKeyboardTypeNumberPad]; } - (IBAction)currentSelected:(id)sender { NSString *str=[NSString stringWithFormat:@"Enter current Value"] ; void (^actionToTake)(NSString *)= [self getModifyFunction:^(int num) { self.tracker.currentValue = num; }]; [self.helper promptForInput:str title:@"Set Value" withAcceptButton:@"Set Value" endingAction:actionToTake keyboardType:UIKeyboardTypeNumberPad]; }I initialize helper in the init method.
if (self.helper==nil) { self.helper=[[AlertButtonHelper alloc] init]; }The result has strange syntax, but eliminates a lot of redundant code. The use of blocks is a great way to isolate the 'different' items. The next stage would be to extract a protocol for the alert interface, so that the method of displaying alerts could change if desired.