Просмотр исходного кода

Added an API to register custom entries in the Globals table view controller.

This way 3rd party applications can extend the functionality of FLEX by adding quick access to important objects in their applications.
I modified FLEXGlobalsTableViewController to keep track of the items to display in the table with a list of FLEXGlobalsTableViewControllerEntry objects which have 2 blocks: one that gives you the name of the entry to display, and one that gives you the view controller to push when that entry is selected. I used a block to make the creation of both lazy because they can change (for example in case the application window changes).
I exposed this partially in the API: users can pass a reference to an object. This way they can set this app lazily in a +load method in some class, and not wait until the object they want to be able to inspect with FLEX is actually created, which simplifies the use (but this may be my brain being contaminated by ReactiveCocoa, so I'm definitely open to criticism!)

Open questions:
Should this API be in FLEXGlobalsTableViewController, which means that users of the API must #import that header, or should it be in FLEXManager?
Javier Soto лет назад: 12
Родитель
Сommit
9547151c38

+ 1 - 1
Classes/Explorer Toolbar/FLEXExplorerViewController.m

@@ -446,7 +446,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 {
     FLEXGlobalsTableViewController *globalsViewController = [[FLEXGlobalsTableViewController alloc] init];
     globalsViewController.delegate = self;
-    globalsViewController.applicationWindow = [[UIApplication sharedApplication] keyWindow];
+    [FLEXGlobalsTableViewController setApplicationWindow:[[UIApplication sharedApplication] keyWindow]];
     UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:globalsViewController];
     [self makeKeyAndPresentViewController:navigationController animated:YES completion:nil];
 }

+ 11 - 1
Classes/Global State Explorers/FLEXGlobalsTableViewController.h

@@ -16,7 +16,17 @@
 
 /// We pretend that one of the app's windows is still the key window, even though the explorer window may have become key.
 /// We want to display debug state about the application, not about this tool.
-@property (nonatomic, strong) UIWindow *applicationWindow;
++ (void)setApplicationWindow:(UIWindow *)applicationWindow;
+
+/// Adds an entry at the bottom of the list of Global State items. Call this method before this view controller is displayed.
+/// @param entryName The string to be displayed in the cell.
+/// @param objectFutureBlock When you tap on the row, information about the object returned by this block will be displayed.
+/// Passing a block that returns an object allows you to display information about an object whose actual pointer may change at runtime (e.g. +currentUser)
+/// @note This method must be called from the main thread.
+/// The objectFutureBlock will be invoked from the main thread and may return nil.
+/// @note The passed block will be copied and retain for the duration of the application, you may want to use __weak references.
++ (void)registerGlobalEntryWithName:(NSString *)entryName
+                  objectFutureBlock:(id(^)(void))objectFutureBlock;
 
 @end
 

+ 223 - 130
Classes/Global State Explorers/FLEXGlobalsTableViewController.m

@@ -15,6 +15,39 @@
 #import "FLEXLiveObjectsTableViewController.h"
 #import "FLEXFileBrowserTableViewController.h"
 
