FLEXShortcut.m 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. //
  2. // FLEXShortcut.m
  3. // FLEX
  4. //
  5. // Created by Tanner Bennett on 12/10/19.
  6. // Copyright © 2019 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXShortcut.h"
  9. #import "FLEXProperty.h"
  10. #import "FLEXPropertyAttributes.h"
  11. #import "FLEXIvar.h"
  12. #import "FLEXMethod.h"
  13. #import "FLEXRuntime+UIKitHelpers.h"
  14. #import "FLEXObjectExplorerFactory.h"
  15. #import "FLEXFieldEditorViewController.h"
  16. #import "FLEXMethodCallingViewController.h"
  17. #import "FLEXMetadataSection.h"
  18. #import "FLEXTableView.h"
  19. #pragma mark - FLEXShortcut
  20. @interface FLEXShortcut () {
  21. id _item;
  22. }
  23. @property (nonatomic, readonly) FLEXMetadataKind metadataKind;
  24. @property (nonatomic, readonly) FLEXProperty *property;
  25. @property (nonatomic, readonly) FLEXMethod *method;
  26. @property (nonatomic, readonly) FLEXIvar *ivar;
  27. @property (nonatomic, readonly) id<FLEXRuntimeMetadata> metadata;
  28. @end
  29. @implementation FLEXShortcut
  30. + (id<FLEXShortcut>)shortcutFor:(id)item {
  31. if ([item conformsToProtocol:@protocol(FLEXShortcut)]) {
  32. return item;
  33. }
  34. FLEXShortcut *shortcut = [self new];
  35. shortcut->_item = item;
  36. if ([item isKindOfClass:[FLEXProperty class]]) {
  37. if (shortcut.property.isClassProperty) {
  38. shortcut->_metadataKind = FLEXMetadataKindClassProperties;
  39. } else {
  40. shortcut->_metadataKind = FLEXMetadataKindProperties;
  41. }
  42. }
  43. if ([item isKindOfClass:[FLEXIvar class]]) {
  44. shortcut->_metadataKind = FLEXMetadataKindIvars;
  45. }
  46. if ([item isKindOfClass:[FLEXMethod class]]) {
  47. // We don't care if it's a class method or not
  48. shortcut->_metadataKind = FLEXMetadataKindMethods;
  49. }
  50. return shortcut;
  51. }
  52. - (id)propertyOrIvarValue:(id)object {
  53. return [self.metadata currentValueWithTarget:object];
  54. }
  55. - (NSString *)titleWith:(id)object {
  56. switch (self.metadataKind) {
  57. case FLEXMetadataKindClassProperties:
  58. case FLEXMetadataKindProperties:
  59. // Since we're outside of the "properties" section, prepend @property for clarity.
  60. return [@"@property " stringByAppendingString:[_item description]];
  61. default:
  62. return [_item description];
  63. }
  64. NSAssert(
  65. [_item isKindOfClass:[NSString class]],
  66. @"Unexpected type: %@", [_item class]
  67. );
  68. return _item;
  69. }
  70. - (NSString *)subtitleWith:(id)object {
  71. if (self.metadataKind) {
  72. return [self.metadata previewWithTarget:object] ?: @"nil";
  73. }
  74. // Item is probably a string; must return empty string since
  75. // these will be gathered into an array. If the object is a
  76. // just a string, it doesn't get a subtitle.
  77. return @"";
  78. }
  79. - (void (^)(UIViewController *))didSelectActionWith:(id)object {
  80. return nil;
  81. }
  82. - (UIViewController *)viewerWith:(id)object {
  83. NSAssert(self.metadataKind, @"Static titles cannot be viewed");
  84. return [self.metadata viewerWithTarget:object];
  85. }
  86. - (UIViewController *)editorWith:(id)object {
  87. NSAssert(self.metadataKind, @"Static titles cannot be edited");
  88. return [self.metadata editorWithTarget:object];
  89. }
  90. - (UITableViewCellAccessoryType)accessoryTypeWith:(id)object {
  91. if (self.metadataKind) {
  92. return [self.metadata suggestedAccessoryTypeWithTarget:object];
  93. }
  94. return UITableViewCellAccessoryNone;
  95. }
  96. - (NSString *)customReuseIdentifierWith:(id)object {
  97. if (self.metadataKind) {
  98. return kFLEXCodeFontCell;
  99. }
  100. return kFLEXMultilineCell;
  101. }
  102. #pragma mark - Helpers
  103. - (FLEXProperty *)property { return _item; }
  104. - (FLEXMethodBase *)method { return _item; }
  105. - (FLEXIvar *)ivar { return _item; }
  106. - (id<FLEXRuntimeMetadata>)metadata { return _item; }
  107. @end
  108. #pragma mark - FLEXActionShortcut
  109. @interface FLEXActionShortcut ()
  110. @property (nonatomic, readonly) NSString *title;
  111. @property (nonatomic, readonly) NSString *(^subtitleFuture)(id);
  112. @property (nonatomic, readonly) UIViewController *(^viewerFuture)(id);
  113. @property (nonatomic, readonly) void (^selectionHandler)(UIViewController *, id);
  114. @property (nonatomic, readonly) UITableViewCellAccessoryType (^accessoryTypeFuture)(id);
  115. @end
  116. @implementation FLEXActionShortcut
  117. + (instancetype)title:(NSString *)title
  118. subtitle:(NSString *(^)(id))subtitle
  119. viewer:(UIViewController *(^)(id))viewer
  120. accessoryType:(UITableViewCellAccessoryType (^)(id))type {
  121. return [[self alloc] initWithTitle:title subtitle:subtitle viewer:viewer selectionHandler:nil accessoryType:type];
  122. }
  123. + (instancetype)title:(NSString *)title
  124. subtitle:(NSString * (^)(id))subtitle
  125. selectionHandler:(void (^)(UIViewController *, id))tapAction
  126. accessoryType:(UITableViewCellAccessoryType (^)(id))type {
  127. return [[self alloc] initWithTitle:title subtitle:subtitle viewer:nil selectionHandler:tapAction accessoryType:type];
  128. }
  129. - (id)initWithTitle:(NSString *)title
  130. subtitle:(id)subtitleFuture
  131. viewer:(id)viewerFuture
  132. selectionHandler:(id)tapAction
  133. accessoryType:(id)accessoryTypeFuture {
  134. NSParameterAssert(title.length);
  135. self = [super init];
  136. if (self) {
  137. id nilBlock = ^id (id obj) { return nil; };
  138. _title = title;
  139. _subtitleFuture = subtitleFuture ?: nilBlock;
  140. _viewerFuture = viewerFuture ?: nilBlock;
  141. _selectionHandler = tapAction;
  142. _accessoryTypeFuture = accessoryTypeFuture ?: nilBlock;
  143. }
  144. return self;
  145. }
  146. - (NSString *)titleWith:(id)object {
  147. return self.title;
  148. }
  149. - (NSString *)subtitleWith:(id)object {
  150. return self.subtitleFuture(object);
  151. }
  152. - (void (^)(UIViewController *))didSelectActionWith:(id)object {
  153. if (self.selectionHandler) {
  154. return ^(UIViewController *host) {
  155. self.selectionHandler(host, object);
  156. };
  157. }
  158. return nil;
  159. }
  160. - (UIViewController *)viewerWith:(id)object {
  161. return self.viewerFuture(object);
  162. }
  163. - (UITableViewCellAccessoryType)accessoryTypeWith:(id)object {
  164. return self.accessoryTypeFuture(object);
  165. }
  166. - (NSString *)customReuseIdentifierWith:(id)object {
  167. if (!self.subtitleFuture(object)) {
  168. // The text is more centered with this style if there is no subtitle
  169. return kFLEXDefaultCell;
  170. }
  171. return nil;
  172. }
  173. @end