FLEXObjectExplorerFactory.m 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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 "FLEXGlobalsViewController.h"
  10. #import "FLEXAlert.h"
  11. #import "FLEXClassShortcuts.h"
  12. #import "FLEXViewShortcuts.h"
  13. #import "FLEXViewControllerShortcuts.h"
  14. #import "FLEXImageShortcuts.h"
  15. #import "FLEXLayerShortcuts.h"
  16. #import "FLEXColorPreviewSection.h"
  17. #import "FLEXDefaultsContentSection.h"
  18. #import "FLEXBundleShortcuts.h"
  19. #import "FLEXBlockShortcuts.h"
  20. #import <objc/runtime.h>
  21. @implementation FLEXObjectExplorerFactory
  22. static NSMutableDictionary<Class, Class> *classesToRegisteredSections = nil;
  23. + (void)initialize {
  24. if (self == [FLEXObjectExplorerFactory class]) {
  25. #define ClassKey(name) (Class<NSCopying>)[name class]
  26. #define ClassKeyByName(str) (Class<NSCopying>)NSClassFromString(@ #str)
  27. #define MetaclassKey(meta) (Class<NSCopying>)object_getClass([meta class])
  28. classesToRegisteredSections = [NSMutableDictionary dictionaryWithDictionary:@{
  29. MetaclassKey(NSObject) : [FLEXClassShortcuts class],
  30. ClassKey(NSArray) : [FLEXCollectionContentSection class],
  31. ClassKey(NSSet) : [FLEXCollectionContentSection class],
  32. ClassKey(NSDictionary) : [FLEXCollectionContentSection class],
  33. ClassKey(NSOrderedSet) : [FLEXCollectionContentSection class],
  34. ClassKey(NSUserDefaults) : [FLEXDefaultsContentSection class],
  35. ClassKey(UIViewController) : [FLEXViewControllerShortcuts class],
  36. ClassKey(UIView) : [FLEXViewShortcuts class],
  37. ClassKey(UIImage) : [FLEXImageShortcuts class],
  38. ClassKey(CALayer) : [FLEXLayerShortcuts class],
  39. ClassKey(UIColor) : [FLEXColorPreviewSection class],
  40. ClassKey(NSBundle) : [FLEXBundleShortcuts class],
  41. ClassKeyByName(NSBlock) : [FLEXBlockShortcuts class],
  42. }];
  43. #undef ClassKey
  44. #undef ClassKeyByName
  45. #undef MetaclassKey
  46. }
  47. }
  48. + (FLEXObjectExplorerViewController *)explorerViewControllerForObject:(id)object {
  49. // Can't explore nil
  50. if (!object) {
  51. return nil;
  52. }
  53. // If we're given an object, this will look up it's class hierarchy
  54. // until it finds a registration. This will work for KVC classes,
  55. // since they are children of the original class, and not siblings.
  56. // If we are given an object, object_getClass will return a metaclass,
  57. // and the same thing will happen. FLEXClassShortcuts is the default
  58. // shortcut section for NSObject.
  59. //
  60. // TODO: rename it to FLEXNSObjectShortcuts or something?
  61. Class sectionClass = nil;
  62. Class cls = object_getClass(object);
  63. do {
  64. sectionClass = classesToRegisteredSections[(Class<NSCopying>)cls];
  65. } while (!sectionClass && (cls = [cls superclass]));
  66. if (!sectionClass) {
  67. sectionClass = [FLEXShortcutsSection class];
  68. }
  69. return [FLEXObjectExplorerViewController
  70. exploringObject:object
  71. customSection:[sectionClass forObject:object]
  72. ];
  73. }
  74. + (void)registerExplorerSection:(Class)explorerClass forClass:(Class)objectClass {
  75. classesToRegisteredSections[(Class<NSCopying>)objectClass] = explorerClass;
  76. }
  77. #pragma mark - FLEXGlobalsEntry
  78. + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row {
  79. switch (row) {
  80. case FLEXGlobalsRowAppDelegate:
  81. return @"🎟 App Delegate";
  82. case FLEXGlobalsRowKeyWindow:
  83. return @"🔑 Key Window";
  84. case FLEXGlobalsRowRootViewController:
  85. return @"🌴 Root View Controller";
  86. case FLEXGlobalsRowProcessInfo:
  87. return @"🚦 NSProcessInfo.processInfo";
  88. case FLEXGlobalsRowUserDefaults:
  89. return @"💾 Preferences";
  90. case FLEXGlobalsRowMainBundle:
  91. return @"📦 NSBundle.mainBundle";
  92. case FLEXGlobalsRowApplication:
  93. return @"🚀 UIApplication.sharedApplication";
  94. case FLEXGlobalsRowMainScreen:
  95. return @"💻 UIScreen.mainScreen";
  96. case FLEXGlobalsRowCurrentDevice:
  97. return @"📱 UIDevice.currentDevice";
  98. case FLEXGlobalsRowPasteboard:
  99. return @"📋 UIPasteboard.generalPasteboard";
  100. case FLEXGlobalsRowURLSession:
  101. return @"📡 NSURLSession.sharedSession";
  102. case FLEXGlobalsRowURLCache:
  103. return @"⏳ NSURLCache.sharedURLCache";
  104. case FLEXGlobalsRowNotificationCenter:
  105. return @"🔔 NSNotificationCenter.defaultCenter";
  106. case FLEXGlobalsRowMenuController:
  107. return @"📎 UIMenuController.sharedMenuController";
  108. case FLEXGlobalsRowFileManager:
  109. return @"🗄 NSFileManager.defaultManager";
  110. case FLEXGlobalsRowTimeZone:
  111. return @"🌎 NSTimeZone.systemTimeZone";
  112. case FLEXGlobalsRowLocale:
  113. return @"🗣 NSLocale.currentLocale";
  114. case FLEXGlobalsRowCalendar:
  115. return @"📅 NSCalendar.currentCalendar";
  116. case FLEXGlobalsRowMainRunLoop:
  117. return @"🏃🏻‍♂️ NSRunLoop.mainRunLoop";
  118. case FLEXGlobalsRowMainThread:
  119. return @"🧵 NSThread.mainThread";
  120. case FLEXGlobalsRowOperationQueue:
  121. return @"📚 NSOperationQueue.mainQueue";
  122. default: return nil;
  123. }
  124. }
  125. + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row {
  126. switch (row) {
  127. case FLEXGlobalsRowAppDelegate: {
  128. id<UIApplicationDelegate> appDelegate = UIApplication.sharedApplication.delegate;
  129. return [self explorerViewControllerForObject:appDelegate];
  130. }
  131. case FLEXGlobalsRowProcessInfo:
  132. return [self explorerViewControllerForObject:NSProcessInfo.processInfo];
  133. case FLEXGlobalsRowUserDefaults:
  134. return [self explorerViewControllerForObject:NSUserDefaults.standardUserDefaults];
  135. case FLEXGlobalsRowMainBundle:
  136. return [self explorerViewControllerForObject:NSBundle.mainBundle];
  137. case FLEXGlobalsRowApplication:
  138. return [self explorerViewControllerForObject:UIApplication.sharedApplication];
  139. case FLEXGlobalsRowMainScreen:
  140. return [self explorerViewControllerForObject:UIScreen.mainScreen];
  141. case FLEXGlobalsRowCurrentDevice:
  142. return [self explorerViewControllerForObject:UIDevice.currentDevice];
  143. case FLEXGlobalsRowPasteboard:
  144. return [self explorerViewControllerForObject:UIPasteboard.generalPasteboard];
  145. case FLEXGlobalsRowURLSession:
  146. return [self explorerViewControllerForObject:NSURLSession.sharedSession];
  147. case FLEXGlobalsRowURLCache:
  148. return [self explorerViewControllerForObject:NSURLCache.sharedURLCache];
  149. case FLEXGlobalsRowNotificationCenter:
  150. return [self explorerViewControllerForObject:NSNotificationCenter.defaultCenter];
  151. case FLEXGlobalsRowMenuController:
  152. return [self explorerViewControllerForObject:UIMenuController.sharedMenuController];
  153. case FLEXGlobalsRowFileManager:
  154. return [self explorerViewControllerForObject:NSFileManager.defaultManager];
  155. case FLEXGlobalsRowTimeZone:
  156. return [self explorerViewControllerForObject:NSTimeZone.systemTimeZone];
  157. case FLEXGlobalsRowLocale:
  158. return [self explorerViewControllerForObject:NSLocale.currentLocale];
  159. case FLEXGlobalsRowCalendar:
  160. return [self explorerViewControllerForObject:NSCalendar.currentCalendar];
  161. case FLEXGlobalsRowMainRunLoop:
  162. return [self explorerViewControllerForObject:NSRunLoop.mainRunLoop];
  163. case FLEXGlobalsRowMainThread:
  164. return [self explorerViewControllerForObject:NSThread.mainThread];
  165. case FLEXGlobalsRowOperationQueue:
  166. return [self explorerViewControllerForObject:NSOperationQueue.mainQueue];
  167. case FLEXGlobalsRowKeyWindow:
  168. return [FLEXObjectExplorerFactory
  169. explorerViewControllerForObject:FLEXUtility.appKeyWindow
  170. ];
  171. case FLEXGlobalsRowRootViewController: {
  172. id<UIApplicationDelegate> delegate = UIApplication.sharedApplication.delegate;
  173. if ([delegate respondsToSelector:@selector(window)]) {
  174. return [self explorerViewControllerForObject:delegate.window.rootViewController];
  175. }
  176. return nil;
  177. }
  178. default: return nil;
  179. }
  180. }
  181. + (FLEXGlobalsTableViewControllerRowAction)globalsEntryRowAction:(FLEXGlobalsRow)row {
  182. switch (row) {
  183. case FLEXGlobalsRowRootViewController: {
  184. // Check if the app delegate responds to -window. If not, present an alert
  185. return ^(FLEXGlobalsViewController *host) {
  186. id<UIApplicationDelegate> delegate = UIApplication.sharedApplication.delegate;
  187. if ([delegate respondsToSelector:@selector(window)]) {
  188. UIViewController *explorer = [self explorerViewControllerForObject:
  189. delegate.window.rootViewController
  190. ];
  191. [host.navigationController pushViewController:explorer animated:YES];
  192. } else {
  193. NSString *msg = @"The app delegate doesn't respond to -window";
  194. [FLEXAlert showAlert:@":(" message:msg from:host];
  195. }
  196. };
  197. }
  198. default: return nil;
  199. }
  200. }
  201. @end