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

Seven Versions of One Web Application
Seven Versions of One Web ApplicationSeven Versions of One Web Application
Seven Versions of One Web Application
Yakov Fain
 
Real-World Scala Design Patterns
Real-World Scala Design PatternsReal-World Scala Design Patterns
Real-World Scala Design PatternsNLJUG
 
Scala’s implicits
Scala’s implicitsScala’s implicits
Scala’s implicits
Pablo Francisco Pérez Hidalgo
 
IoC&Laravel
IoC&LaravelIoC&Laravel
IoC&Laravel
Hoang Long
 
Painless Javascript Unit Testing
Painless Javascript Unit TestingPainless Javascript Unit Testing
Painless Javascript Unit Testing
Benjamin Wilson
 
Introduction to Sightly
Introduction to SightlyIntroduction to Sightly
Introduction to Sightly
Ankit Gubrani
 
Javascript Best Practices
Javascript Best PracticesJavascript Best Practices
Javascript Best Practices
Christian Heilmann
 
Design Patterns For 70% Of Programmers In The World
Design Patterns For 70% Of Programmers In The WorldDesign Patterns For 70% Of Programmers In The World
Design Patterns For 70% Of Programmers In The World
Saurabh Moody
 
Spring AOP
Spring AOPSpring AOP
Spring AOP
Jeroen Rosenberg
 
Doc abap
Doc abapDoc abap
Doc abap
poussin_forever
 
React advance
React advanceReact advance
React advance
Vivek Tikar
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular Intermediate
LinkMe Srl
 
Introduction to Core Java Programming
Introduction to Core Java ProgrammingIntroduction to Core Java Programming
Introduction to Core Java Programming
Raveendra R
 
Advance JS and oop
Advance JS and oopAdvance JS and oop
Advance JS and oop
Abuzer Firdousi
 
Effective Java, Third Edition - Keepin' it Effective
Effective Java, Third Edition - Keepin' it EffectiveEffective Java, Third Edition - Keepin' it Effective
Effective Java, Third Edition - Keepin' it Effective
C4Media
 
2CPP03 - Object Orientation Fundamentals
2CPP03 - Object Orientation Fundamentals2CPP03 - Object Orientation Fundamentals
2CPP03 - Object Orientation Fundamentals
Michael Heron
 
Enterprise Java Beans( E)
Enterprise  Java  Beans( E)Enterprise  Java  Beans( E)
Enterprise Java Beans( E)vikram singh
 

What's hot (20)

Seven Versions of One Web Application
Seven Versions of One Web ApplicationSeven Versions of One Web Application
Seven Versions of One Web Application
 
Real-World Scala Design Patterns
Real-World Scala Design PatternsReal-World Scala Design Patterns
Real-World Scala Design Patterns
 
Scala’s implicits
Scala’s implicitsScala’s implicits
Scala’s implicits
 
IoC&Laravel
IoC&LaravelIoC&Laravel
IoC&Laravel
 
Painless Javascript Unit Testing
Painless Javascript Unit TestingPainless Javascript Unit Testing
Painless Javascript Unit Testing
 
Introduction to Sightly
Introduction to SightlyIntroduction to Sightly
Introduction to Sightly
 
Javascript Best Practices
Javascript Best PracticesJavascript Best Practices
Javascript Best Practices
 
slingmodels
slingmodelsslingmodels
slingmodels
 
Design Patterns For 70% Of Programmers In The World
Design Patterns For 70% Of Programmers In The WorldDesign Patterns For 70% Of Programmers In The World
Design Patterns For 70% Of Programmers In The World
 
Spring AOP
Spring AOPSpring AOP
Spring AOP
 
Doc abap
Doc abapDoc abap
Doc abap
 
ParisJS #10 : RequireJS
ParisJS #10 : RequireJSParisJS #10 : RequireJS
ParisJS #10 : RequireJS
 
React advance
React advanceReact advance
React advance
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular Intermediate
 
Introduction to Core Java Programming
Introduction to Core Java ProgrammingIntroduction to Core Java Programming
Introduction to Core Java Programming
 
Advance JS and oop
Advance JS and oopAdvance JS and oop
Advance JS and oop
 
Effective Java
Effective JavaEffective Java
Effective Java
 
Effective Java, Third Edition - Keepin' it Effective
Effective Java, Third Edition - Keepin' it EffectiveEffective Java, Third Edition - Keepin' it Effective
Effective Java, Third Edition - Keepin' it Effective
 
2CPP03 - Object Orientation Fundamentals
2CPP03 - Object Orientation Fundamentals2CPP03 - Object Orientation Fundamentals
2CPP03 - Object Orientation Fundamentals
 
Enterprise Java Beans( E)
Enterprise  Java  Beans( E)Enterprise  Java  Beans( E)
Enterprise Java Beans( E)
 

Similar to iOS API Design

Reactive Type-safe WebComponents
Reactive Type-safe WebComponentsReactive Type-safe WebComponents
Reactive Type-safe WebComponents
Martin Hochel
 
Swift - One step forward from Obj-C
Swift -  One step forward from Obj-CSwift -  One step forward from Obj-C
Swift - One step forward from Obj-C
Nissan Tsafrir
 
RubyMotion
RubyMotionRubyMotion
RubyMotion
Mark
 
Visual AI Testing Using Applitools
Visual AI Testing Using ApplitoolsVisual AI Testing Using Applitools
Visual AI Testing Using Applitools
Mikhail Laptev
 
