You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
5.9 KiB

  1. //
  2. // FLEXAlert.m
  3. // FLEX
  4. //
  5. // Created by Tanner Bennett on 8/20/19.
  6. // Copyright © 2019 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXAlert.h"
  9. @interface FLEXAlert ()
  10. @property (nonatomic, readonly) UIAlertController *_controller;
  11. @property (nonatomic, readonly) NSMutableArray<FLEXAlertAction *> *_actions;
  12. @end
  13. #define FLEXAlertActionMutationAssertion() \
  14. NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAlertAction");
  15. @interface FLEXAlertAction ()
  16. @property (nonatomic) UIAlertController *_controller;
  17. @property (nonatomic) NSString *_title;
  18. @property (nonatomic) UIAlertActionStyle _style;
  19. @property (nonatomic) BOOL _disable;
  20. @property (nonatomic) void(^_handler)(UIAlertAction *action);
  21. @property (nonatomic) UIAlertAction *_action;
  22. @end
  23. @implementation FLEXAlert
  24. + (void)showAlert:(NSString *)title message:(NSString *)message from:(UIViewController *)viewController {
  25. [self makeAlert:^(FLEXAlert *make) {
  26. make.title(title).message(message).button(@"Dismiss").cancelStyle();
  27. } showFrom:viewController];
  28. }
  29. #pragma mark Initialization
  30. - (instancetype)initWithController:(UIAlertController *)controller {
  31. self = [super init];
  32. if (self) {
  33. __controller = controller;
  34. __actions = [NSMutableArray new];
  35. }
  36. return self;
  37. }
  38. + (UIAlertController *)make:(FLEXAlertBuilder)block withStyle:(UIAlertControllerStyle)style {
  39. // Create alert builder
  40. FLEXAlert *alert = [[self alloc] initWithController:
  41. [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:style]
  42. ];
  43. // Configure alert
  44. block(alert);
  45. // Add actions
  46. for (FLEXAlertAction *builder in alert._actions) {
  47. [alert._controller addAction:builder.action];
  48. }
  49. return alert._controller;
  50. }
  51. + (void)make:(FLEXAlertBuilder)block withStyle:(UIAlertControllerStyle)style showFrom:(UIViewController *)viewController {
  52. UIAlertController *alert = [self make:block withStyle:style];
  53. [viewController presentViewController:alert animated:YES completion:nil];
  54. }
  55. + (void)makeAlert:(FLEXAlertBuilder)block showFrom:(UIViewController *)viewController {
  56. [self make:block withStyle:UIAlertControllerStyleAlert showFrom:viewController];
  57. }
  58. + (void)makeSheet:(FLEXAlertBuilder)block showFrom:(UIViewController *)viewController {
  59. [self make:block withStyle:UIAlertControllerStyleActionSheet showFrom:viewController];
  60. }
  61. + (UIAlertController *)makeAlert:(FLEXAlertBuilder)block {
  62. return [self make:block withStyle:UIAlertControllerStyleAlert];
  63. }
  64. + (UIAlertController *)makeSheet:(FLEXAlertBuilder)block {
  65. return [self make:block withStyle:UIAlertControllerStyleActionSheet];
  66. }
  67. #pragma mark Configuration
  68. - (FLEXAlertStringProperty)title {
  69. return ^FLEXAlert *(NSString *title) {
  70. if (self._controller.title) {
  71. self._controller.title = [self._controller.title stringByAppendingString:title];
  72. } else {
  73. self._controller.title = title;
  74. }
  75. return self;
  76. };
  77. }
  78. - (FLEXAlertStringProperty)message {
  79. return ^FLEXAlert *(NSString *message) {
  80. if (self._controller.message) {
  81. self._controller.message = [self._controller.message stringByAppendingString:message];
  82. } else {
  83. self._controller.message = message;
  84. }
  85. return self;
  86. };
  87. }
  88. - (FLEXAlertAddAction)button {
  89. return ^FLEXAlertAction *(NSString *title) {
  90. FLEXAlertAction *action = FLEXAlertAction.new.title(title);
  91. action._controller = self._controller;
  92. [self._actions addObject:action];
  93. return action;
  94. };
  95. }
  96. - (FLEXAlertStringArg)textField {
  97. return ^FLEXAlert *(NSString *placeholder) {
  98. [self._controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
  99. textField.placeholder = placeholder;
  100. }];
  101. return self;
  102. };
  103. }
  104. - (FLEXAlertTextField)configuredTextField {
  105. return ^FLEXAlert *(void(^configurationHandler)(UITextField *)) {
  106. [self._controller addTextFieldWithConfigurationHandler:configurationHandler];
  107. return self;
  108. };
  109. }
  110. @end
  111. @implementation FLEXAlertAction
  112. - (FLEXAlertActionStringProperty)title {
  113. return ^FLEXAlertAction *(NSString *title) {
  114. FLEXAlertActionMutationAssertion();
  115. if (self._title) {
  116. self._title = [self._title stringByAppendingString:title];
  117. } else {
  118. self._title = title;
  119. }
  120. return self;
  121. };
  122. }
  123. - (FLEXAlertActionProperty)destructiveStyle {
  124. return ^FLEXAlertAction *() {
  125. FLEXAlertActionMutationAssertion();
  126. self._style = UIAlertActionStyleDestructive;
  127. return self;
  128. };
  129. }
  130. - (FLEXAlertActionProperty)cancelStyle {
  131. return ^FLEXAlertAction *() {
  132. FLEXAlertActionMutationAssertion();
  133. self._style = UIAlertActionStyleCancel;
  134. return self;
  135. };
  136. }
  137. - (FLEXAlertActionBOOLProperty)enabled {
  138. return ^FLEXAlertAction *(BOOL enabled) {
  139. FLEXAlertActionMutationAssertion();
  140. self._disable = !enabled;
  141. return self;
  142. };
  143. }
  144. - (FLEXAlertActionHandler)handler {
  145. return ^FLEXAlertAction *(void(^handler)(NSArray<NSString *> *)) {
  146. FLEXAlertActionMutationAssertion();
  147. // Get weak reference to the alert to avoid block <--> alert retain cycle
  148. __weak __typeof(self._controller) weakController = self._controller;
  149. self._handler = ^(UIAlertAction *action) {
  150. // Strongify that reference and pass the text field strings to the handler
  151. __strong __typeof(weakController) controller = weakController;
  152. NSArray *strings = [controller.textFields valueForKeyPath:@"text"];
  153. handler(strings);
  154. };
  155. return self;
  156. };
  157. }
  158. - (UIAlertAction *)action {
  159. if (self._action) {
  160. return self._action;
  161. }
  162. self._action = [UIAlertAction
  163. actionWithTitle:self._title
  164. style:self._style
  165. handler:self._handler
  166. ];
  167. self._action.enabled = !self._disable;
  168. return self._action;
  169. }
  170. @end