White Labels

Can we do better?

Scalability

Will it hurt If tomorrow we asked to make 10 new white labels?

Problems:

  • if (IS_BRAND_NAME) {...}
  • BPF_FEATURE_ON(BMA_USE_TABBAR_MENU). Is it on?
  • Ambiguous hierarchy of application classes
  • Responsibilities and hierarchy of .xcconfigs
  • Same vs separate repositories

How do we check brand?

  • IS_HUGGLE macro - used 116 times
  • [BPFApplication isHuggleBrand] - used 3 times
  • BMAGlobals.isHuggle - used 2 times

 

Let's agree on one!

Styling

  • string constants
  • color constants
  • images

Behaviour

  • additional/restricted behavior
  • navigation differences 
  • brand specific bug fixes
  • location permissions differences.

What we use it for?

if (IS_HUGGLE) {
    self.loadingView = [[HUGLoadingScreenView alloc] initWithFrame:self.view.bounds videoLoadingPreferred:self.videoLoadingPreferred];
} else {          
    self.loadingView = [[BMALoadingScreenView alloc] initWithFrame:self.view.bounds];
}

Brand specific implementation

Instead, check for feature:

if (BMA_FEATURE_ON(USING_DOTS_SPASH_SCREEN)) {
    self.loadingView = [[BMADotsLoadingScreenView alloc] initWithFrame:self.view.bounds videoLoadingPreferred:self.videoLoadingPreferred];
} else {          
    self.loadingView = [[BMALoadingScreenView alloc] initWithFrame:self.view.bounds];
}

Check for brand:

if (IS_HUGGLE) {
   styleInfo = @{
       kBMAFlatSegmentedControlSelectionColor : [BMAStyleSheet colorForHuggleBlueColor],
       kBMAFlatSegmentedControlTitleSelectedColor : [BMAStyleSheet colorForHuggleBlueColor],
       kBMAFlatSegmentedControlUnderscoreLineColor : [BMAStyleSheet colorForHuggleSeparator],
   };
} else {
    ...
}

Brand styling

Instead, private BMAStyleSheet subclasses:

Brand hardcoded into method signature:

 styleInfo = @{
     kBMAFlatSegmentedControlSelectionColor : [BMAStyleSheet segmentedControlSelectionColor],
     kBMAFlatSegmentedControlTitleSelectedColor : [BMAStyleSheet segmentedControlTitleSelectedColor],
     kBMAFlatSegmentedControlUnderscoreLineColor : [BMAStyleSheet segmentedControlUnderscoreLineColor],
 };
if (IS_HUGGLE) {
    return @[
        @(BMAUserCollectionViewCellBadgeTypeNewPerson),
        @(BMAUserCollectionViewCellBadgeTypeMatched),
        @(BMAUserCollectionViewCellBadgeTypeCommonPlace),
    ];
} else {
    return @[
        @(BMAUserCollectionViewCellBadgeTypeMatched),
        @(BMAUserCollectionViewCellBadgeTypeBumpedInto),
        @(BMAUserCollectionViewCellBadgeTypeCommonPlace),
    ];
}

Exceptional brand specific logic

Still convertible to feature based:

Brand based badge priorities:

if (BMA_FEATURE_ON(NEW_PEOPLE)) {
    [priorities addObject:@(BMAUserCollectionViewCellBadgeTypeNewPerson)];
}

[priorities addObject:@(BMAUserCollectionViewCellBadgeTypeMatched)];

if (BMA_FEATURE_ON(BUMPED_INTO)) {
    [priorities addObject:@(BMAUserCollectionViewCellBadgeTypeBumpedInto)];
}

[priorities addObject:@(BMAUserCollectionViewCellBadgeTypeCommonPlace)];
BPF_FEATURE_ON(BMA_USE_TABBAR_MENU)

How do we check that a feature is on?

Suggestion: define static feature instead

let tabBarMenuFeature: BPFFeature = {
    let feature = BPFFeature(title: "Using Tab Bar Menu", 
                             group: "UI", 
                               key: "com.bma.using.tab.menu")
    feature.onByDefault = BPFApplication.isHuggleBrand()
    return feature
}()

public extension BPFFeature {
    public class func tabBarMenu() -> BPFFeature {
        return tabBarMenuFeature
    }
}
[brandBasedFeatures addObject:[BPFFeature tabBarMenu]];

    
BMA_FEATURE_ON([BPFFeature tabBarMenu].key);

Ambiguous hierarchy of application classes

@protocol BPFApplicationInterface <NSObject>
...
@end

@interface BPFApplication : NSObject <BPFApplicationInterface>
...
@end

@interface BMABaseApplication : BPFApplication // most of the things here

@end

@interface HUGApplication : BPFApplication
@end

@interface BMABadooApplication : BMABaseApplication // empty implementation
@end

@interface HONApplication : BMABaseApplication
@end

Does white label require application subclass?

Huggle derives all features from Badoo by default, but why it is different in the code?

Suggestion: move Badoo only code, fix Huggle superclass

Responsibilities and hierarchy of .xcconfigs

NSString *const BCFileUtilsTestFairyAppID = @"BPFTestFairyIdentifier";
NSString *testFairyAppIdentifier = [BCFileUtils infoEntryForKey:BCFileUtilsTestFairyAppID];

"Stringly" typed configuration

Unsafe usage of base configuration

FACEBOOK_APPID
base.xcconfig 12312312323
hon.xcconfig 54645645645
FACEBOOK_APPID
base.xcconfig N/A
badoo.xcconfig 12312312323
hon.xcconfig 54645645645

Suggestion: use .xcconfigs for required keys, move rest into  the code

Same

Pros:

+ all code is shared by default

+ separate workspaces available if needed

 

Separate

Pros:

+ no one brakes white label code

+ release whenever we want

 

Repositories for white labels

Cons:

- no code is shared by default

- versioning system needed for using shared code

Cons:

- base app changes brakes white labels

- the release process is not ready for multiple release branches

Take out:

  • use features instead of is_brand_name
  • make white labeling process safer, faster and cleaner by:
    • improving discoverability of checking if a feature is enabled for a brand. [BPFeature usingTabBarMenu]?
    • sorting out BMAStyleSheet. Class cluster?
    • sorting out base classes. 
    • sorting out .xcconfigs (extracting configuration code)

Extra points: get rid of CocoaPods for platform code?

Made with Slides.com