FLEXMethodCallingViewController.m 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. //
  2. // FLEXMethodCallingViewController.m
  3. // Flipboard
  4. //
  5. // Created by Ryan Olson on 5/23/14.
  6. // Copyright (c) 2020 FLEX Team. All rights reserved.
  7. //
  8. #import "FLEXMethodCallingViewController.h"
  9. #import "FLEXRuntimeUtility.h"
  10. #import "FLEXFieldEditorView.h"
  11. #import "FLEXObjectExplorerFactory.h"
  12. #import "FLEXObjectExplorerViewController.h"
  13. #import "FLEXArgumentInputView.h"
  14. #import "FLEXArgumentInputViewFactory.h"
  15. #import "FLEXUtility.h"
  16. @interface FLEXMethodCallingViewController ()
  17. @property (nonatomic, readonly) FLEXMethod *method;
  18. @end
  19. @implementation FLEXMethodCallingViewController
  20. + (instancetype)target:(id)target method:(FLEXMethod *)method {
  21. return [[self alloc] initWithTarget:target method:method];
  22. }
  23. - (id)initWithTarget:(id)target method:(FLEXMethod *)method {
  24. NSParameterAssert(method.isInstanceMethod == !object_isClass(target));
  25. self = [super initWithTarget:target data:method commitHandler:nil];
  26. if (self) {
  27. self.title = method.isInstanceMethod ? @"Method: " : @"Class Method: ";
  28. self.title = [self.title stringByAppendingString:method.selectorString];
  29. }
  30. return self;
  31. }
  32. - (void)viewDidLoad {
  33. [super viewDidLoad];
  34. #if !TARGET_OS_TV
  35. self.actionButton.title = @"Call";
  36. #else
  37. [self.actionButton setTitle:@"Call" forState:UIControlStateNormal];
  38. #endif
  39. // Configure field editor view
  40. self.fieldEditorView.argumentInputViews = [self argumentInputViews];
  41. self.fieldEditorView.fieldDescription = [NSString stringWithFormat:
  42. @"Signature:\n%@\n\nReturn Type:\n%s",
  43. self.method.description, (char *)self.method.returnType
  44. ];
  45. }
  46. - (NSArray<FLEXArgumentInputView *> *)argumentInputViews {
  47. Method method = self.method.objc_method;
  48. NSArray *methodComponents = [FLEXRuntimeUtility prettyArgumentComponentsForMethod:method];
  49. NSMutableArray<FLEXArgumentInputView *> *argumentInputViews = [NSMutableArray new];
  50. unsigned int argumentIndex = kFLEXNumberOfImplicitArgs;
  51. for (NSString *methodComponent in methodComponents) {
  52. char *argumentTypeEncoding = method_copyArgumentType(method, argumentIndex);
  53. FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:argumentTypeEncoding];
  54. free(argumentTypeEncoding);
  55. inputView.backgroundColor = self.view.backgroundColor;
  56. inputView.title = methodComponent;
  57. [argumentInputViews addObject:inputView];
  58. argumentIndex++;
  59. }
  60. return argumentInputViews;
  61. }
  62. - (void)actionButtonPressed:(id)sender {
  63. // Gather arguments
  64. NSMutableArray *arguments = [NSMutableArray new];
  65. for (FLEXArgumentInputView *inputView in self.fieldEditorView.argumentInputViews) {
  66. // Use NSNull as a nil placeholder; it will be interpreted as nil
  67. [arguments addObject:inputView.inputValue ?: NSNull.null];
  68. }
  69. // Call method
  70. NSError *error = nil;
  71. id returnValue = [FLEXRuntimeUtility
  72. performSelector:self.method.selector
  73. onObject:self.target
  74. withArguments:arguments
  75. error:&error
  76. ];
  77. // Dismiss keyboard and handle committed changes
  78. [super actionButtonPressed:sender];
  79. // Display return value or error
  80. if (error) {
  81. [FLEXAlert showAlert:@"Method Call Failed" message:error.localizedDescription from:self];
  82. } else if (returnValue) {
  83. // For non-nil (or void) return types, push an explorer view controller to display the returned object
  84. returnValue = [FLEXRuntimeUtility potentiallyUnwrapBoxedPointer:returnValue type:self.method.returnType];
  85. FLEXObjectExplorerViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:returnValue];
  86. [self.navigationController pushViewController:explorer animated:YES];
  87. } else {
  88. [self exploreObjectOrPopViewController:returnValue];
  89. }
  90. }
  91. - (FLEXMethod *)method {
  92. return _data;
  93. }
  94. @end