+typedef NSString *(^FLEXGlobalsTableViewControllerEntryNameFuture)(void);
+typedef UIViewController *(^FLEXGlobalsTableViewControllerViewControllerFuture)(void);
+
+static UIWindow *s_applicationWindow = nil;
+static NSMutableArray *s_globalEntries = nil;
+
+@interface FLEXGlobalsTableViewControllerEntry : NSObject
+
+@property (nonatomic, readonly, copy) FLEXGlobalsTableViewControllerEntryNameFuture entryName;
+@property (nonatomic, readonly, copy) FLEXGlobalsTableViewControllerViewControllerFuture viewControllerToPush;
+
++ (instancetype)entryWithName:(FLEXGlobalsTableViewControllerEntryNameFuture)name
+         viewControllerToPush:(FLEXGlobalsTableViewControllerViewControllerFuture)viewControllerToPush;
+
+@end
+
+@implementation FLEXGlobalsTableViewControllerEntry
+
++ (instancetype)entryWithName:(FLEXGlobalsTableViewControllerEntryNameFuture)name
+         viewControllerToPush:(FLEXGlobalsTableViewControllerViewControllerFuture)viewControllerToPush
+{
+    NSParameterAssert(name);
+    NSParameterAssert(viewControllerToPush);
+
+    FLEXGlobalsTableViewControllerEntry *entry = [[self alloc] init];
+    entry->_entryName = [name copy];
+    entry->_viewControllerToPush = [viewControllerToPush copy];
+
+    return entry;
+}
+
+@end
+
 typedef NS_ENUM(NSUInteger, FLEXGlobalsRow) {
     FLEXGlobalsRowAppClasses,
     FLEXGlobalsRowSystemLibraries,
@@ -26,170 +59,230 @@ typedef NS_ENUM(NSUInteger, FLEXGlobalsRow) {
     FLEXGlobalsRowKeyWindow,
     FLEXGlobalsRowMainScreen,
     FLEXGlobalsRowCurrentDevice,
-    FLEXGlobalsRowFileBrowser
+    FLEXGlobalsRowFileBrowser,
+    FLEXGlobalsRowCount
 };
 
 @interface FLEXGlobalsTableViewController ()
 
-@property (nonatomic, strong) NSArray *rows;
+/// [FLEXGlobalsTableViewControllerEntry *]
+@property (nonatomic, readonly, copy) NSArray *entries;
 
 @end
 
 @implementation FLEXGlobalsTableViewController
 
++ (void)initialize
+{
+    if (self == [FLEXGlobalsTableViewController class]) {
+        [self initializeStandardGlobalEntries];
+    }
+}
+
++ (void)initializeStandardGlobalEntries {
+    s_globalEntries = [NSMutableArray array];
+
+    for (FLEXGlobalsRow defaultRowIndex = 0; defaultRowIndex < FLEXGlobalsRowCount; defaultRowIndex++) {
+        FLEXGlobalsTableViewControllerEntryNameFuture title = nil;
+        FLEXGlobalsTableViewControllerViewControllerFuture viewControllerFuture = nil;
+
+        switch (defaultRowIndex) {
+            case FLEXGlobalsRowAppClasses:
+                title = ^NSString *{
+                    return [NSString stringWithFormat:@"📕  %@ Classes", [FLEXUtility applicationName]];;
+                };
+                viewControllerFuture = ^UIViewController *{
+                    FLEXClassesTableViewController *classesViewController = [[FLEXClassesTableViewController alloc] init];
+                    classesViewController.binaryImageName = [FLEXUtility applicationImageName];
+
+                    return classesViewController;
+                };
+                break;
+
+            case FLEXGlobalsRowSystemLibraries: {
+                NSString *titleString = @"📚  System Libraries";
+                title = ^NSString *{
+                    return titleString;
+                };
+                viewControllerFuture = ^UIViewController *{
+                    FLEXLibrariesTableViewController *librariesViewController = [[FLEXLibrariesTableViewController alloc] init];
+                    librariesViewController.title = titleString;
+
+                    return librariesViewController;
+                };
+                break;
+            }
+
+            case FLEXGlobalsRowLiveObjects:
+                title = ^NSString *{
+                    return @"💩  Heap Objects";
+                };
+                viewControllerFuture = ^UIViewController *{
+                    return [[FLEXLiveObjectsTableViewController alloc] init];
+                };
+
+                break;
+
+            case FLEXGlobalsRowAppDelegate:
+                title = ^NSString *{
+                    return [NSString stringWithFormat:@"👉  %@", [[[UIApplication sharedApplication] delegate] class]];
+                };
+                viewControllerFuture = ^UIViewController *{
+                    id <UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
+                    return [FLEXObjectExplorerFactory explorerViewControllerForObject:appDelegate];
+                };
+                break;
+
+            case FLEXGlobalsRowRootViewController:
+                title = ^NSString *{
+                    return [NSString stringWithFormat:@"🌴  %@", [[s_applicationWindow rootViewController] class]];
+                };
+                viewControllerFuture = ^UIViewController *{
+                    UIViewController *rootViewController = [s_applicationWindow rootViewController];
+                    return [FLEXObjectExplorerFactory explorerViewControllerForObject:rootViewController];
+                };
+                break;
+
+            case FLEXGlobalsRowUserDefaults:
+                title = ^NSString *{
+                    return @"🚶  +[NSUserDefaults standardUserDefaults]";
+                };
+                viewControllerFuture = ^UIViewController *{
+                    NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
+                    return [FLEXObjectExplorerFactory explorerViewControllerForObject:standardUserDefaults];
+                };
+                break;
+
+            case FLEXGlobalsRowApplication:
+                title = ^NSString *{
+                    return @"💾  +[UIApplication sharedApplication]";
+                };
+                viewControllerFuture = ^UIViewController *{
+                    UIApplication *sharedApplication = [UIApplication sharedApplication];
+                    return [FLEXObjectExplorerFactory explorerViewControllerForObject:sharedApplication];
+                };
+                break;
+
+            case FLEXGlobalsRowKeyWindow:
+                title = ^NSString *{
+                   return @"🔑  -[UIApplication keyWindow]";
+                };
+                viewControllerFuture = ^UIViewController *{
+                    return [FLEXObjectExplorerFactory explorerViewControllerForObject:s_applicationWindow];
+                };
+                break;
+
+            case FLEXGlobalsRowMainScreen:
+                title = ^NSString *{
+                    return @"💻  +[UIScreen mainScreen]";
+                };
+                viewControllerFuture = ^UIViewController *{
+                    UIScreen *mainScreen = [UIScreen mainScreen];
+                    return [FLEXObjectExplorerFactory explorerViewControllerForObject:mainScreen];
+                };
+                break;
+
+            case FLEXGlobalsRowCurrentDevice:
+                title = ^NSString *{
+                    return @"📱  +[UIDevice currentDevice]";
+                };
+                viewControllerFuture = ^UIViewController *{
+                    UIDevice *currentDevice = [UIDevice currentDevice];
+                    return [FLEXObjectExplorerFactory explorerViewControllerForObject:currentDevice];
+                };
+                break;
+
+            case FLEXGlobalsRowFileBrowser:
+                title = ^NSString *{
+                    return @"📁  File Browser";
+                };
+                viewControllerFuture = ^UIViewController *{
+                    return [[FLEXFileBrowserTableViewController alloc] init];
+                };
+                break;
+            case FLEXGlobalsRowCount:
+                break;
+        }
+
+        NSParameterAssert(title);
+        NSParameterAssert(viewControllerFuture);
+        
+        [s_globalEntries addObject:[FLEXGlobalsTableViewControllerEntry entryWithName:title
+                                                                 viewControllerToPush:viewControllerFuture]];
+    }
+}
+
 - (id)initWithStyle:(UITableViewStyle)style
 {
     self = [super initWithStyle:style];
     if (self) {
         self.title = @"🌎  Global State";
+        _entries = [s_globalEntries copy];
     }
     return self;
 }
 
+#pragma mark - Public
+
++ (void)setApplicationWindow:(UIWindow *)applicationWindow
+{
+    s_applicationWindow = applicationWindow;
+}
+
++ (void)registerGlobalEntryWithName:(NSString *)entryName objectFutureBlock:(id (^)(void))objectFutureBlock
+{
+    NSParameterAssert(entryName);
+    NSParameterAssert(objectFutureBlock);
+    NSAssert([NSThread isMainThread], @"This method must be called from the main thread.");
+
+    entryName = entryName.copy;
+    FLEXGlobalsTableViewControllerEntry *entry = [FLEXGlobalsTableViewControllerEntry entryWithName:^NSString *{
+        return entryName;
+    }
+                                                                               viewControllerToPush:^UIViewController *
+                                                  {
+                                                      return [FLEXObjectExplorerFactory explorerViewControllerForObject:objectFutureBlock()];
+                                                  }];
+
+    [s_globalEntries addObject:entry];    
+}
+
+#pragma mark - UIViewController
+
 - (void)viewDidLoad
 {
     [super viewDidLoad];
-
-    self.rows = @[@(FLEXGlobalsRowLiveObjects),
-                  @(FLEXGlobalsRowFileBrowser),
-                  @(FLEXGlobalsRowSystemLibraries),
-                  @(FLEXGlobalsRowAppClasses),
-                  @(FLEXGlobalsRowAppDelegate),
-                  @(FLEXGlobalsRowRootViewController),
-                  @(FLEXGlobalsRowUserDefaults),
-                  @(FLEXGlobalsRowApplication),
-                  @(FLEXGlobalsRowKeyWindow),
-                  @(FLEXGlobalsRowMainScreen),
-                  @(FLEXGlobalsRowCurrentDevice)];
     
     self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(donePressed:)];
 }
 