Dsug 05 02-15 - ScalDI - lightweight DI in Scala
Dsug 05 02-15 - ScalDI - lightweight DI in ScalaDsug 05 02-15 - ScalDI - lightweight DI in Scala
Dsug 05 02-15 - ScalDI - lightweight DI in Scala
Ugo Matrangolo
 
Protocols promised-land-2
Protocols promised-land-2Protocols promised-land-2
Protocols promised-land-2
Michele Titolo
 
Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2
Aldo Chiecchia
 
iOS
iOSiOS
Thinking In Swift
Thinking In SwiftThinking In Swift
Thinking In Swift
Janie Clayton
 
Migrating Objective-C to Swift
Migrating Objective-C to SwiftMigrating Objective-C to Swift
Migrating Objective-C to Swift
Elmar Kretzer
 
Unethical JavaScript - Giorgio Natili - Codemotion Rome 2017
Unethical JavaScript - Giorgio Natili - Codemotion Rome 2017Unethical JavaScript - Giorgio Natili - Codemotion Rome 2017
Unethical JavaScript - Giorgio Natili - Codemotion Rome 2017
Codemotion
 
Robots in Swift
Robots in SwiftRobots in Swift
Robots in Swift
Janie Clayton
 
Kotlin for Android - Vali Iorgu - mRready
Kotlin for Android - Vali Iorgu - mRreadyKotlin for Android - Vali Iorgu - mRready
Kotlin for Android - Vali Iorgu - mRready
MobileAcademy
 
How AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsHow AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design Patterns
Ran Mizrahi
 
Building a Native Camera Access Library - Part V - Transcript.pdf
Building a Native Camera Access Library - Part V - Transcript.pdfBuilding a Native Camera Access Library - Part V - Transcript.pdf
Building a Native Camera Access Library - Part V - Transcript.pdf
ShaiAlmog1
 
Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
Fabio Collini
 
Con5623 pdf 5623_001
Con5623 pdf 5623_001Con5623 pdf 5623_001
Con5623 pdf 5623_001
Euegene Fedorenko
 
SOLID
SOLIDSOLID

Similar to iOS API Design (20)

Reactive Type-safe WebComponents
Reactive Type-safe WebComponentsReactive Type-safe WebComponents
Reactive Type-safe WebComponents
 
Swift - One step forward from Obj-C
Swift -  One step forward from Obj-CSwift -  One step forward from Obj-C
Swift - One step forward from Obj-C
 
RubyMotion
RubyMotionRubyMotion
RubyMotion
 
Visual AI Testing Using Applitools
Visual AI Testing Using ApplitoolsVisual AI Testing Using Applitools
Visual AI Testing Using Applitools
 
Dsug 05 02-15 - ScalDI - lightweight DI in Scala
Dsug 05 02-15 - ScalDI - lightweight DI in ScalaDsug 05 02-15 - ScalDI - lightweight DI in Scala
Dsug 05 02-15 - ScalDI - lightweight DI in Scala
 
Protocols promised-land-2
Protocols promised-land-2Protocols promised-land-2
Protocols promised-land-2
 
Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2
 
iOS
iOSiOS
iOS
 
Thinking In Swift
Thinking In SwiftThinking In Swift
Thinking In Swift
 
Migrating Objective-C to Swift
Migrating Objective-C to SwiftMigrating Objective-C to Swift
Migrating Objective-C to Swift
 
Unethical JavaScript - Giorgio Natili - Codemotion Rome 2017
Unethical JavaScript - Giorgio Natili - Codemotion Rome 2017Unethical JavaScript - Giorgio Natili - Codemotion Rome 2017
Unethical JavaScript - Giorgio Natili - Codemotion Rome 2017
 
Knockoutjs
KnockoutjsKnockoutjs
Knockoutjs
 
Robots in Swift
Robots in SwiftRobots in Swift
Robots in Swift
 
Kotlin for Android - Vali Iorgu - mRready
Kotlin for Android - Vali Iorgu - mRreadyKotlin for Android - Vali Iorgu - mRready
Kotlin for Android - Vali Iorgu - mRready
 
Deep dive into Oracle ADF
Deep dive into Oracle ADFDeep dive into Oracle ADF
Deep dive into Oracle ADF
 
How AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsHow AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design Patterns
 
Building a Native Camera Access Library - Part V - Transcript.pdf
Building a Native Camera Access Library - Part V - Transcript.pdfBuilding a Native Camera Access Library - Part V - Transcript.pdf
Building a Native Camera Access Library - Part V - Transcript.pdf
 
Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
 
Con5623 pdf 5623_001
Con5623 pdf 5623_001Con5623 pdf 5623_001
Con5623 pdf 5623_001
 
SOLID
SOLIDSOLID
SOLID
 

More from Brian Gesiak

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

More from Brian Gesiak (10)

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

Recently uploaded

Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
Adtran
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
mikeeftimakis1
 
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex ProofszkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
Alex Pruden
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
ControlCase
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)
Ralf Eggert
 
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
James Anderson
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
Kari Kakkonen
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to ProductionGenerative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Aggregage
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Laura Byrne
 
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
UiPathCommunity
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
Enhancing Performance with Globus and the Science DMZ
Enhancing Performance with Globus and the Science DMZEnhancing Performance with Globus and the Science DMZ
Enhancing Performance with Globus and the Science DMZ
Globus
 
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
Jen Stirrup
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024
Pierluigi Pugliese
 

Recently uploaded (20)

Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
 
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex ProofszkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)
 
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to ProductionGenerative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to Production
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
 
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
Enhancing Performance with Globus and the Science DMZ
Enhancing Performance with Globus and the Science DMZEnhancing Performance with Globus and the Science DMZ
Enhancing Performance with Globus and the Science DMZ
 
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024
 

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