SlideShare a Scribd company logo
iOSAPIDesign
what’sthecoolestthingabout
iOSdevelopment?
- I’m sure everyone has a different opinion on this
- Able to make delightful user interfaces

- This is JVFloatLabeledTextField

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

- This is JVFloatLabeledTextField

- Engrossing interfaces are at the core of the iPhone
sharingiscaring
- With dependency managers like Carthage and CocoaPods, it’s easy to create and share your designs
showyoucarewithyourAPI
- 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…
“🙅”
-KentBeck
- Everything is a tradeoff. (paraphrasing here)

- What makes a good API? There are very few clear answers, instead we have to evaluate tradeoffs
- What’s the API for JVFloatLabeledTextField?
- What’s the API for JVFloatLabeledTextField?
@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
@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
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
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
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
subclassesforcepeopleto
useyourclassexclusively
- This is limiting
- MHTextField provides a next/previous input accessory view and scrolling, but is also a subclass

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

- Subclasses force us to choose one over the other
@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
@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
@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
@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
@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
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
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
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
@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?
@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?
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
}
- (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
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!
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!
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!
doesn’tscale
- Defining two methods for every new property is nuts

- Too much boilerplate code, but also…
// 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…
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
CGFloat *floatingLabelYPadding
UIFont *floatingLabelFont
UIColor *floatingLabelTextColor
UIColor *floatingLabelActiveColor
BOOL animateEvenIfNotFirstResponder
JVFloatLabeledOptions
- Instead, we can encapsulate these properties in a single “options”, or “configuration” object
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
@interface UITextField (JVFloatLabeledTextField)
@property (nonatomic, copy)
JVFloatLabeledOptions *options;
@end
- Now we have just one property, options
@interface UITextField (JVFloatLabeledTextField)
@property (nonatomic, copy)
JVFloatLabeledOptions *options;
@end
- Now we have just one property, options
@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
🙅
- So what are the tradeoffs of using categories over subclasses?
composemultiplecategories
- 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
runtimesorcery
- Runtime manipulation makes this approach a little less safe, a little less performant
- I used this and other patterns in an open-source UI library I developed last year, MDCSwipeToChoose

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

- Category on UIView
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
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
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
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
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
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
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
- 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
- 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
- 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
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
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
// 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
// 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
func evaluateServerTrust(
trust: SecTrustRef) -> Bool {
// ...
}
- Even easier in Swift—just define default parameters
func evaluateServerTrust(
trust: SecTrustRef, domain: String? = nil) -> Bool {
// ...
}
- Even easier in Swift—just define default parameters
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…
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…
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
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
options.onPan = ^(UIView *view,
MDCSwipeDirection direction,
CGFloat thresholdRatio) {
if (direction == MDCSwipeDirectionLeft) {
// ...panning to the left.
}
};
- Instead, you could encapsulate params in an object
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
@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
@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
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
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
@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
@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
@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
🙅
- So what are the tradeoffs of parameter objects?
versionyourAPI
- Future-proof block or protocol APIs
overhead
- Small perf overhead of creating object.

- Large dev overhead of defining new class

- Use sparingly, for public APIs that may change?
1.Categoriesvs.Subclasses
- You can compose categories, with some runtime hacking
2.Configurationobjects
- Configuration objects allow you to initialize an object with a set of parameters you can easily change
3.Alwayspreferimmutability
- Set it and forget it—if you allow your objects to change and mutate, you’re going to be chasing subtle bugs
4.Parameterobjects
- Use parameter objects to version your APIs
1.Categoriesvs.Subclasses

2.Configurationobjects
3.Alwayspreferimmutability

4.Parameterobjects
Thanks!

More Related Content

What's hot (20)

PDF
Seven Versions of One Web Application
Yakov Fain
 
PDF
Real-World Scala Design Patterns
NLJUG
 
PPTX
Scala’s implicits
Pablo Francisco Pérez Hidalgo
 
PDF
IoC&Laravel
Hoang Long
 
PDF
Painless Javascript Unit Testing
Benjamin Wilson
 
PDF
Introduction to Sightly
Ankit Gubrani
 
PDF
Javascript Best Practices
Christian Heilmann
 
PDF
slingmodels
Ankur Chauhan
 
PPS
Design Patterns For 70% Of Programmers In The World
Saurabh Moody
 
KEY
Spring AOP
Jeroen Rosenberg
 
DOCX
Doc abap
poussin_forever
 
PDF
ParisJS #10 : RequireJS
Julien Cabanès
 
PPTX
React advance
Vivek Tikar
 
PDF
Angular Intermediate
LinkMe Srl
 
PPTX
Introduction to Core Java Programming
Raveendra R
 
PPTX
Advance JS and oop
Abuzer Firdousi
 
PPTX
Effective Java
Brice Argenson
 
PDF
Effective Java, Third Edition - Keepin' it Effective
C4Media
 
PPTX
2CPP03 - Object Orientation Fundamentals
Michael Heron
 
PPT
Enterprise Java Beans( E)
vikram singh
 
Seven Versions of One Web Application
Yakov Fain
 
Real-World Scala Design Patterns
NLJUG
 
Scala’s implicits
Pablo Francisco Pérez Hidalgo
 
IoC&Laravel
Hoang Long
 
Painless Javascript Unit Testing
Benjamin Wilson
 
Introduction to Sightly
Ankit Gubrani
 
Javascript Best Practices
Christian Heilmann
 
slingmodels
Ankur Chauhan
 
Design Patterns For 70% Of Programmers In The World
Saurabh Moody
 
Spring AOP
Jeroen Rosenberg
 
Doc abap
poussin_forever
 
ParisJS #10 : RequireJS
Julien Cabanès
 
React advance
Vivek Tikar
 
Angular Intermediate
LinkMe Srl
 
Introduction to Core Java Programming
Raveendra R
 
Advance JS and oop
Abuzer Firdousi
 
Effective Java
Brice Argenson
 
Effective Java, Third Edition - Keepin' it Effective
C4Media
 
2CPP03 - Object Orientation Fundamentals
Michael Heron
 
Enterprise Java Beans( E)
vikram singh
 

Similar to iOS API Design (20)

PDF
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
PDF
Reactive Type-safe WebComponents
Martin Hochel
 
PDF
Swift - One step forward from Obj-C
Nissan Tsafrir
 
KEY
RubyMotion
Mark
 
PDF
Visual AI Testing Using Applitools
Mikhail Laptev
 
PDF
Dsug 05 02-15 - ScalDI - lightweight DI in Scala
Ugo Matrangolo
 
PDF
Protocols promised-land-2
Michele Titolo
 
PDF
Meet Elcodi, the flexible e-commerce components built on Symfony2
Aldo Chiecchia
 
PDF
Thinking In Swift
Janie Clayton
 
PDF
Migrating Objective-C to Swift
Elmar Kretzer
 
PDF
Unethical JavaScript - Giorgio Natili - Codemotion Rome 2017
Codemotion
 
PDF
Knockoutjs
Yourcontent YC
 
PDF
Robots in Swift
Janie Clayton
 
PDF
Kotlin for Android - Vali Iorgu - mRready
MobileAcademy
 
PDF
Deep dive into Oracle ADF
Euegene Fedorenko
 
PDF
How AngularJS Embraced Traditional Design Patterns
Ran Mizrahi
 
PDF
Building a Native Camera Access Library - Part V - Transcript.pdf
ShaiAlmog1
 
PDF
Architectures in the compose world
Fabio Collini
 
PDF
Con5623 pdf 5623_001
Euegene Fedorenko
 
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
Reactive Type-safe WebComponents
Martin Hochel
 
Swift - One step forward from Obj-C
Nissan Tsafrir
 
RubyMotion
Mark
 
Visual AI Testing Using Applitools
Mikhail Laptev
 
Dsug 05 02-15 - ScalDI - lightweight DI in Scala
Ugo Matrangolo
 
Protocols promised-land-2
Michele Titolo
 
Meet Elcodi, the flexible e-commerce components built on Symfony2
Aldo Chiecchia
 
Thinking In Swift
Janie Clayton
 
Migrating Objective-C to Swift
Elmar Kretzer
 
Unethical JavaScript - Giorgio Natili - Codemotion Rome 2017
Codemotion
 
Knockoutjs
Yourcontent YC
 
Robots in Swift
Janie Clayton
 
Kotlin for Android - Vali Iorgu - mRready
MobileAcademy
 
Deep dive into Oracle ADF
Euegene Fedorenko
 
How AngularJS Embraced Traditional Design Patterns
Ran Mizrahi
 
Building a Native Camera Access Library - Part V - Transcript.pdf
ShaiAlmog1
 
Architectures in the compose world
Fabio Collini
 
Con5623 pdf 5623_001
Euegene Fedorenko
 
Ad

More from Brian Gesiak (10)

PDF
Everything You (N)ever Wanted to Know about Testing View Controllers
Brian Gesiak
 
PDF
Quick: Better Tests via Incremental Setup
Brian Gesiak
 
PDF
Property-Based Testing in Objective-C & Swift with Fox
Brian Gesiak
 
PDF
Intel® Xeon® Phi Coprocessor High Performance Programming
Brian Gesiak
 
PDF
iOS UI Component API Design
Brian Gesiak
 
PDF
Apple Templates Considered Harmful
Brian Gesiak
 
PDF
アップルのテンプレートは有害と考えられる
Brian Gesiak
 
PDF
RSpec 3.0: Under the Covers
Brian Gesiak
 
PDF
iOS Behavior-Driven Development
Brian Gesiak
 
PDF
iOSビヘイビア駆動開発
Brian Gesiak
 
Everything You (N)ever Wanted to Know about Testing View Controllers
Brian Gesiak
 
Quick: Better Tests via Incremental Setup
Brian Gesiak
 
Property-Based Testing in Objective-C & Swift with Fox
Brian Gesiak
 
Intel® Xeon® Phi Coprocessor High Performance Programming
Brian Gesiak
 
iOS UI Component API Design
Brian Gesiak
 
Apple Templates Considered Harmful
Brian Gesiak
 
アップルのテンプレートは有害と考えられる
Brian Gesiak
 
RSpec 3.0: Under the Covers
Brian Gesiak
 
iOS Behavior-Driven Development
Brian Gesiak
 
iOSビヘイビア駆動開発
Brian Gesiak
 
Ad

Recently uploaded (20)

PDF
July Patch Tuesday
Ivanti
 
PPTX
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
PDF
How Startups Are Growing Faster with App Developers in Australia.pdf
India App Developer
 
PDF
LLMs.txt: Easily Control How AI Crawls Your Site
Keploy
 
PDF
Français Patch Tuesday - Juillet
Ivanti
 
PPTX
Top Managed Service Providers in Los Angeles
Captain IT
 
PDF
Predicting the unpredictable: re-engineering recommendation algorithms for fr...
Speck&Tech
 
PDF
TrustArc Webinar - Data Privacy Trends 2025: Mid-Year Insights & Program Stra...
TrustArc
 
PDF
Impact of IEEE Computer Society in Advancing Emerging Technologies including ...
Hironori Washizaki
 
PDF
Fl Studio 24.2.2 Build 4597 Crack for Windows Free Download 2025
faizk77g
 
PDF
Smart Air Quality Monitoring with Serrax AQM190 LITE
SERRAX TECHNOLOGIES LLP
 
PPTX
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
PDF
SWEBOK Guide and Software Services Engineering Education
Hironori Washizaki
 
PPTX
✨Unleashing Collaboration: Salesforce Channels & Community Power in Patna!✨
SanjeetMishra29
 
PPTX
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
PDF
NewMind AI Journal - Weekly Chronicles - July'25 Week II
NewMind AI
 
PDF
Windsurf Meetup Ottawa 2025-07-12 - Planning Mode at Reliza.pdf
Pavel Shukhman
 
PDF
Apache CloudStack 201: Let's Design & Build an IaaS Cloud
ShapeBlue
 
PDF
The Builder’s Playbook - 2025 State of AI Report.pdf
jeroen339954
 
PDF
Women in Automation Presents: Reinventing Yourself — Bold Career Pivots That ...
DianaGray10
 
July Patch Tuesday
Ivanti
 
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
How Startups Are Growing Faster with App Developers in Australia.pdf
India App Developer
 
LLMs.txt: Easily Control How AI Crawls Your Site
Keploy
 
Français Patch Tuesday - Juillet
Ivanti
 
Top Managed Service Providers in Los Angeles
Captain IT
 
Predicting the unpredictable: re-engineering recommendation algorithms for fr...
Speck&Tech
 
TrustArc Webinar - Data Privacy Trends 2025: Mid-Year Insights & Program Stra...
TrustArc
 
Impact of IEEE Computer Society in Advancing Emerging Technologies including ...
Hironori Washizaki
 
Fl Studio 24.2.2 Build 4597 Crack for Windows Free Download 2025
faizk77g
 
Smart Air Quality Monitoring with Serrax AQM190 LITE
SERRAX TECHNOLOGIES LLP
 
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
SWEBOK Guide and Software Services Engineering Education
Hironori Washizaki
 
✨Unleashing Collaboration: Salesforce Channels & Community Power in Patna!✨
SanjeetMishra29
 
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
NewMind AI Journal - Weekly Chronicles - July'25 Week II
NewMind AI
 
Windsurf Meetup Ottawa 2025-07-12 - Planning Mode at Reliza.pdf
Pavel Shukhman
 
Apache CloudStack 201: Let's Design & Build an IaaS Cloud
ShapeBlue
 
The Builder’s Playbook - 2025 State of AI Report.pdf
jeroen339954
 
Women in Automation Presents: Reinventing Yourself — Bold Career Pivots That ...
DianaGray10
 

iOS API Design

  • 2. what’sthecoolestthingabout iOSdevelopment? - I’m sure everyone has a different opinion on this
  • 3. - Able to make delightful user interfaces
 - This is JVFloatLabeledTextField - Engrossing interfaces are at the core of the iPhone
  • 4. - Able to make delightful user interfaces
 - This is JVFloatLabeledTextField - Engrossing interfaces are at the core of the iPhone
  • 5. sharingiscaring - With dependency managers like Carthage and CocoaPods, it’s easy to create and share your designs
  • 6. showyoucarewithyourAPI - 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…
  • 7. “🙅” -KentBeck - Everything is a tradeoff. (paraphrasing here)
 - What makes a good API? There are very few clear answers, instead we have to evaluate tradeoffs
  • 8. - What’s the API for JVFloatLabeledTextField?
  • 9. - What’s the API for JVFloatLabeledTextField?
  • 10. @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
  • 11. @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
  • 12. 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
  • 13. 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
  • 14. 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
  • 16. - MHTextField provides a next/previous input accessory view and scrolling, but is also a subclass
 - Subclasses force us to choose one over the other
  • 17. - MHTextField provides a next/previous input accessory view and scrolling, but is also a subclass
 - Subclasses force us to choose one over the other
  • 18. @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
  • 19. @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
  • 20. @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
  • 21. @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
  • 22. @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
  • 23. 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
  • 24. 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
  • 25. 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
  • 26. @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?
  • 27. @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?
  • 28. 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
  • 29. } - (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
  • 30. 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!
  • 31. 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!
  • 32. 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!
  • 33. doesn’tscale - Defining two methods for every new property is nuts
 - Too much boilerplate code, but also…
  • 34. // 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…
  • 35. 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
  • 36. CGFloat *floatingLabelYPadding UIFont *floatingLabelFont UIColor *floatingLabelTextColor UIColor *floatingLabelActiveColor BOOL animateEvenIfNotFirstResponder JVFloatLabeledOptions - Instead, we can encapsulate these properties in a single “options”, or “configuration” object
  • 37. 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
  • 38. @interface UITextField (JVFloatLabeledTextField) @property (nonatomic, copy) JVFloatLabeledOptions *options; @end - Now we have just one property, options
  • 39. @interface UITextField (JVFloatLabeledTextField) @property (nonatomic, copy) JVFloatLabeledOptions *options; @end - Now we have just one property, options
  • 40. @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
  • 41. 🙅 - So what are the tradeoffs of using categories over subclasses?
  • 42. composemultiplecategories - 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
  • 43. runtimesorcery - Runtime manipulation makes this approach a little less safe, a little less performant
  • 44. - I used this and other patterns in an open-source UI library I developed last year, MDCSwipeToChoose
 - Category on UIView
  • 45. - I used this and other patterns in an open-source UI library I developed last year, MDCSwipeToChoose
 - Category on UIView
  • 46. 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
  • 47. 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
  • 48. 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
  • 49. 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
  • 50. 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
  • 51. 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
  • 52. 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
  • 53. - 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
  • 54. - 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
  • 55. - 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
  • 56. 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
  • 57. 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
  • 58. // 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
  • 59. // 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
  • 60. func evaluateServerTrust( trust: SecTrustRef) -> Bool { // ... } - Even easier in Swift—just define default parameters
  • 61. func evaluateServerTrust( trust: SecTrustRef, domain: String? = nil) -> Bool { // ... } - Even easier in Swift—just define default parameters
  • 62. 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…
  • 63. 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…
  • 64. 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
  • 65. 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
  • 66. options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio) { if (direction == MDCSwipeDirectionLeft) { // ...panning to the left. } }; - Instead, you could encapsulate params in an object
  • 67. 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
  • 68. @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
  • 69. @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
  • 70. 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
  • 71. 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
  • 72. @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
  • 73. @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
  • 74. @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
  • 75. 🙅 - So what are the tradeoffs of parameter objects?
  • 77. overhead - Small perf overhead of creating object.
 - Large dev overhead of defining new class
 - Use sparingly, for public APIs that may change?
  • 78. 1.Categoriesvs.Subclasses - You can compose categories, with some runtime hacking
  • 79. 2.Configurationobjects - Configuration objects allow you to initialize an object with a set of parameters you can easily change
  • 80. 3.Alwayspreferimmutability - Set it and forget it—if you allow your objects to change and mutate, you’re going to be chasing subtle bugs
  • 81. 4.Parameterobjects - Use parameter objects to version your APIs