+#pragma mark -
+
 - (void)donePressed:(id)sender
 {
     [self.delegate globalsViewControllerDidFinish:self];
 }
 
-
 #pragma mark - Table Data Helpers
 
-- (FLEXGlobalsRow)rowTypeAtIndexPath:(NSIndexPath *)indexPath
+- (FLEXGlobalsTableViewControllerEntry *)globalEntryAtIndexPath:(NSIndexPath *)indexPath
 {
-    return [[self.rows objectAtIndex:indexPath.row] unsignedIntegerValue];
+    return self.entries[indexPath.row];
 }
 
-- (NSString *)titleForRowType:(FLEXGlobalsRow)rowType
+- (NSString *)titleForRowAtIndexPath:(NSIndexPath *)indexPath
 {
-    NSString *title = nil;
-    switch (rowType) {
-        case FLEXGlobalsRowAppClasses:
-            title = [NSString stringWithFormat:@"📕  %@ Classes", [FLEXUtility applicationName]];
-            break;
-            
-        case FLEXGlobalsRowSystemLibraries:
-            title = @"📚  System Libraries";
-            break;
-        
-        case FLEXGlobalsRowLiveObjects:
-            title = @"💩  Heap Objects";
-            break;
-            
-        case FLEXGlobalsRowAppDelegate:
-            title = [NSString stringWithFormat:@"👉  %@", [[[UIApplication sharedApplication] delegate] class]];
-            break;
-            
-        case FLEXGlobalsRowRootViewController:
-            title = [NSString stringWithFormat:@"🌴  %@", [[self.applicationWindow rootViewController] class]];
-            break;
-            
-        case FLEXGlobalsRowUserDefaults:
-            title = @"🚶  +[NSUserDefaults standardUserDefaults]";
-            break;
-            
-        case FLEXGlobalsRowApplication:
-            title = @"💾  +[UIApplication sharedApplication]";
-            break;
-            
-        case FLEXGlobalsRowKeyWindow:
-            title = @"🔑  -[UIApplication keyWindow]";
-            break;
-            
-        case FLEXGlobalsRowMainScreen:
-            title = @"💻  +[UIScreen mainScreen]";
-            break;
-            
-        case FLEXGlobalsRowCurrentDevice:
-            title = @"📱  +[UIDevice currentDevice]";
-            break;
-            
-        case FLEXGlobalsRowFileBrowser:
-            title = @"📁  File Browser";
-            break;
-    }
-    return title;
+    FLEXGlobalsTableViewControllerEntry *entry = [self globalEntryAtIndexPath:indexPath];
+
+    return entry.entryName();
 }
 
