Avoid Overrides

Method overrides make components more difficult to use.

Imagine you're adding an optional text color parameter to a component. You might be tempted to add an override:

@interface ArticleTextComponent
+ (instancetype)newWithText:(NSString *)text;
+ (instancetype)newWithText:(NSString *)text textColor:(UIColor *)textColor;
@end

But someone will later add another override:

@interface ArticleTextComponent
+ (instancetype)newWithText:(NSString *)text;
+ (instancetype)newWithText:(NSString *)text textColor:(UIColor *)textColor;
+ (instancetype)newWithText:(NSString *)text backgroundColor:(UIColor *)textColor;
+ (instancetype)newWithText:(NSString *)text
textColor:(UIColor *)textColor
backgroundColor:(UIColor *)backgroundColor;
@end

The component is splintering. It's not obvious which override to use and we need a lot of boilerplate behind the scenes to redirect to the designated initializer.

Instead, always expose all parameters in a single designated initializer and document which are optional.

@interface ArticleTextComponent
/**
@param text The text to render in the component.
@param textColor Optional; pass nil for the default.
@param backgroundColor Optional; pass nil for the default.
*/
+ (instancetype)newWithText:(NSString *)text
textColor:(UIColor *)textColor
backgroundColor:(UIColor *)backgroundColor;
@end

If there are too many parameters, a useful pattern is a "style struct". For example, see CKTextComponent:

struct CKTextKitAttributes {
NSAttributedString *attributedString;
NSAttributedString *truncationAttributedString;
NSCharacterSet *avoidTailTruncationSet;
NSLineBreakMode lineBreakMode;
NSUInteger maximumNumberOfLines;
CGSize shadowOffset;
UIColor *shadowColor;
CGFloat shadowOpacity;
CGFloat shadowRadius;
};
@interface CKTextComponent : CKComponent
+ (instancetype)newWithTextAttributes:(const CKTextKitAttributes &)attributes
viewAttributes:(const CKViewComponentAttributeValueMap &)viewAttributes
accessibilityContext:(const CKTextComponentAccessibilityContext &)accessibilityContext;
@end

Using aggregate initialization, it's easy to initialize the style struct with only some parameters:

{
.shadowColor = [UIColor blackColor],
.maximumNumberOfLines = 1,
}