FLEXRuntime+UIKitHelpers.m 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. //
  2. // FLEXRuntime+UIKitHelpers.m
  3. // FLEX
  4. //
  5. // Created by Tanner Bennett on 12/16/19.
  6. // Copyright © 2019 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXRuntime+UIKitHelpers.h"
  9. #import "FLEXRuntimeUtility.h"
  10. #import "FLEXPropertyAttributes.h"
  11. #import "FLEXArgumentInputViewFactory.h"
  12. #import "FLEXObjectExplorerFactory.h"
  13. #import "FLEXFieldEditorViewController.h"
  14. #import "FLEXMethodCallingViewController.h"
  15. @implementation FLEXProperty (UIKitHelpers)
  16. /// Decide whether to use object or [object class] to get or set property
  17. - (id)targetForPropertyTypeGivenObject:(id)object {
  18. if (!object_isClass(object)) {
  19. if (self.isClassProperty) {
  20. return [object class];
  21. } else {
  22. return object;
  23. }
  24. } else {
  25. if (self.isClassProperty) {
  26. return object;
  27. } else {
  28. // Instance property with a class object
  29. return nil;
  30. }
  31. }
  32. }
  33. - (BOOL)isEditable {
  34. if (self.attributes.isReadOnly) {
  35. return self.likelySetterExists;
  36. }
  37. const FLEXTypeEncoding *typeEncoding = self.attributes.typeEncoding.UTF8String;
  38. return [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:typeEncoding currentValue:nil];
  39. }
  40. - (BOOL)isCallable {
  41. return YES;
  42. }
  43. - (id)currentValueWithTarget:(id)object {
  44. return [self getPotentiallyUnboxedValue:
  45. [self targetForPropertyTypeGivenObject:object]
  46. ];
  47. }
  48. - (NSString *)previewWithTarget:(id)object {
  49. return [FLEXRuntimeUtility
  50. summaryForObject:[self currentValueWithTarget:object]
  51. ];
  52. }
  53. - (UIViewController *)viewerWithTarget:(id)object {
  54. id value = [self currentValueWithTarget:object];
  55. return [FLEXObjectExplorerFactory explorerViewControllerForObject:value];
  56. }
  57. - (UIViewController *)editorWithTarget:(id)object {
  58. id target = [self targetForPropertyTypeGivenObject:object];
  59. return [FLEXFieldEditorViewController target:target property:self];
  60. }
  61. - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object {
  62. id targetForValueCheck = [self targetForPropertyTypeGivenObject:object];
  63. if (!targetForValueCheck) {
  64. // Instance property with a class object
  65. return UITableViewCellAccessoryNone;
  66. }
  67. // We use .tag to store the cached value of .isEditable that is
  68. // initialized by FLEXObjectExplorer in -reloadMetada
  69. if ([self getPotentiallyUnboxedValue:targetForValueCheck]) {
  70. if (self.tag) {
  71. // Editable non-nil value, both
  72. return UITableViewCellAccessoryDetailDisclosureButton;
  73. } else {
  74. // Uneditable non-nil value, chevron only
  75. return UITableViewCellAccessoryDisclosureIndicator;
  76. }
  77. } else {
  78. if (self.tag) {
  79. // Editable nil value, just (i)
  80. return UITableViewCellAccessoryDetailButton;
  81. } else {
  82. // Non-editable nil value, neither
  83. return UITableViewCellAccessoryNone;
  84. }
  85. }
  86. }
  87. @end
  88. @implementation FLEXIvar (UIKitHelpers)
  89. - (BOOL)isEditable {
  90. const FLEXTypeEncoding *typeEncoding = self.typeEncoding.UTF8String;
  91. return [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:typeEncoding currentValue:nil];
  92. }
  93. - (BOOL)isCallable {
  94. return NO;
  95. }
  96. - (id)currentValueWithTarget:(id)object {
  97. if (!object_isClass(object)) {
  98. return [self getPotentiallyUnboxedValue:object];
  99. }
  100. return nil;
  101. }
  102. - (NSString *)previewWithTarget:(id)object {
  103. return [FLEXRuntimeUtility
  104. summaryForObject:[self currentValueWithTarget:object]
  105. ];
  106. }
  107. - (UIViewController *)viewerWithTarget:(id)object {
  108. NSAssert(!object_isClass(object), @"Unreachable state: viewing ivar on class object");
  109. id value = [self currentValueWithTarget:object];
  110. return [FLEXObjectExplorerFactory explorerViewControllerForObject:value];
  111. }
  112. - (UIViewController *)editorWithTarget:(id)object {
  113. NSAssert(!object_isClass(object), @"Unreachable state: editing ivar on class object");
  114. return [FLEXFieldEditorViewController target:object ivar:self];
  115. }
  116. - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object {
  117. if (object_isClass(object)) {
  118. return UITableViewCellAccessoryNone;
  119. }
  120. // Could use .isEditable here, but we use .tag for speed since it is cached
  121. if ([self getPotentiallyUnboxedValue:object]) {
  122. if (self.tag) {
  123. // Editable non-nil value, both
  124. return UITableViewCellAccessoryDetailDisclosureButton;
  125. } else {
  126. // Uneditable non-nil value, chevron only
  127. return UITableViewCellAccessoryDisclosureIndicator;
  128. }
  129. } else {
  130. if (self.tag) {
  131. // Editable nil value, just (i)
  132. return UITableViewCellAccessoryDetailButton;
  133. } else {
  134. // Non-editable nil value, neither
  135. return UITableViewCellAccessoryNone;
  136. }
  137. }
  138. }
  139. @end
  140. @implementation FLEXMethodBase (UIKitHelpers)
  141. - (BOOL)isEditable {
  142. return NO;
  143. }
  144. - (BOOL)isCallable {
  145. return NO;
  146. }
  147. - (id)currentValueWithTarget:(id)object {
  148. // Methods can't be "edited" and have no "value"
  149. return nil;
  150. }
  151. - (NSString *)previewWithTarget:(id)object {
  152. return self.selectorString;
  153. }
  154. - (UIViewController *)viewerWithTarget:(id)object {
  155. // We disallow calling of FLEXMethodBase methods
  156. @throw NSInternalInconsistencyException;
  157. return nil;
  158. }
  159. - (UIViewController *)editorWithTarget:(id)object {
  160. // Methods cannot be edited
  161. @throw NSInternalInconsistencyException;
  162. return nil;
  163. }
  164. - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object {
  165. // We shouldn't be using any FLEXMethodBase objects for this
  166. @throw NSInternalInconsistencyException;
  167. return UITableViewCellAccessoryNone;
  168. }
  169. @end
  170. @implementation FLEXMethod (UIKitHelpers)
  171. - (BOOL)isCallable {
  172. return self.signature != nil;
  173. }
  174. - (UIViewController *)viewerWithTarget:(id)object {
  175. object = self.isInstanceMethod ? object : (object_isClass(object) ? object : [object class]);
  176. return [FLEXMethodCallingViewController target:object method:self];
  177. }
  178. - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object {
  179. if (self.isInstanceMethod) {
  180. if (object_isClass(object)) {
  181. // Instance method from class, can't call
  182. return UITableViewCellAccessoryNone;
  183. } else {
  184. // Instance method from instance, can call
  185. return UITableViewCellAccessoryDisclosureIndicator;
  186. }
  187. } else {
  188. return UITableViewCellAccessoryDisclosureIndicator;
  189. }
  190. }
  191. @end