-- (UIViewController *)drillDownViewControllerForRowType:(FLEXGlobalsRow)rowType
+- (UIViewController *)viewControllerToPushForRowAtIndexPath:(NSIndexPath *)indexPath
 {
-    UIViewController *viewController = nil;
-    switch (rowType) {
-        case FLEXGlobalsRowAppClasses: {
-            FLEXClassesTableViewController *classesViewController = [[FLEXClassesTableViewController alloc] init];
-            classesViewController.binaryImageName = [FLEXUtility applicationImageName];
-            viewController = classesViewController;
-        } break;
-            
-        case FLEXGlobalsRowSystemLibraries: {
-            FLEXLibrariesTableViewController *librariesViewController = [[FLEXLibrariesTableViewController alloc] init];
-            librariesViewController.title = [self titleForRowType:FLEXGlobalsRowSystemLibraries];
-            viewController = librariesViewController;
-        } break;
-            
-        case FLEXGlobalsRowLiveObjects: {
-            viewController = [[FLEXLiveObjectsTableViewController alloc] init];
-        } break;
-            
-        case FLEXGlobalsRowAppDelegate: {
-            id <UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
-            viewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:appDelegate];
-        } break;
-            
-        case FLEXGlobalsRowRootViewController: {
-            UIViewController *rootViewController = [self.applicationWindow rootViewController];
-            viewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:rootViewController];
-        } break;
-            
-        case FLEXGlobalsRowUserDefaults: {
-            NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
-            viewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:standardUserDefaults];
-        } break;
-            
-        case FLEXGlobalsRowApplication: {
-            UIApplication *sharedApplication = [UIApplication sharedApplication];
-            viewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:sharedApplication];
-        } break;
-            
-        case FLEXGlobalsRowKeyWindow: {
-            viewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:self.applicationWindow];
-        } break;
-            
-        case FLEXGlobalsRowMainScreen: {
-            UIScreen *mainScreen = [UIScreen mainScreen];
-            viewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:mainScreen];
-        } break;
-            
-        case FLEXGlobalsRowCurrentDevice: {
-            UIDevice *currentDevice = [UIDevice currentDevice];
-            viewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:currentDevice];
-        } break;
-            
-        case FLEXGlobalsRowFileBrowser: {
-            viewController = [[FLEXFileBrowserTableViewController alloc] init];
-        }
-    }
-    return viewController;
-}
+    FLEXGlobalsTableViewControllerEntry *entry = [self globalEntryAtIndexPath:indexPath];
 
