FLEXObjectExplorerFactory.m 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. //
  2. // FLEXObjectExplorerFactory.m
  3. // Flipboard
  4. //
  5. // Created by Ryan Olson on 5/15/14.
  6. // Copyright (c) 2014 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXObjectExplorerFactory.h"
  9. #import "FLEXGlobalsTableViewController.h"
  10. #import "FLEXAlert.h"
  11. #import "FLEXClassShortcuts.h"
  12. #import "FLEXViewShortcuts.h"
  13. #import "FLEXImageShortcuts.h"
  14. #import "FLEXLayerShortcuts.h"
  15. #import "FLEXColorPreviewSection.h"
  16. #import "FLEXDefaultsContentSection.h"
  17. #import "FLEXBundleShortcuts.h"
  18. #import <objc/runtime.h>
  19. @implementation FLEXObjectExplorerFactory
  20. static NSMutableDictionary<Class, Class> *classesToRegisteredSections = nil;
  21. + (void)initialize
  22. {
  23. if (self == [FLEXObjectExplorerFactory class]) {
  24. #define ClassKey(name) (Class<NSCopying>)[name class]
  25. classesToRegisteredSections = [NSMutableDictionary dictionaryWithDictionary:@{
  26. ClassKey(NSArray) : [FLEXCollectionContentSection class],
  27. ClassKey(NSSet) : [FLEXCollectionContentSection class],
  28. ClassKey(NSDictionary) : [FLEXCollectionContentSection class],
  29. ClassKey(NSUserDefaults) : [FLEXDefaultsContentSection class],
  30. // ClassKey(UIViewController) : [FLEXViewControllerExplorerViewController class],
  31. ClassKey(UIView) : [FLEXViewShortcuts class],
  32. ClassKey(UIImage) : [FLEXImageShortcuts class],
  33. ClassKey(CALayer) : [FLEXLayerShortcuts class],
  34. ClassKey(UIColor) : [FLEXColorPreviewSection class],
  35. ClassKey(NSBundle) : [FLEXBundleShortcuts class],
  36. }];
  37. #undef ClassKey
  38. }
  39. }
  40. + (FLEXObjectExplorerViewController *)explorerViewControllerForObject:(id)object
  41. {
  42. // Can't explore nil
  43. if (!object) {
  44. return nil;
  45. }
  46. // If we're given an object, this will look up it's class hierarchy
  47. // until it finds a registration. This will work for KVC classes,
  48. // since they are children of the original class, and not siblings.
  49. // If we are given an object, object_getClass will return a metaclass,
  50. // and the same thing will happen. FLEXClassShortcuts is the default
  51. // shortcut section for NSObject.
  52. //
  53. // TODO: rename it to FLEXNSObjectShortcuts or something?
  54. Class sectionClass = nil;
  55. Class cls = object_getClass(object);
  56. do {
  57. sectionClass = classesToRegisteredSections[(Class<NSCopying>)cls];
  58. } while (!sectionClass && (cls = [cls superclass]));
  59. if (!sectionClass) {
  60. sectionClass = [FLEXShortcutsSection class];
  61. }
  62. return [FLEXObjectExplorerViewController
  63. exploringObject:object
  64. customSection:[sectionClass forObject:object]
  65. ];
  66. }
  67. + (void)registerExplorerSection:(Class)explorerClass forClass:(Class)objectClass
  68. {
  69. classesToRegisteredSections[(Class<NSCopying>)objectClass] = explorerClass;
  70. }
  71. #pragma mark - FLEXGlobalsEntry
  72. + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row
  73. {
  74. switch (row) {
  75. case FLEXGlobalsRowAppDelegate:
  76. return @"👉 App delegate";
  77. case FLEXGlobalsRowRootViewController:
  78. return @"🌴 Root view controller";
  79. case FLEXGlobalsRowProcessInfo:
  80. return @"🚦 NSProcessInfo.processInfo";
  81. case FLEXGlobalsRowUserDefaults:
  82. return @"💾 Preferences (NSUserDefaults)";
  83. case FLEXGlobalsRowMainBundle:
  84. return @"📦 NSBundle.mainBundle";
  85. case FLEXGlobalsRowApplication:
  86. return @"🚀 UIApplication.sharedApplication";
  87. case FLEXGlobalsRowMainScreen:
  88. return @"💻 UIScreen.mainScreen";
  89. case FLEXGlobalsRowCurrentDevice:
  90. return @"📱 UIDevice.currentDevice";
  91. case FLEXGlobalsRowPasteboard:
  92. return @"📋 UIPasteboard.generalPasteboard";
  93. default: return nil;
  94. }
  95. }
  96. + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row
  97. {
  98. switch (row) {
  99. case FLEXGlobalsRowAppDelegate: {
  100. id<UIApplicationDelegate> appDelegate = UIApplication.sharedApplication.delegate;
  101. return [self explorerViewControllerForObject:appDelegate];
  102. }
  103. case FLEXGlobalsRowProcessInfo:
  104. return [self explorerViewControllerForObject:NSProcessInfo.processInfo];
  105. case FLEXGlobalsRowUserDefaults:
  106. return [self explorerViewControllerForObject:NSUserDefaults.standardUserDefaults];
  107. case FLEXGlobalsRowMainBundle:
  108. return [self explorerViewControllerForObject:NSBundle.mainBundle];
  109. case FLEXGlobalsRowApplication:
  110. return [self explorerViewControllerForObject:UIApplication.sharedApplication];
  111. case FLEXGlobalsRowMainScreen:
  112. return [self explorerViewControllerForObject:UIScreen.mainScreen];
  113. case FLEXGlobalsRowCurrentDevice:
  114. return [self explorerViewControllerForObject:UIDevice.currentDevice];
  115. case FLEXGlobalsRowPasteboard:
  116. return [self explorerViewControllerForObject:UIPasteboard.generalPasteboard];
  117. case FLEXGlobalsRowRootViewController: {
  118. id<UIApplicationDelegate> delegate = UIApplication.sharedApplication.delegate;
  119. if ([delegate respondsToSelector:@selector(window)]) {
  120. return [self explorerViewControllerForObject:delegate.window.rootViewController];
  121. }
  122. return nil;
  123. }
  124. default: return nil;
  125. }
  126. }
  127. + (FLEXGlobalsTableViewControllerRowAction)globalsEntryRowAction:(FLEXGlobalsRow)row
  128. {
  129. switch (row) {
  130. case FLEXGlobalsRowRootViewController: {
  131. // Check if the app delegate responds to -window. If not, present an alert
  132. return ^(FLEXGlobalsTableViewController *host) {
  133. id<UIApplicationDelegate> delegate = UIApplication.sharedApplication.delegate;
  134. if ([delegate respondsToSelector:@selector(window)]) {
  135. UIViewController *explorer = [self explorerViewControllerForObject:
  136. delegate.window.rootViewController
  137. ];
  138. [host.navigationController pushViewController:explorer animated:YES];
  139. } else {
  140. NSString *msg = @"The app delegate doesn't respond to -window";
  141. [FLEXAlert showAlert:@":(" message:msg from:host];
  142. }
  143. };
  144. }
  145. default: return nil;
  146. }
  147. }
  148. @end