ios api design

82
iOS API Design

Upload: brian-gesiak

Post on 13-Jul-2015

753 views

Category:

Technology


4 download

TRANSCRIPT

Page 1: iOS API Design

iOS API Design

Page 2: iOS API Design

what’s the coolest thing about iOS development?

- I’m sure everyone has a different opinion on this

Page 3: iOS API Design

- Able to make delightful user interfaces- This is JVFloatLabeledTextField- Engrossing interfaces are at the core of the iPhone

Page 4: iOS API Design

- Able to make delightful user interfaces- This is JVFloatLabeledTextField- Engrossing interfaces are at the core of the iPhone

Page 5: iOS API Design

sharing is caring

- With dependency managers like Carthage and CocoaPods, it’s easy to create and share your designs

Page 6: iOS API Design

show you care with your API

- But just sharing your design isn’t enough- You have to make it easy for people to use it- But this reminds me of a great quote from Kent Beck…

Page 7: iOS API Design

“🙅”-Kent Beck

- Everything is a tradeoff. (paraphrasing here)- What makes a good API? There are very few clear answers, instead we have to evaluate tradeoffs

Page 8: iOS API Design

- What’s the API for JVFloatLabeledTextField?

Page 9: iOS API Design

- What’s the API for JVFloatLabeledTextField?

Page 10: iOS API Design

@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

- Really nice, allows you to use UIAppearance

Page 11: iOS API Design

@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

- Really nice, allows you to use UIAppearance

Page 12: iOS API Design

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];

- Pretty simple to use- But it’s a specific class

Page 13: iOS API Design

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];

- Pretty simple to use- But it’s a specific class

Page 14: iOS API Design

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];

- Pretty simple to use- But it’s a specific class

Page 15: iOS API Design

subclasses force people to use your class exclusively

- This is limiting

Page 16: iOS API Design

- MHTextField provides a next/previous input accessory view and scrolling, but is also a subclass- Subclasses force us to choose one over the other

Page 17: iOS API Design

- MHTextField provides a next/previous input accessory view and scrolling, but is also a subclass- Subclasses force us to choose one over the other

Page 18: iOS API Design

@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

- A category in ObjC, or an extension in Swift, allows people to use the implementation with *any* text field- But we can’t add properties on categories/extensions

Page 19: iOS API Design

@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

- A category in ObjC, or an extension in Swift, allows people to use the implementation with *any* text field- But we can’t add properties on categories/extensions

Page 20: iOS API Design

@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

- A category in ObjC, or an extension in Swift, allows people to use the implementation with *any* text field- But we can’t add properties on categories/extensions

Page 21: iOS API Design

@interface UITextField (JVFloatLabeledTextField) { CGFloat _floatingLabelYPadding; }

@property (nonatomic, assign) CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR;

@end

- More precisely, defining a property automatically synthesizes an instance variable- Can’t do that on a category/extension

Page 22: iOS API Design

@interface UITextField (JVFloatLabeledTextField) { CGFloat _floatingLabelYPadding; }

@property (nonatomic, assign) CGFloat floatingLabelYPadding UI_APPEARANCE_SELECTOR;

@end

- More precisely, defining a property automatically synthesizes an instance variable- Can’t do that on a category/extension

Page 23: iOS API Design

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]; }

- An ivar won’t be auto synthesized if you define custom setters and getters- We can dynamically tack on objects using the runtime

Page 24: iOS API Design

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]; }

- An ivar won’t be auto synthesized if you define custom setters and getters- We can dynamically tack on objects using the runtime

Page 25: iOS API Design

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]; }

- An ivar won’t be auto synthesized if you define custom setters and getters- We can dynamically tack on objects using the runtime

Page 26: iOS API Design

@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

- So we just do the same for every property, right?

Page 27: iOS API Design

@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

- So we just do the same for every property, right?

Page 28: iOS API Design

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]; }

- Results in a ton of code for only 3 properties

Page 29: iOS API Design

}

