Last active
April 11, 2016 20:23
-
-
Save brentsimmons/4873cedd2773318d495e0952f20868bb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* The rules outlined in Comparing Reactive and Traditional represent the business logic | |
for contacting the server: coalesce requests over a timeout period, | |
coalesce non-unique consecutive requests, and ignore requests shorter | |
than a specified length. If I’ve learnt anything in nearly 30 years | |
of writing software, it’s you don’t want to put business logic in the UI. | |
Working with UI is complicated enough without embedding your business logic there. | |
That’s why the business logic is embedded in the Fetcher object | |
– mostly in the -fetchQuery:error: method. | |
Because we’re coalescing calls, having a method with a completion handler | |
isn’t appropriate. One unifying theme in Apple’s use of completion handlers | |
is they are ALWAYS called – either with a result or an error – the block is never just ignored. | |
Because we plan to ignore many calls to our query method based on the business logic, | |
either a property with a handler block or a delegate is appropriate. | |
I chose a delegate, because they still have slightly more historical precedence. | |
I inferred from Brent’s implementation the rule that new queries should cancel | |
incomplete queries. I’m not certain whether that’s correct, but it seemed appropriate | |
to prevent responses coming out of order.*/ | |
#import "ViewController.h" | |
#import "Fetcher.h" | |
@interface ViewController () <UITableViewDataSource, UITableViewDelegate, FetcherDelegate> | |
@property (nonatomic, strong) IBOutlet UITextField *queryField; | |
@property (nonatomic, strong) IBOutlet UITableView *tableView; | |
@property (nonatomic, strong) NSMutableArray<FetcherResult *> *messages; | |
@end | |
@implementation ViewController | |
- (void)viewDidLoad | |
{ | |
self.messages = [NSMutableArray array]; | |
[super viewDidLoad]; | |
[self.queryField addTarget:self action:@selector(queryFieldDidChange:) forControlEvents:UIControlEventEditingChanged]; | |
self.tableView.dataSource = self; | |
self.tableView.delegate = self; | |
self.fetcher.delegate = self; | |
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"]; | |
} | |
- (IBAction)refreshTable:(id)sender | |
{ | |
// clear the messages list and reload the query | |
self.messages = [NSMutableArray array]; | |
[self.tableView reloadData]; | |
[self.fetcher reset]; | |
NSString *query = self.queryField.text ?: @""; | |
// Probably shouldn't ignore the result or error here… | |
[self.fetcher fetchQuery:query error:NULL]; | |
} | |
- (void)queryFieldDidChange:(id)sender | |
{ | |
NSString *query = self.queryField.text ?: @""; | |
// Probably shouldn't ignore the result or error here… | |
[self.fetcher fetchQuery:query error:NULL]; | |
} | |
#pragma mark - FetcherDelegate | |
- (void)fetcher:(Fetcher *)fetcher didReceiveResult:(nullable FetcherResult *)result forQuery:(NSString *)query error:(nullable NSError *)error | |
{ | |
// We're not guaranteed to be on the main queue… | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
if (error) | |
{ | |
// We shouldn't ignore errors… | |
return; | |
} | |
if (!result) | |
{ | |
// Not certain how we got here without a result… | |
return; | |
} | |
NSInteger newRow = self.messages.count; | |
[self.messages addObject:result]; | |
[self.tableView beginUpdates]; | |
[self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:newRow inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic]; | |
[self.tableView endUpdates]; | |
}); | |
} | |
#pragma mark - UITableViewDataSource | |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section | |
{ | |
return self.messages.count; | |
} | |
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; | |
FetcherResult *result = self.messages[indexPath.row]; | |
cell.textLabel.text = result.text; | |
return cell; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment