You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
6.2 KiB

5 years ago
5 years ago
5 years ago
  1. //
  2. // FLEXLibrariesTableViewController.m
  3. // Flipboard
  4. //
  5. // Created by Ryan Olson on 2014-05-02.
  6. // Copyright (c) 2014 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXLibrariesTableViewController.h"
  9. #import "FLEXUtility.h"
  10. #import "FLEXClassesTableViewController.h"
  11. #import "FLEXObjectExplorerFactory.h"
  12. #import <objc/runtime.h>
  13. @interface FLEXLibrariesTableViewController ()
  14. @property (nonatomic) NSArray<NSString *> *imageNames;
  15. @property (nonatomic) NSArray<NSString *> *filteredImageNames;
  16. @property (nonatomic) NSString *headerTitle;
  17. @property (nonatomic) Class foundClass;
  18. @end
  19. @implementation FLEXLibrariesTableViewController
  20. - (id)initWithStyle:(UITableViewStyle)style
  21. {
  22. self = [super initWithStyle:style];
  23. if (self) {
  24. [self loadImageNames];
  25. }
  26. return self;
  27. }
  28. - (void)viewDidLoad
  29. {
  30. [super viewDidLoad];
  31. self.showsSearchBar = YES;
  32. [self updateHeaderTitle];
  33. }
  34. - (void)updateHeaderTitle
  35. {
  36. if (self.foundClass) {
  37. self.headerTitle = @"Looking for this?";
  38. } else if (self.imageNames.count == self.filteredImageNames.count) {
  39. // Unfiltered
  40. self.headerTitle = [NSString stringWithFormat:@"%@ libraries", @(self.imageNames.count)];
  41. } else {
  42. self.headerTitle = [NSString
  43. stringWithFormat:@"%@ of %@ libraries",
  44. @(self.filteredImageNames.count), @(self.imageNames.count)
  45. ];
  46. }
  47. }
  48. #pragma mark - FLEXGlobalsEntry
  49. + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row {
  50. return @"📚 System Libraries";
  51. }
  52. + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row {
  53. FLEXLibrariesTableViewController *librariesViewController = [self new];
  54. librariesViewController.title = [self globalsEntryTitle:row];
  55. return librariesViewController;
  56. }
  57. #pragma mark - Binary Images
  58. - (void)loadImageNames
  59. {
  60. unsigned int imageNamesCount = 0;
  61. const char **imageNames = objc_copyImageNames(&imageNamesCount);
  62. if (imageNames) {
  63. NSMutableArray<NSString *> *imageNameStrings = [NSMutableArray array];
  64. NSString *appImageName = [FLEXUtility applicationImageName];
  65. for (unsigned int i = 0; i < imageNamesCount; i++) {
  66. const char *imageName = imageNames[i];
  67. NSString *imageNameString = [NSString stringWithUTF8String:imageName];
  68. // Skip the app's image. We're just showing system libraries and frameworks.
  69. if (![imageNameString isEqual:appImageName]) {
  70. [imageNameStrings addObject:imageNameString];
  71. }
  72. }
  73. // Sort alphabetically
  74. self.imageNames = [imageNameStrings sortedArrayWithOptions:0 usingComparator:^NSComparisonResult(NSString *name1, NSString *name2) {
  75. NSString *shortName1 = [self shortNameForImageName:name1];
  76. NSString *shortName2 = [self shortNameForImageName:name2];
  77. return [shortName1 caseInsensitiveCompare:shortName2];
  78. }];
  79. free(imageNames);
  80. }
  81. }
  82. - (NSString *)shortNameForImageName:(NSString *)imageName
  83. {
  84. NSArray<NSString *> *components = [imageName componentsSeparatedByString:@"/"];
  85. if (components.count >= 2) {
  86. return [NSString stringWithFormat:@"%@/%@", components[components.count - 2], components[components.count - 1]];
  87. }
  88. return imageName.lastPathComponent;
  89. }
  90. - (void)setImageNames:(NSArray<NSString *> *)imageNames
  91. {
  92. if (![_imageNames isEqual:imageNames]) {
  93. _imageNames = imageNames;
  94. self.filteredImageNames = imageNames;
  95. }
  96. }
  97. #pragma mark - Filtering
  98. - (void)updateSearchResults:(NSString *)searchText
  99. {
  100. if (searchText.length) {
  101. NSPredicate *searchPredicate = [NSPredicate predicateWithBlock:^BOOL(NSString *evaluatedObject, NSDictionary<NSString *, id> *bindings) {
  102. BOOL matches = NO;
  103. NSString *shortName = [self shortNameForImageName:evaluatedObject];
  104. if ([shortName rangeOfString:searchText options:NSCaseInsensitiveSearch].length > 0) {
  105. matches = YES;
  106. }
  107. return matches;
  108. }];
  109. self.filteredImageNames = [self.imageNames filteredArrayUsingPredicate:searchPredicate];
  110. } else {
  111. self.filteredImageNames = self.imageNames;
  112. }
  113. self.foundClass = NSClassFromString(searchText);
  114. [self updateHeaderTitle];
  115. [self.tableView reloadData];
  116. }
  117. #pragma mark - Table View Data Source
  118. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  119. {
  120. return self.filteredImageNames.count + (self.foundClass ? 1 : 0);
  121. }
  122. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  123. {
  124. static NSString *cellIdentifier = @"Cell";
  125. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
  126. if (!cell) {
  127. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
  128. cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  129. cell.textLabel.font = [FLEXUtility defaultTableViewCellLabelFont];
  130. }
  131. NSString *executablePath;
  132. if (self.foundClass) {
  133. if (indexPath.row == 0) {
  134. cell.textLabel.text = [NSString stringWithFormat:@"Class \"%@\"", self.searchText];
  135. return cell;
  136. } else {
  137. executablePath = self.filteredImageNames[indexPath.row-1];
  138. }
  139. } else {
  140. executablePath = self.filteredImageNames[indexPath.row];
  141. }
  142. cell.textLabel.text = [self shortNameForImageName:executablePath];
  143. return cell;
  144. }
  145. - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
  146. {
  147. return self.headerTitle;
  148. }
  149. #pragma mark - Table View Delegate
  150. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  151. {
  152. if (indexPath.row == 0 && self.foundClass) {
  153. [self.navigationController pushViewController:[FLEXObjectExplorerFactory
  154. explorerViewControllerForObject:self.foundClass
  155. ] animated:YES];
  156. } else {
  157. [self.navigationController pushViewController:[FLEXClassesTableViewController
  158. binaryImageName:self.filteredImageNames[self.foundClass ? 0 : indexPath.row]
  159. ] animated:YES];
  160. }
  161. }
  162. @end