- (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); }

- Results in a ton of code for only 3 properties

Page 30: iOS API Design

extension UITextField { var floatingLabelYPadding: CGFloat { set { objc_setAssociatedObject( self, YPaddingKey, newValue, UInt(OBJC_ASSOCIATION_ASSIGN) ) }

get { if let padding = objc_getAssociatedObject( self, YPaddingKey) as? CGFloat { return padding } else { return 0 }

- In Swift, it’s worse—notice we have to be explicit about the fact that the assoc object might not be set, or may not be the right type. - And so much code!

Page 31: iOS API Design

extension UITextField { var floatingLabelYPadding: CGFloat { set { objc_setAssociatedObject( self, YPaddingKey, newValue, UInt(OBJC_ASSOCIATION_ASSIGN) ) }

get { if let padding = objc_getAssociatedObject( self, YPaddingKey) as? CGFloat { return padding } else { return 0 }

- In Swift, it’s worse—notice we have to be explicit about the fact that the assoc object might not be set, or may not be the right type. - And so much code!

Page 32: iOS API Design

set { objc_setAssociatedObject( self, TextColorKey, newValue, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC) ) }

get { if let color = objc_getAssociatedObject( self, TextColorKey) as? UIColor { return color } else { return UIColor.blackColor() } } } }

- In Swift, it’s worse—notice we have to be explicit about the fact that the assoc object might not be set, or may not be the right type. - And so much code!

Page 33: iOS API Design

doesn’t scale

- Defining two methods for every new property is nuts- Too much boilerplate code, but also…

Page 34: iOS API Design

// 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); } };

- Associated objects are stored in a global hashmap- The more you add, the worse performance/memory usage gets- Of course, reference counting also works this way, but…

Page 35: iOS API Design

CGFloat *floatingLabelYPadding

UIFont *floatingLabelFont

UIColor *floatingLabelTextColor

UIColor *floatingLabelActiveColor

BOOL animateEvenIfNotFirstResponder

UITextField(JVFloatLabeledTextField)

- Instead, we can encapsulate these properties in a single “options”, or “configuration” object

Page 36: iOS API Design

CGFloat *floatingLabelYPadding

UIFont *floatingLabelFont

UIColor *floatingLabelTextColor

UIColor *floatingLabelActiveColor

BOOL animateEvenIfNotFirstResponder

JVFloatLabeledOptions

- Instead, we can encapsulate these properties in a single “options”, or “configuration” object

Page 37: iOS API Design

CGFloat *floatingLabelYPadding

UIFont *floatingLabelFont

UIColor *floatingLabelTextColor

UIColor *floatingLabelActiveColor

BOOL animateEvenIfNotFirstResponder

JVFloatLabeledOptions

UITextField(JVFloatLabeledTextField) JVFloatLabeledOptions *options

- Instead, we can encapsulate these properties in a single “options”, or “configuration” object

Page 38: iOS API Design

@interface UITextField (JVFloatLabeledTextField)

@property (nonatomic, copy) JVFloatLabeledOptions *options;

@end

- Now we have just one property, options

Page 39: iOS API Design

@interface UITextField (JVFloatLabeledTextField)

@property (nonatomic, copy) JVFloatLabeledOptions *options;

@end

- Now we have just one property, options

Page 40: iOS API Design

@interface JVFloatLabeledOptions : NSObject

@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

- From within the implementation, we can access properties on this object- They’re ordinary properties—no runtime

Page 41: iOS API Design

🙅- So what are the tradeoffs of using categories over subclasses?

Page 42: iOS API Design

compose multiple categories

- You don’t have to choose between one set of functionality or another- You don’t have to choose floating labels *or* a cool input accessory—you can have both

Page 43: iOS API Design

runtime sorcery

- Runtime manipulation makes this approach a little less safe, a little less performant

Page 44: iOS API Design

- I used this and other patterns in an open-source UI library I developed last year, MDCSwipeToChoose- Category on UIView

Page 45: iOS API Design

- I used this and other patterns in an open-source UI library I developed last year, MDCSwipeToChoose- Category on UIView

Page 46: iOS API Design

MDCSwipeOptions *options = [MDCSwipeOptions new]; options.threshold = 130.f; options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { bookmarkView.alpha = 0.f; dontBookmarkView.alpha = state.thresholdRatio; } else if (state.direction == MDCSwipeDirectionRight) { bookmarkView.alpha = state.thresholdRatio; dontBookmarkView.alpha = 0.f; } };

[webView mdc_swipeToChooseSetup:options];

- Add swiping behavior to any view—here a web view- We create the options object and set its properties- Then we setup the view using the options—this can only be done once, so the properties can’t be changed—a killer feature

Page 47: iOS API Design

MDCSwipeOptions *options = [MDCSwipeOptions new]; options.threshold = 130.f; options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { bookmarkView.alpha = 0.f; dontBookmarkView.alpha = state.thresholdRatio; } else if (state.direction == MDCSwipeDirectionRight) { bookmarkView.alpha = state.thresholdRatio; dontBookmarkView.alpha = 0.f; } };

[webView mdc_swipeToChooseSetup:options];

- Add swiping behavior to any view—here a web view- We create the options object and set its properties- Then we setup the view using the options—this can only be done once, so the properties can’t be changed—a killer feature

Page 48: iOS API Design

MDCSwipeOptions *options = [MDCSwipeOptions new]; options.threshold = 130.f; options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { bookmarkView.alpha = 0.f; dontBookmarkView.alpha = state.thresholdRatio; } else if (state.direction == MDCSwipeDirectionRight) { bookmarkView.alpha = state.thresholdRatio; dontBookmarkView.alpha = 0.f; } };

[webView mdc_swipeToChooseSetup:options];

- Add swiping behavior to any view—here a web view- We create the options object and set its properties- Then we setup the view using the options—this can only be done once, so the properties can’t be changed—a killer feature

Page 49: iOS API Design

MDCSwipeOptions *options = [MDCSwipeOptions new]; options.threshold = 130.f; options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { bookmarkView.alpha = 0.f; dontBookmarkView.alpha = state.thresholdRatio; } else if (state.direction == MDCSwipeDirectionRight) { bookmarkView.alpha = state.thresholdRatio; dontBookmarkView.alpha = 0.f; } };

[webView mdc_swipeToChooseSetup:options];

- Add swiping behavior to any view—here a web view- We create the options object and set its properties- Then we setup the view using the options—this can only be done once, so the properties can’t be changed—a killer feature

Page 50: iOS API Design

MDCSwipeOptions *options = [MDCSwipeOptions new]; options.threshold = 130.f; options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { bookmarkView.alpha = 0.f; dontBookmarkView.alpha = state.thresholdRatio; } else if (state.direction == MDCSwipeDirectionRight) { bookmarkView.alpha = state.thresholdRatio; dontBookmarkView.alpha = 0.f; } };

[webView mdc_swipeToChooseSetup:options];

- Add swiping behavior to any view—here a web view- We create the options object and set its properties- Then we setup the view using the options—this can only be done once, so the properties can’t be changed—a killer feature

Page 51: iOS API Design

MDCSwipeOptions *options = [MDCSwipeOptions new]; options.threshold = 130.f; options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { bookmarkView.alpha = 0.f; dontBookmarkView.alpha = state.thresholdRatio; } else if (state.direction == MDCSwipeDirectionRight) { bookmarkView.alpha = state.thresholdRatio; dontBookmarkView.alpha = 0.f; } };

[webView mdc_swipeToChooseSetup:options];

- Add swiping behavior to any view—here a web view- We create the options object and set its properties- Then we setup the view using the options—this can only be done once, so the properties can’t be changed—a killer feature

Page 52: iOS API Design

MDCSwipeOptions *options = [MDCSwipeOptions new]; options.threshold = 130.f; options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { bookmarkView.alpha = 0.f; dontBookmarkView.alpha = state.thresholdRatio; } else if (state.direction == MDCSwipeDirectionRight) { bookmarkView.alpha = state.thresholdRatio; dontBookmarkView.alpha = 0.f; } };

[webView mdc_swipeToChooseSetup:options];

- Add swiping behavior to any view—here a web view- We create the options object and set its properties- Then we setup the view using the options—this can only be done once, so the properties can’t be changed—a killer feature

Page 53: iOS API Design

- The view category is configured has a threshold—when the center of the view moves out of the threshold, it gets swiped offscreen- I don’t have to worry about the threshold changing, or the pan block, or anything

Page 54: iOS API Design

- The view category is configured has a threshold—when the center of the view moves out of the threshold, it gets swiped offscreen- I don’t have to worry about the threshold changing, or the pan block, or anything

Page 55: iOS API Design

- The view category is configured has a threshold—when the center of the view moves out of the threshold, it gets swiped offscreen- I don’t have to worry about the threshold changing, or the pan block, or anything

Page 56: iOS API Design

MDCSwipeOptions *options = [MDCSwipeOptions new]; options.threshold = 130.f; options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { bookmarkView.alpha = 0.f; dontBookmarkView.alpha = state.thresholdRatio; } else if (state.direction == MDCSwipeDirectionRight) { bookmarkView.alpha = state.thresholdRatio; dontBookmarkView.alpha = 0.f; } };

[webView mdc_swipeToChooseSetup:options];

- Another thing to note: the pan block takes a single parameter

Page 57: iOS API Design

MDCSwipeOptions *options = [MDCSwipeOptions new]; options.threshold = 130.f; options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { bookmarkView.alpha = 0.f; dontBookmarkView.alpha = state.thresholdRatio; } else if (state.direction == MDCSwipeDirectionRight) { bookmarkView.alpha = state.thresholdRatio; dontBookmarkView.alpha = 0.f; } };

[webView mdc_swipeToChooseSetup:options];

- Another thing to note: the pan block takes a single parameter

Page 58: iOS API Design

// AFNetworking/AFSecurityPolicy.m

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust { return [self evaluateServerTrust:serverTrust forDomain:nil]; }// ...

- With public methods, you can define new methods, and have the old call the new with default parameters

Page 59: iOS API Design

