FLEXNetworkHistoryTableViewController.m 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. //
  2. // FLEXNetworkHistoryTableViewController.m
  3. // Flipboard
  4. //
  5. // Created by Ryan Olson on 2/8/15.
  6. // Copyright (c) 2015 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXNetworkHistoryTableViewController.h"
  9. #import "FLEXNetworkTransaction.h"
  10. #import "FLEXNetworkTransactionTableViewCell.h"
  11. #import "FLEXNetworkRecorder.h"
  12. #import "FLEXNetworkTransactionDetailTableViewController.h"
  13. @interface FLEXNetworkHistoryTableViewController () <UISearchDisplayDelegate>
  14. /// Backing model
  15. @property (nonatomic, copy) NSArray *networkTransactions;
  16. @property (nonatomic, copy) NSArray *filteredNetworkTransactions;
  17. @property (nonatomic, strong) UISearchDisplayController *searchController;
  18. @end
  19. @implementation FLEXNetworkHistoryTableViewController
  20. - (instancetype)initWithStyle:(UITableViewStyle)style
  21. {
  22. self = [super initWithStyle:style];
  23. if (self) {
  24. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNewTransactionRecordedNotification:) name:kFLEXNetworkRecorderNewTransactionNotification object:nil];
  25. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTransactionUpdatedNotification:) name:kFLEXNetworkRecorderTransactionUpdatedNotification object:nil];
  26. self.title = @"📡 Network";
  27. }
  28. return self;
  29. }
  30. - (void)dealloc
  31. {
  32. [[NSNotificationCenter defaultCenter] removeObserver:self];
  33. }
  34. - (void)viewDidLoad
  35. {
  36. [super viewDidLoad];
  37. [self.tableView registerClass:[FLEXNetworkTransactionTableViewCell class] forCellReuseIdentifier:kFLEXNetworkTransactionCellIdentifier];
  38. self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  39. self.tableView.rowHeight = [FLEXNetworkTransactionTableViewCell preferredCellHeight];
  40. UISearchBar *searchBar = [[UISearchBar alloc] init];
  41. [searchBar sizeToFit];
  42. self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
  43. self.searchController.delegate = self;
  44. self.searchController.searchResultsDataSource = self;
  45. self.searchController.searchResultsDelegate = self;
  46. [self.searchController.searchResultsTableView registerClass:[FLEXNetworkTransactionTableViewCell class] forCellReuseIdentifier:kFLEXNetworkTransactionCellIdentifier];
  47. self.searchController.searchResultsTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  48. self.searchController.searchResultsTableView.rowHeight = [FLEXNetworkTransactionTableViewCell preferredCellHeight];
  49. self.tableView.tableHeaderView = self.searchController.searchBar;
  50. [self updateTransactions];
  51. }
  52. - (void)updateTransactions
  53. {
  54. self.networkTransactions = [[FLEXNetworkRecorder defaultRecorder] networkTransactions];
  55. }
  56. - (void)handleNewTransactionRecordedNotification:(NSNotification *)notification
  57. {
  58. NSInteger existingRowCount = [self.networkTransactions count];
  59. [self updateTransactions];
  60. NSInteger newRowCount = [self.networkTransactions count];
  61. NSInteger addedRowCount = newRowCount - existingRowCount;
  62. if (self.tableView.contentOffset.y <= 0.0 && addedRowCount > 0) {
  63. // Insert animation if we're at the top.
  64. NSMutableArray *indexPathsToReload = [NSMutableArray array];
  65. for (NSInteger row = 0; row < addedRowCount; row++) {
  66. [indexPathsToReload addObject:[NSIndexPath indexPathForRow:row inSection:0]];
  67. }
  68. [self.tableView insertRowsAtIndexPaths:indexPathsToReload withRowAnimation:UITableViewRowAnimationAutomatic];
  69. } else {
  70. // Maintain the user's position if they've scrolled down.
  71. CGSize existingContentSize = self.tableView.contentSize;
  72. [self.tableView reloadData];
  73. CGFloat contentHeightChange = self.tableView.contentSize.height - existingContentSize.height;
  74. self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, self.tableView.contentOffset.y + contentHeightChange);
  75. }
  76. if (self.searchController.isActive) {
  77. [self updateSearchResultsWithSearchString:self.searchController.searchBar.text];
  78. }
  79. }
  80. - (void)handleTransactionUpdatedNotification:(NSNotification *)notification
  81. {
  82. FLEXNetworkTransaction *transaction = [notification.userInfo objectForKey:kFLEXNetworkRecorderUserInfoTransactionKey];
  83. UITableView *activeTableView = self.searchController.isActive ? self.searchController.searchResultsTableView : self.tableView;
  84. for (FLEXNetworkTransactionTableViewCell *cell in [activeTableView visibleCells]) {
  85. if ([cell.transaction isEqual:transaction]) {
  86. NSIndexPath *indexPath = [activeTableView indexPathForCell:cell];
  87. if (indexPath) {
  88. [activeTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
  89. }
  90. break;
  91. }
  92. }
  93. }
  94. #pragma mark - Table view data source
  95. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  96. {
  97. return 1;
  98. }
  99. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  100. {
  101. NSInteger numberOfRows = 0;
  102. if (tableView == self.tableView) {
  103. numberOfRows = [self.networkTransactions count];
  104. } else if (tableView == self.searchController.searchResultsTableView) {
  105. numberOfRows = [self.filteredNetworkTransactions count];
  106. }
  107. return numberOfRows;
  108. }
  109. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  110. {
  111. FLEXNetworkTransactionTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXNetworkTransactionCellIdentifier forIndexPath:indexPath];
  112. cell.transaction = [self transactionAtIndexPath:indexPath inTableView:tableView];
  113. // Since we insert from the top, assign background colors bottom up to keep them consistent for each transaction.
  114. NSInteger totalRows = [tableView numberOfRowsInSection:indexPath.section];
  115. if ((totalRows - indexPath.row) % 2 == 0) {
  116. cell.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
  117. } else {
  118. cell.backgroundColor = [UIColor whiteColor];
  119. }
  120. return cell;
  121. }
  122. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  123. {
  124. FLEXNetworkTransactionDetailTableViewController *detailViewController = [[FLEXNetworkTransactionDetailTableViewController alloc] init];
  125. detailViewController.transaction = [self transactionAtIndexPath:indexPath inTableView:tableView];
  126. [self.navigationController pushViewController:detailViewController animated:YES];
  127. }
  128. - (FLEXNetworkTransaction *)transactionAtIndexPath:(NSIndexPath *)indexPath inTableView:(UITableView *)tableView
  129. {
  130. FLEXNetworkTransaction *transaction = nil;
  131. if (tableView == self.tableView) {
  132. transaction = [self.networkTransactions objectAtIndex:indexPath.row];
  133. } else if (tableView == self.searchController.searchResultsTableView) {
  134. transaction = [self.filteredNetworkTransactions objectAtIndex:indexPath.row];
  135. }
  136. return transaction;
  137. }
  138. #pragma mark - Search display delegate
  139. - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
  140. {
  141. [self updateSearchResultsWithSearchString:searchString];
  142. // Reload done after the data is filtered asynchronously
  143. return NO;
  144. }
  145. - (void)updateSearchResultsWithSearchString:(NSString *)searchString
  146. {
  147. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  148. NSArray *filteredNetworkTransactions = [self.networkTransactions filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(FLEXNetworkTransaction *transaction, NSDictionary *bindings) {
  149. return [[transaction.request.URL absoluteString] rangeOfString:searchString options:NSCaseInsensitiveSearch].length > 0;
  150. }]];
  151. dispatch_async(dispatch_get_main_queue(), ^{
  152. if ([self.searchDisplayController.searchBar.text isEqual:searchString]) {
  153. self.filteredNetworkTransactions = filteredNetworkTransactions;
  154. [self.searchDisplayController.searchResultsTableView reloadData];
  155. }
  156. });
  157. });
  158. }
  159. @end