UIKonf 2015: iOS API Design

UIKonf 2015: iOS API Design

A talk really isn't a talk without presenter notes and animations! For those, check out the Keynote file here: https://github.com/modocache/talks/blob/master/20150519-uikonf/ios-api-design.key

F921fa5d507b31ef6984fd3d77ae710c?s=128

Brian Gesiak

May 19, 2015
Tweet

Transcript

  1. share your code

  2. share your code

  3. Brian Gesiak
 @modocache

  4. None
  5. None
  6. github.com/Quick/Quick/ issues/300

  7. None
  8. None
  9. None
  10. ✨ BananaUIKit

  11. Series A
 B


  12. Series X
 Y
 Z


  13. abstractions 
 for what will change

  14. None
  15. None
  16. @interface BananaAlertView : UIAlertView + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString

    *)dismissButtonText; @end
  17. None
  18. @interface BananaAlertView : UIAlertView + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString

    *)dismissButtonText; @end
  19. @interface BananaAlertView : UIAlertView + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString

    *)dismissButtonText; @end tintColor:(UIColor *)tintColor;
  20. [BananaAlertView showWithTitleText:@"403 Error" messageText:@"Forbidden banana." dismissButtonText:@"Dang."];

  21. [BananaAlertView showWithTitleText:@"403 Error" messageText:@"Forbidden banana." dismissButtonText:@"Dang."]; No known class method

    for selector 'showWithTitleText:messageText:…' 
  22. BananaUIKit

  23. BananaUIKit 2.0

  24. None
  25. v1.0 v1.1 v2.0 v2.1 v2.2

  26. [BananaAlertView showWithTitleText:@"403 Error" messageText:@"Forbidden banana." dismissButtonText:@"Dang."]; No known class method

    for selector 'showWithTitleText:messageText:…' 
  27. None
  28. @interface BananaAlertView : UIAlertView + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString

    *)dismissButtonText; + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString *)dismissButtonText tintColor:(UIColor *)tintColor; @end
  29. @interface BananaAlertView : UIAlertView + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString

    *)dismissButtonText; + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString *)dismissButtonText tintColor:(UIColor *)tintColor; @end
  30. @implementation BananaAlertView + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString *)dismissButtonText {

    [self showWithTitleText:titleText messageText:messageText dismissButtonText:dismissButtonText tintColor:[UIColor yellowColor]]; } + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString *)dismissButtonText tintColor:(UIColor *)tintColor { // ... }
  31. @implementation BananaAlertView + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString *)dismissButtonText {

    [self showWithTitleText:titleText messageText:messageText dismissButtonText:dismissButtonText tintColor:[UIColor yellowColor]]; } + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString *)dismissButtonText tintColor:(UIColor *)tintColor { // ... }
  32. @implementation BananaAlertView + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString *)dismissButtonText {

    [self showWithTitleText:titleText messageText:messageText dismissButtonText:dismissButtonText tintColor:[UIColor yellowColor]]; } + (void)showWithTitleText:(NSString *)titleText messageText:(NSString *)messageText dismissButtonText:(NSString *)dismissButtonText tintColor:(UIColor *)tintColor { // ... }
  33. [BananaAlertView showWithTitleText:@"403 Error" messageText:@"Forbidden banana." dismissButtonText:@"Dang."];

  34. [BananaAlertView showWithTitleText:@"403 Error" messageText:@"Forbidden banana." dismissButtonText:@"Dang."]; [BananaAlertView showWithTitleText:@"500 Error" messageText:@"Internal

    banana." dismissButtonText:@"Rats."
 tintColor:[UIColor redColor]];
  35. public class BananaAlertView { public static func show( titleText: String,

    messageText: String, dismissButtonText: String) {
 // ... } }
  36. public class BananaAlertView { public static func show( titleText: String,

    messageText: String, dismissButtonText: String) {
 // ... } } dismissButtonText: String, tintColor: UIColor = UIColor.yellowColor()) { // ... }
  37. public typealias ButtonCallback = (buttonIndex: Int) -> () public class

    BananaAlertView { public static func show( titleText: String, messageText: String, dismissButtonText: String, dismissButtonCallback: ButtonCallback, tintColor: UIColor = UIColor.yellowColor()) { // ... } }
  38. public typealias ButtonCallback = (buttonIndex: Int) -> () public class

    BananaAlertView { public static func show( titleText: String, messageText: String, dismissButtonText: String, dismissButtonCallback: ButtonCallback, tintColor: UIColor = UIColor.yellowColor()) { // ... } }
  39. public typealias ButtonCallback = (buttonIndex: Int) -> () public class

    BananaAlertView { public static func show( titleText: String, messageText: String, dismissButtonText: String, dismissButtonCallback: ButtonCallback, tintColor: UIColor = UIColor.yellowColor()) { // ... } }
  40. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { (buttonIndex: Int) in
 // ...
 }, tintColor: UIColor.redColor() )
  41. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { (buttonIndex: Int) in
 // ...
 }, tintColor: UIColor.redColor() )
  42. None
  43. public typealias ButtonCallback = (buttonIndex: Int) -> () public class

    BananaAlertView { public static func show( titleText: String, messageText: String, dismissButtonText: String, dismissButtonCallback: ButtonCallback, tintColor: UIColor = UIColor.yellowColor()) { // ... } }
  44. public typealias ButtonCallback = (buttonIndex: Int) -> () public class

    BananaAlertView { public static func show( titleText: String, messageText: String, dismissButtonText: String, dismissButtonCallback: ButtonCallback, tintColor: UIColor = UIColor.yellowColor()) { // ... } } (buttonIndex: Int, buttonTitle: String) -> ()
  45. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { (buttonIndex: Int) in
 // ...
 }, tintColor: UIColor.redColor() )
  46. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { (buttonIndex: Int) in
 // ...
 }, tintColor: UIColor.redColor() ) Cannot invoke 'show' with an argument list of type '(String, message… 
  47. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { (buttonIndex: Int) in
 // ...
 }, tintColor: UIColor.redColor() ) Cannot invoke 'show' with an argument list of type '(String, message… 
  48. parameter objects

  49. public struct ButtonCallbackParameters { let buttonIndex: Int let buttonText: String

    } public typealias ButtonCallback = (buttonIndex: Int) -> ()
  50. public struct ButtonCallbackParameters { let buttonIndex: Int let buttonText: String

    } public typealias ButtonCallback = (buttonIndex: Int) -> ()
  51. public struct ButtonCallbackParameters { let buttonIndex: Int let buttonText: String

    } public typealias ButtonCallback = (buttonIndex: Int) -> () public typealias ButtonCallback = (parameters: ButtonCallbackParameters) -> ()
  52. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { (buttonIndex: Int) in
 // ...
 }, tintColor: UIColor.redColor() )
  53. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { (buttonIndex: Int) in
 // ...
 }, tintColor: UIColor.redColor() ) dismissButtonCallback: { parameters in if parameters.buttonText == "Rats." { // ... } },
  54. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { (buttonIndex: Int) in
 // ...
 }, tintColor: UIColor.redColor() ) dismissButtonCallback: { parameters in if parameters.buttonText == "Rats." { // ... } },
  55. public struct ButtonCallbackParameters { let buttonIndex: Int let buttonTitle: String

    } public typealias ButtonCallback = (parameters: ButtonCallbackParameters) -> ()
  56. public struct ButtonCallbackParameters { let buttonIndex: Int let buttonTitle: String

    } public typealias ButtonCallback = (parameters: ButtonCallbackParameters) -> () let totalButtonCount: Int
  57. public struct ButtonCallbackParameters { let buttonIndex: Int let buttonTitle: String

    } public typealias ButtonCallback = (parameters: ButtonCallbackParameters) -> () let totalButtonCount: Int
  58. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { parameters in if parameters.buttonText == "Rats." { // ... } }, tintColor: UIColor.redColor() )
  59. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { parameters in if parameters.buttonText == "Rats." { // ... } }, tintColor: UIColor.redColor() )
  60. None
  61. public struct ButtonCallbackParameters { let buttonIndex: Int
 let buttonTitle: String


    let totalButtonCount: Int } public typealias ButtonCallback = (parameters: ButtonCallbackParameters) -> ()
  62. public struct ButtonCallbackParameters { let buttonIndex: Int
 let buttonTitle: String


    let totalButtonCount: Int } public typealias ButtonCallback = (parameters: ButtonCallbackParameters) -> () @availability(*, deprecated=2.0)
  63. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { parameters in if parameters.buttonText == "Rats." { // ... } }, tintColor: UIColor.redColor() )
  64. BananaAlertView.show( "500 Error", messageText: "Internal banana error.", dismissButtonText: "Rats.", dismissButtonCallback:

    { parameters in if parameters.buttonText == "Rats." { // ... } }, tintColor: UIColor.redColor() ) 'buttonText' was deprecated in * version 2.0 
  65. public struct AlertViewOptions {
 public let dismissButtonText: String
 public let

    tintColor: UIColor public let animationDuration: NSTimeInterval } public class BananaAlertView { public static func show( titleText: String, messageText: String, options: AlertViewOptions) { // ... } }
  66. public struct AlertViewOptions {
 public let dismissButtonText: String
 public let

    tintColor: UIColor public let animationDuration: NSTimeInterval } public class BananaAlertView { public static func show( titleText: String, messageText: String, options: AlertViewOptions) { // ... } } = AlertViewOptions()) { = "OK" = UIColor.yellowColor() = 1
  67. None
  68. None
  69. None
  70. None
  71. None
  72. None
  73. None
  74. # U+1F645 KENT BECK

  75. #

  76. future-proof your API

  77. more overhead

  78. don’t force users to choose

  79. None
  80. None
  81. @interface JVFloatLabeledTextField : UITextField @property (nonatomic, assign) CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR;

    @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @end
  82. @interface JVFloatLabeledTextField : UITextField @property (nonatomic, assign) CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR;

    @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @end
  83. CGRect frame = CGRectMake(0.f, 0.f, 100.f, 44.f); JVFloatLabeledTextField *textField =

    [[JVFloatLabeledTextField alloc] initWithFrame:frame];
  84. CGRect frame = CGRectMake(0.f, 0.f, 100.f, 44.f); JVFloatLabeledTextField *textField =

    [[JVFloatLabeledTextField alloc] initWithFrame:frame]; textField.placeholder = @"Price"; textField.floatingLabelYPadding = 10.f; textField.floatingLabelTextColor = [UIColor blueColor];
  85. CGRect frame = CGRectMake(0.f, 0.f, 100.f, 44.f); JVFloatLabeledTextField *textField =

    [[JVFloatLabeledTextField alloc] initWithFrame:frame]; textField.placeholder = @"Price"; textField.floatingLabelYPadding = 10.f; textField.floatingLabelTextColor = [UIColor blueColor];
  86. subclasses force people to use your class exclusively

  87. None
  88. None
  89. @interface UITextField (JVFloatLabeledTextField) @property (nonatomic, assign) CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property

    (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @end @interface JVFloatLabeledTextField : UITextField
  90. @interface UITextField (JVFloatLabeledTextField) @property (nonatomic, assign) CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property

    (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @end
  91. @interface UITextField (JVFloatLabeledTextField) @property (nonatomic, assign) CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property

    (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @end
  92. @interface UITextField (JVFloatLabeledTextField) { CGFloat _floatingLabelYPadding; } @property (nonatomic, assign)

    CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR; @end
  93. @interface UITextField (JVFloatLabeledTextField) { CGFloat _floatingLabelYPadding; } @property (nonatomic, assign)

    CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR; @end
  94. const void * const YPaddingKey = &YPaddingKey; - (void)setFloatingLabelYPadding:(CGFloat)yPadding {

    objc_setAssociatedObject( self, YPaddingKey, @(yPadding), OBJC_ASSOCIATION_RETAIN_NONATOMIC ); } - (CGFloat)floatingLabelYPadding { return [objc_getAssociatedObject(self, YPaddingKey) floatValue]; }
  95. const void * const YPaddingKey = &YPaddingKey; - (void)setFloatingLabelYPadding:(CGFloat)yPadding {

    objc_setAssociatedObject( self, YPaddingKey, @(yPadding), OBJC_ASSOCIATION_RETAIN_NONATOMIC ); } - (CGFloat)floatingLabelYPadding { return [objc_getAssociatedObject(self, YPaddingKey) floatValue]; }
  96. const void * const YPaddingKey = &YPaddingKey; - (void)setFloatingLabelYPadding:(CGFloat)yPadding {

    objc_setAssociatedObject( self, YPaddingKey, @(yPadding), OBJC_ASSOCIATION_RETAIN_NONATOMIC ); } - (CGFloat)floatingLabelYPadding { return [objc_getAssociatedObject(self, YPaddingKey) floatValue]; }
  97. @interface UITextField (JVFloatLabeledTextField) @property (nonatomic, assign) CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property

    (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @end
  98. @interface UITextField (JVFloatLabeledTextField) @property (nonatomic, assign) CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property

    (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @end
  99. const void * const YPaddingKey = &YPaddingKey; const void *

    const FontKey = &FontKey; const void * const TextColorKey = &TextColorKey; - (void)setFloatingLabelYPadding:(CGFloat)yPadding { objc_setAssociatedObject( self, YPaddingKey, @(yPadding), OBJC_ASSOCIATION_RETAIN_NONATOMIC ); } - (CGFloat)floatingLabelYPadding { return [objc_getAssociatedObject(self, YPaddingKey) floatValue];
  100. } - (UIFont *)floatingLabelFont { return objc_getAssociatedObject(self, FontKey); } -

    (void)setFloatingLabelTextColor:(UIColor *)color { objc_setAssociatedObject( self, TextColorKey, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC ); } - (UIColor *)floatingLabelTextColor { return objc_getAssociatedObject(self, TextColorKey); }
  101. extension UITextField { var floatingLabelYPadding: CGFloat { set { objc_setAssociatedObject(

    self, &YPaddingKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_ASSIGN) ) } get { if let padding = objc_getAssociatedObject( self, &YPaddingKey) as? CGFloat { return padding } else { return 0
  102. extension UITextField { var floatingLabelYPadding: CGFloat { set { objc_setAssociatedObject(

    self, &YPaddingKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_ASSIGN) ) } get { if let padding = objc_getAssociatedObject( self, &YPaddingKey) as? CGFloat { return padding } else { return 0
  103. &TextColorKey, newValue, objc_AssociationPolicy( OBJC_ASSOCIATION_RETAIN_NONATOMIC) ) } get { if let

    color = objc_getAssociatedObject( self, &TextColorKey) as? UIColor { return color } else { return UIColor.blackColor() } } } }
  104. doesn’t scale

  105. // objc4-532/runtime/objc-references.mm
 
 // class AssociationsManager manages a lock /

    hash table // singleton pair. Allocating an instance acquires the // lock, and calling its assocations() method // lazily allocates it. class AssociationsManager { static OSSpinLock _lock; // associative references: // object pointer -> PtrPtrHashMap. static AssociationsHashMap *_map; public: AssociationsManager() { OSSpinLockLock(&_lock); } ~AssociationsManager() { OSSpinLockUnlock(&_lock); } };
  106. CGFloat *floatingLabelYPadding UIFont *floatingLabelFont UIColor *floatingLabelTextColor UIColor *floatingLabelActiveColor BOOL animateEvenIfNotFirstResponder

    UITextField
 (JVFloatLabeledTextField)
  107. CGFloat *floatingLabelYPadding UIFont *floatingLabelFont UIColor *floatingLabelTextColor UIColor *floatingLabelActiveColor BOOL animateEvenIfNotFirstResponder

    JVFloatLabeledOptions
  108. CGFloat *floatingLabelYPadding UIFont *floatingLabelFont UIColor *floatingLabelTextColor UIColor *floatingLabelActiveColor BOOL animateEvenIfNotFirstResponder

    JVFloatLabeledOptions UITextField
 (JVFloatLabeledTextField) JVFloatLabeledOptions *options
  109. extension UITextField { var options: FloatLabelOptions { get { if

    let options = objc_getAssociatedObject( self, &OptionsKey) as? FloatLabelOptions { return options } else { return FloatLabelOptions() } } set { // ... } } }
  110. extension UITextField { var options: FloatLabelOptions { get { if

    let options = objc_getAssociatedObject( self, &OptionsKey) as? FloatLabelOptions { return options } else { return FloatLabelOptions() } } set { // ... } } }
  111. extension UITextField { var options: FloatLabelOptions { get { if

    let options = objc_getAssociatedObject( self, &OptionsKey) as? FloatLabelOptions { return options } else { return FloatLabelOptions() } } set { // ... } } }
  112. extension UITextField { var options: FloatLabelOptions { get { if

    let options = objc_getAssociatedObject( self, &OptionsKey) as? FloatLabelOptions { return options } else { return FloatLabelOptions() } } set { // ... } } }
  113. @objc class FloatLabelOptions { let yPadding: CGFloat let font: UIFont

    let textColor: UIColor // ... }
  114. @objc class FloatLabelOptions { let yPadding: CGFloat let font: UIFont

    let textColor: UIColor // ... }
  115. #

  116. compose multiple categories

  117. runtime sorcery

  118. None
  119. None
  120. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in switch state.direction { case .Left: self.bookmarkView.alpha = 0 self.dontBookmarkView.alpha = state.thresholdRatio case .Right: self.bookmarkView.alpha = state.thresholdRatio self.dontBookmarkView.alpha = 0 } } webView.mdc_swipeToChooseSetup(options)
  121. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in switch state.direction { case .Left: self.bookmarkView.alpha = 0 self.dontBookmarkView.alpha = state.thresholdRatio case .Right: self.bookmarkView.alpha = state.thresholdRatio self.dontBookmarkView.alpha = 0 } } webView.mdc_swipeToChooseSetup(options)
  122. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in switch state.direction { case .Left: self.bookmarkView.alpha = 0 self.dontBookmarkView.alpha = state.thresholdRatio case .Right: self.bookmarkView.alpha = state.thresholdRatio self.dontBookmarkView.alpha = 0 } } webView.mdc_swipeToChooseSetup(options)
  123. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in switch state.direction { case .Left: self.bookmarkView.alpha = 0 self.dontBookmarkView.alpha = state.thresholdRatio case .Right: self.bookmarkView.alpha = state.thresholdRatio self.dontBookmarkView.alpha = 0 } } webView.mdc_swipeToChooseSetup(options)
  124. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in switch state.direction { case .Left: self.bookmarkView.alpha = 0 self.dontBookmarkView.alpha = state.thresholdRatio case .Right: self.bookmarkView.alpha = state.thresholdRatio self.dontBookmarkView.alpha = 0 } } webView.mdc_swipeToChooseSetup(options)
  125. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in switch state.direction { case .Left: self.bookmarkView.alpha = 0 self.dontBookmarkView.alpha = state.thresholdRatio case .Right: self.bookmarkView.alpha = state.thresholdRatio self.dontBookmarkView.alpha = 0 } } webView.mdc_swipeToChooseSetup(options)
  126. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in switch state.direction { case .Left: self.bookmarkView.alpha = 0 self.dontBookmarkView.alpha = state.thresholdRatio case .Right: self.bookmarkView.alpha = state.thresholdRatio self.dontBookmarkView.alpha = 0 } } webView.mdc_swipeToChooseSetup(options)
  127. None
  128. None
  129. None
  130. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in switch state.direction { case .Left: self.bookmarkView.alpha = 0 self.dontBookmarkView.alpha = state.thresholdRatio case .Right: self.bookmarkView.alpha = state.thresholdRatio self.dontBookmarkView.alpha = 0 } } webView.mdc_swipeToChooseSetup(options)
  131. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in switch state.direction { case .Left: self.bookmarkView.alpha = 0 self.dontBookmarkView.alpha = state.thresholdRatio case .Right: self.bookmarkView.alpha = state.thresholdRatio self.dontBookmarkView.alpha = 0 } } webView.mdc_swipeToChooseSetup(options)
  132. parameter objects

  133. public struct ButtonCallbackParameters { let buttonIndex: Int
 let buttonTitle: String


    let totalButtonCount: Int } public typealias ButtonCallback = (parameters: ButtonCallbackParameters) -> ()
  134. public struct ButtonCallbackParameters { let buttonIndex: Int
 let buttonTitle: String


    let totalButtonCount: Int } public typealias ButtonCallback = (parameters: ButtonCallbackParameters) -> () @availability(*, deprecated=2.0)
  135. extensions vs. subclasses

  136. extension UITextField { var options: FloatLabelOptions { get { //

    ... } set { // ... } } }
  137. configuration objects

  138. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in // ... } webView.mdc_swipeToChooseSetup(options)
  139. prefer immutability

  140. let options = MDCSwipeOptions() options.threshold = 130 options.onPan = {

    state in // ... } webView.mdc_swipeToChooseSetup(options)
  141. - github.com/Quick/Quick
 - github.com/modocache/MDCSwipeToChoose 
 @modocache