// AFNetworking/AFSecurityPolicy.m

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust { return [self evaluateServerTrust:serverTrust forDomain:nil]; }

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { // ... }

- With public methods, you can define new methods, and have the old call the new with default parameters

Page 60: iOS API Design

func evaluateServerTrust( trust: SecTrustRef) -> Bool { // ... }

- Even easier in Swift—just define default parameters

Page 61: iOS API Design

func evaluateServerTrust( trust: SecTrustRef, domain: String? = nil) -> Bool { // ... }

- Even easier in Swift—just define default parameters

Page 62: iOS API Design

options.onPan = ^(UIView *view, MDCSwipeDirection direction) {

if (direction == MDCSwipeDirectionLeft) { // ...panning to the left. } };

- Obj-C or Swift, block params and delegate callbacks lock you into an API- So if we add a parameter here…

Page 63: iOS API Design

options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio) { if (direction == MDCSwipeDirectionLeft) { // ...panning to the left. } };

- Obj-C or Swift, block params and delegate callbacks lock you into an API- So if we add a parameter here…

Page 64: iOS API Design

view.onPan = ^(UIView *view, MDCSwipeDirection direction) { if (direction == MDCSwipeDirectionLeft) { // ... } }; subview.onPan = ^(UIView *view, MDCSwipeDirection direction) { // ... }; void (^onPanBlock)(UIView *view, MDCSwipeDirection direction) = nil; subview.onPan = onPanBlock;

- …every callsite breaks

Page 65: iOS API Design

view.onPan = ^(UIView *view, MDCSwipeDirection direction) { if (direction == MDCSwipeDirectionLeft) { // ... } }; subview.onPan = ^(UIView *view, MDCSwipeDirection direction) { // ... }; void (^onPanBlock)(UIView *view, MDCSwipeDirection direction) = nil; subview.onPan = onPanBlock;

- …every callsite breaks

Page 66: iOS API Design

options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio) { if (direction == MDCSwipeDirectionLeft) { // ...panning to the left. } };

- Instead, you could encapsulate params in an object

Page 67: iOS API Design

options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio) { if (direction == MDCSwipeDirectionLeft) { // ...panning to the left. } };

options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) {

- Instead, you could encapsulate params in an object

Page 68: iOS API Design

@interface MDCPanState : NSObject

@property (nonatomic, strong, readonly) UIView *view;

@property (nonatomic, assign, readonly) MDCSwipeDirection direction; DEPRECATED_ATTRIBUTE

@property (nonatomic, assign, readonly) CGFloat thresholdRatio;

@end

- This is a design pattern that Martin Fowler calls “parameter objects”- One benefit is that it’s easy to change- Deprecation can help you slowly phase out APIs

Page 69: iOS API Design

@interface MDCPanState : NSObject

@property (nonatomic, strong, readonly) UIView *view;

@property (nonatomic, assign, readonly) MDCSwipeDirection direction; DEPRECATED_ATTRIBUTE

@property (nonatomic, assign, readonly) CGFloat thresholdRatio;

@end

- This is a design pattern that Martin Fowler calls “parameter objects”- One benefit is that it’s easy to change- Deprecation can help you slowly phase out APIs

Page 70: iOS API Design

options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { // ...panning to the left. } };

- Users will see a warning when they attempt to use the deprecated property

Page 71: iOS API Design

options.onPan = ^(MDCPanState *state) { if (state.direction == MDCSwipeDirectionLeft) { // ...panning to the left. } };

- Users will see a warning when they attempt to use the deprecated property

Page 72: iOS API Design

@protocol MDCSwipeToChooseDelegate <NSObject>

@optional

- (void)swipeToChooseView:(UIView *)view wasChosenWithDirection:(MDCSwipeDirection)direction;

@end

- Same goes for delegates and other protocols—changing params is a major version bump- Use parameter object for future-proofing

Page 73: iOS API Design

@protocol MDCSwipeToChooseDelegate <NSObject>

@optional

- (void)swipeToChooseView:(UIView *)view wasChosenWithDirection:(MDCSwipeDirection)direction momentum:(CGFloat)momentum;

@end

- Same goes for delegates and other protocols—changing params is a major version bump- Use parameter object for future-proofing

Page 74: iOS API Design

@protocol MDCSwipeToChooseDelegate <NSObject>

@optional

- (void)swipeToChooseView:(UIView *)view wasChosenWithParameters:(MDCChosenParameters *)params;

@end

- Same goes for delegates and other protocols—changing params is a major version bump- Use parameter object for future-proofing

Page 75: iOS API Design

🙅- So what are the tradeoffs of parameter objects?

Page 76: iOS API Design

version your API

- Future-proof block or protocol APIs

Page 77: iOS API Design

overhead

- Small perf overhead of creating object.- Large dev overhead of defining new class- Use sparingly, for public APIs that may change?

Page 78: iOS API Design

1. Categories vs. Subclasses

- You can compose categories, with some runtime hacking

Page 79: iOS API Design

2. Configuration objects

- Configuration objects allow you to initialize an object with a set of parameters you can easily change

Page 80: iOS API Design

3. Always prefer immutability

- Set it and forget it—if you allow your objects to change and mutate, you’re going to be chasing subtle bugs

Page 81: iOS API Design

4. Parameter objects

- Use parameter objects to version your APIs

Page 82: iOS API Design

1. Categories vs. Subclasses 2. Configuration objects 3. Always prefer immutability4. Parameter objects

Thanks!