+    return entry.viewControllerToPush();
+}
 
 #pragma mark - Table View Data Source
 
@@ -200,7 +293,7 @@ typedef NS_ENUM(NSUInteger, FLEXGlobalsRow) {
 
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 {
-    return [self.rows count];
+    return [self.entries count];
 }
 
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
@@ -212,8 +305,8 @@ typedef NS_ENUM(NSUInteger, FLEXGlobalsRow) {
         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
         cell.textLabel.font = [FLEXUtility defaultTableViewCellLabelFont];
     }
-    FLEXGlobalsRow rowType = [self rowTypeAtIndexPath:indexPath];
-    cell.textLabel.text = [self titleForRowType:rowType];
+
+    cell.textLabel.text = [self titleForRowAtIndexPath:indexPath];
     
     return cell;
 }
@@ -223,9 +316,9 @@ typedef NS_ENUM(NSUInteger, FLEXGlobalsRow) {
 
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
 {
-    FLEXGlobalsRow rowType = [self rowTypeAtIndexPath:indexPath];
-    UIViewController *drillDownViewController = [self drillDownViewControllerForRowType:rowType];
-    [self.navigationController pushViewController:drillDownViewController animated:YES];
+    UIViewController *viewControllerToPush = [self viewControllerToPushForRowAtIndexPath:indexPath];
+
+    [self.navigationController pushViewController:viewControllerToPush animated:YES];
 }
 
 @end