Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Write once, ship multiple
times
ŽELJKO PLESAC
Independent design & development agency
107 people in 3 offices
18 Android engineers
WHITE LABEL SYSTEMS
A white label product is a product or
service produced by one company
(the producer) that other companies
(the marketers) ...
Provides your brand with a refined product.
Outsourcing the development to a trusted thirty-party company.
Saving time and ...
Lack of control.
Lack of customisation.
Unified product vision.
HOW TO BUILD WHITE LABEL APPS?
NO.
Carefully
designing the
system.
PREREQUISITES
THOU SHALL NOT USE FAT CLIENTS.
Fat clients
• not customisable on the fly
• new app version for each new
feature
• prone to errors
• hard to maintain
MOST OF THE SYSTEMS FAIL ON THIS
POINT.
SYSTEM ARCHITECTURE
Flavours.
Modules.
FLAVOUR ARCHITECTURE
Main flavour.
Product flavours.
MAIN FLAVOUR
• default product
• used only for demo purposes
• contains shared code
• uses default set of resources
• can ...
FLAVOURS
• each flavour is an application
• holds product specific resources
• contains only product specific code
• should b...
ADVANTAGES
Easy to maintain and develop.
Easy to configure.
Resource and code sharing between main flavour and product flavou...
DISADVANTAGES
Customisation is extremely hard.
Exponential increase of files and resources.
MODULE ARCHITECTURE
Main module.
Product module.
MAIN MODULE
• set of core functionalities
• default resources
• can be configured by configuration file
PRODUCT MODULES
• each module is one app
• product specific code and resources
ADVANTAGES
Easy to customise.
Apps are isolated.
Business logic can be product specific.
DISADVANTAGES
Hard to maintain.
Losing focus.
CUSTOMIZE APP BEHAVIOUR
BUILD CONFIG FILE
Customise constant values between the apps.
productFlavors {
white {
dimension ‘product’
applicationId ‘com.infinum.white’
buildConfigField STRING, GOOGLE_ANALYTICS_I...
HostModule.setEndpoint(BuildConfig.API_URL);
AnalyticsModule.setGoogleAnalyticsId(BuildConfig.
GOOGLE_ANALYTICS_ID);
BONUS - CUSTOMISE RESOURCES
applicationVariants.all {

resValue XML_STRING, SEARCH_AUTHORITY, applicationId + '.providers.SearchSuggestionsProvider'

}
CONFIGURATION FILE
Locally cached or obtained from the API.
More flexible that BuildConfig file.
Descriptive method naming.
CUSTOMISE APPLICATION ON THE FLY
• enable/disable features
• data flow
• screen flow
public interface AppConfig {
boolean isSocialLoginEnabled();
List<MenuItem> getLoggedInUserNavigationMenu();
}
public class WhiteConfiguration implements AppConfig {
private static final List<MenuItem> LOGGED_IN_USER_NAVIGATION_MENU ...
DESCRIPTIVE METHODS
Method names should be descriptive and should not contain product names.
public interface AppConfig {
...
PROGRAM TO INTERFACES
INTERFACES
Extract functionalities.
Integrate with external dependencies.
Default or product specific implementation.
public interface ImageLoader {
void displayImage(ImageView imageView, @NonNull String imageUrl);
void displayImage(ImageVi...
@Module
public class ProvidersModule {
@Provides
public ImageLoader provideImageLoader(ImageLoaderType type) {
switch(type...
Include external dependencies only for specific product types.
whiteCompile 'com.github.bumptech.glide:glide:3.6.1'
blueCom...
GROUP FUNCTIONALITIES IN
FEATURES
Features
Each functionality is a feature.
Can be enabled/disabled.
Can be customised.
Feature
Contains everything what’s needed
for their integration.
Opened or closed.
CUSTOMISATION EXAMPLES
Default resources + product specific resources.
Default resources + product specific resources.
Same key is needed.
Provide presenter implementation per product.
Provide presenter implementation per product.
DI has to be handled per product (no default option).
Interfaces, features and configuration.
Interfaces, features and configuration.
Provide default and custom configuration.
Interfaces, features and configuration.
Provide default and custom configuration.
WHAT?
Feature has a default navigation flow, but it has to be customised only for one
or two products.
public interface Navigation {
<T> void createTrip(FragmentActivity activity, int
createTripRequestCode, Map<String, T> par...
public interface NavigationBehaviour {
<T> void startCreateTripScreen(FragmentActivity activity, int createTripRequestCode...
public class NavigationManager implements Navigation {
private NavigationBehaviour navigationBehaviour;
public NavigationM...
public class DefaultNavigationBehaviour implements NavigationBehaviour {
@Override
public <Parcelable> void startCreateTri...
public class BlueNavigationBehaviour implements NavigationBehaviour {
@Override
public <Parcelable> void startCreateTripSc...
CUSTOMIZE EVERYTHING?
When too much customisation is needed.
Different product vision.
FLAVOUR MODULE
STAND ALONE
APPLICATION
ROUNDUP
Thin clients.
Architecture planning.
Offer limited customisation.
Leave the system.
Visit infinum.co or find us on social networks:
infinum.co infinumco infinumco infinum
Thank you!
ZELJKO.PLESAC@INFINUM.CO
@ZELJ...
Write once, ship multiple times
Write once, ship multiple times
Write once, ship multiple times
Upcoming SlideShare
Loading in …5
×

Write once, ship multiple times

267 views

Published on

Best practices in developing white-label apps.

Published in: Software
  • Be the first to comment

Write once, ship multiple times

  1. 1. Write once, ship multiple times ŽELJKO PLESAC
  2. 2. Independent design & development agency 107 people in 3 offices 18 Android engineers
  3. 3. WHITE LABEL SYSTEMS
  4. 4. A white label product is a product or service produced by one company (the producer) that other companies (the marketers) rebrand to make it appear as if they had made it. - WIKIPEDIA
  5. 5. Provides your brand with a refined product. Outsourcing the development to a trusted thirty-party company. Saving time and money.
  6. 6. Lack of control. Lack of customisation. Unified product vision.
  7. 7. HOW TO BUILD WHITE LABEL APPS?
  8. 8. NO.
  9. 9. Carefully designing the system.
  10. 10. PREREQUISITES
  11. 11. THOU SHALL NOT USE FAT CLIENTS.
  12. 12. Fat clients • not customisable on the fly • new app version for each new feature • prone to errors • hard to maintain
  13. 13. MOST OF THE SYSTEMS FAIL ON THIS POINT.
  14. 14. SYSTEM ARCHITECTURE
  15. 15. Flavours. Modules.
  16. 16. FLAVOUR ARCHITECTURE
  17. 17. Main flavour. Product flavours.
  18. 18. MAIN FLAVOUR • default product • used only for demo purposes • contains shared code • uses default set of resources • can be customised by configuration file
  19. 19. FLAVOURS • each flavour is an application • holds product specific resources • contains only product specific code • should be as minimal as possible
  20. 20. ADVANTAGES Easy to maintain and develop. Easy to configure. Resource and code sharing between main flavour and product flavour.
  21. 21. DISADVANTAGES Customisation is extremely hard. Exponential increase of files and resources.
  22. 22. MODULE ARCHITECTURE
  23. 23. Main module. Product module.
  24. 24. MAIN MODULE • set of core functionalities • default resources • can be configured by configuration file
  25. 25. PRODUCT MODULES • each module is one app • product specific code and resources
  26. 26. ADVANTAGES Easy to customise. Apps are isolated. Business logic can be product specific.
  27. 27. DISADVANTAGES Hard to maintain. Losing focus.
  28. 28. CUSTOMIZE APP BEHAVIOUR
  29. 29. BUILD CONFIG FILE
  30. 30. Customise constant values between the apps.
  31. 31. productFlavors { white { dimension ‘product’ applicationId ‘com.infinum.white’ buildConfigField STRING, GOOGLE_ANALYTICS_ID, ‘”analyticsIdForWhite”’ buildConfigField STRING, API_URL, ‘”www.api.co/white/v1”’ manifestPlaceholders = [urlScheme: “white”] } blue { dimension ‘product’ applicationId ‘com.infinum.blue’ buildConfigField STRING, GOOGLE_ANALYTICS_ID, ‘”analyticsIdForBlue”’ buildConfigField STRING, API_URL, ‘”www.api.co/blue/v1”’ manifestPlaceholders = [urlScheme: “blue”] } }
  32. 32. HostModule.setEndpoint(BuildConfig.API_URL); AnalyticsModule.setGoogleAnalyticsId(BuildConfig. GOOGLE_ANALYTICS_ID);
  33. 33. BONUS - CUSTOMISE RESOURCES
  34. 34. applicationVariants.all {
 resValue XML_STRING, SEARCH_AUTHORITY, applicationId + '.providers.SearchSuggestionsProvider'
 }
  35. 35. CONFIGURATION FILE
  36. 36. Locally cached or obtained from the API. More flexible that BuildConfig file. Descriptive method naming.
  37. 37. CUSTOMISE APPLICATION ON THE FLY • enable/disable features • data flow • screen flow
  38. 38. public interface AppConfig { boolean isSocialLoginEnabled(); List<MenuItem> getLoggedInUserNavigationMenu(); }
  39. 39. public class WhiteConfiguration implements AppConfig { private static final List<MenuItem> LOGGED_IN_USER_NAVIGATION_MENU = Collections.unmodifiableList(Arrays.asList( new MenuItem(R.drawable.ic_gamepad, R.string.home), new MenuItem(R.drawable.ic_settings, R.string.settings), new MenuItem(R.drawable.ic_settings, R.string.info) )); @Override public boolean isSocialLoginEnabled() { return !BuildConfig.DEBUG || BuildConfig.APPLICATION_ID.endsWith(STAGING_APP_ID); } @Override public List<MenuItem> getLoggedInUserNavigationMenu() { return LOGGED_IN_USER_NAVIGATION_MENU; } }
  40. 40. DESCRIPTIVE METHODS Method names should be descriptive and should not contain product names. public interface AppConfig { boolean isWhite(); boolean isBlue(); }
  41. 41. PROGRAM TO INTERFACES
  42. 42. INTERFACES Extract functionalities. Integrate with external dependencies. Default or product specific implementation.
  43. 43. public interface ImageLoader { void displayImage(ImageView imageView, @NonNull String imageUrl); void displayImage(ImageView imageView, @NonNull File imageFile); }
  44. 44. @Module public class ProvidersModule { @Provides public ImageLoader provideImageLoader(ImageLoaderType type) { switch(type){ case GLIDE: return new GlideImageLoader(); default: return new PicassoImageLoader(); } } provided with configuration file
  45. 45. Include external dependencies only for specific product types. whiteCompile 'com.github.bumptech.glide:glide:3.6.1' blueCompile 'com.github.bumptech.glide:glide:3.5.0’
  46. 46. GROUP FUNCTIONALITIES IN FEATURES
  47. 47. Features Each functionality is a feature. Can be enabled/disabled. Can be customised.
  48. 48. Feature Contains everything what’s needed for their integration. Opened or closed.
  49. 49. CUSTOMISATION EXAMPLES
  50. 50. Default resources + product specific resources.
  51. 51. Default resources + product specific resources. Same key is needed.
  52. 52. Provide presenter implementation per product.
  53. 53. Provide presenter implementation per product. DI has to be handled per product (no default option).
  54. 54. Interfaces, features and configuration.
  55. 55. Interfaces, features and configuration. Provide default and custom configuration.
  56. 56. Interfaces, features and configuration. Provide default and custom configuration.
  57. 57. WHAT?
  58. 58. Feature has a default navigation flow, but it has to be customised only for one or two products.
  59. 59. public interface Navigation { <T> void createTrip(FragmentActivity activity, int createTripRequestCode, Map<String, T> params); }
  60. 60. public interface NavigationBehaviour { <T> void startCreateTripScreen(FragmentActivity activity, int createTripRequestCode, Map<String, T> params); }
  61. 61. public class NavigationManager implements Navigation { private NavigationBehaviour navigationBehaviour; public NavigationManager(NavigationBehaviour navigationBehaviour) { this.navigationBehaviour = navigationBehaviour; } @Override public <T> void createTrip(FragmentActivity activity, int createTripRequestCode, Map<String, T> params) { navigationBehaviour.startCreateTripScreen(activity, createTripRequestCode, params); } }
  62. 62. public class DefaultNavigationBehaviour implements NavigationBehaviour { @Override public <Parcelable> void startCreateTripScreen(FragmentActivity activity, int createTripRequestCode, Map<String, Parcelable> params) { Intent intent = CreateTripActivity.newIntent(activity, TripType.WALKING, recentStops); activity.startActivityForResult(intent, createTripRequestCode); } }
  63. 63. public class BlueNavigationBehaviour implements NavigationBehaviour { @Override public <Parcelable> void startCreateTripScreen(FragmentActivity activity, int createTripRequestCode, Map<String, Parcelable> params) { Intent intent = PlanTripActivity.newIntent(activity, TripType.CAR, recentStops); activity.startActivityForResult(intent, createTripRequestCode); } }
  64. 64. CUSTOMIZE EVERYTHING?
  65. 65. When too much customisation is needed. Different product vision.
  66. 66. FLAVOUR MODULE STAND ALONE APPLICATION
  67. 67. ROUNDUP
  68. 68. Thin clients. Architecture planning. Offer limited customisation. Leave the system.
  69. 69. Visit infinum.co or find us on social networks: infinum.co infinumco infinumco infinum Thank you! ZELJKO.PLESAC@INFINUM.CO @ZELJKOPLESAC

×