Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Firebase Remote Config + Kotlin = EasyFRC

Avatar for Omar Miatello Omar Miatello
September 25, 2017

Firebase Remote Config + Kotlin = EasyFRC

Slide for: GDD Europe 2017 Extended – Milano

Android utility for handle Firebase Remote Config

Firebase Remote Config | What is it?
Change the behavior and appearance of your app without publishing an app update.
Feature:
It’s a cloud service
A simple key-value store
Customize your app for segments of your user base (ex: A/B tests)

Docs: https://firebase.google.com/docs/remote-config/ (+ 3min video)

Avatar for Omar Miatello

Omar Miatello

September 25, 2017
Tweet

More Decks by Omar Miatello

Other Decks in Technology

Transcript

  1. Omar Miatello Personal profile google.com/+OmarMiatello Google+ Community: Kotlin for Android

    goo.gl/mUKF1w Slide on Google Presentation goo.gl/1S1Z1E Organizer Android Developer
  2. Firebase Remote Config | What is it? Change the behavior

    and appearance of your app without publishing an app update. Feature: • It’s a cloud service • A simple key-value store • Customize your app for segments of your user base (ex: A/B tests) Docs: https://firebase.google.com/docs/remote-config/ (+ 3min video)
  3. Firebase Remote Config | How it works? • Open Firebase

    console https://console.firebase.google.com • Choose or create a new project • Select “Remote Config” • Add/change/remove parameter • Publish!
  4. Firebase Remote Config | API architecture • Singleton exposed to

    your app • Default Config bundled with your app • Active Config for keep consistency • Fetched Config synced with FRC console values
  5. Firebase Remote Config | Configure project | 1/3 1) Add

    Firebase to your app Add the dependency for Remote Config to your app-level build.gradle file as part of this step: compile 'com.google.firebase:firebase-config:11.4.0' 2) Get the Remote Config singleton object mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance() 3) Set in-app default parameter values using a Map object or an XML resource file stored in your app's res/xml folder mFirebaseRemoteConfig.setDefaults(R.xml.remote_config_defaults);
  6. Firebase Remote Config | Configure project | 2/3 Example of

    Default Config XML <?xml version="1.0" encoding="utf-8"?> <defaultsMap> <entry> <key>loading_phrase</key> <value>Fetching config…</value> </entry> <entry> <key>secretFeature1_enabled</key> <value>false</value> </entry> </defaultsMap>
  7. Firebase Remote Config | Configure project | 3/3 4) Get

    parameter values to use in your app getBoolean(key) , getByteArray(key), getDouble(key), getLong(key), getString(key) mFirebaseRemoteConfig.getBoolean("secretFeature1_enabled"); 5) Fetch and activate values from the service (as needed) • To fetch parameter values from the Remote Config service, call the fetch() method. Any values that you set in the Remote Config service are fetched and cached in the Remote Config object. • To make fetched parameter values available to your app, call the activateFetched() method.
  8. Kotlin: Properties Declaring Properties val secretFeature1_enabled: Boolean = false Properties:

    Getters and Setters val secretFeature1_enabled: Boolean get() = FirebaseRemoteConfig.getInstance().getBoolean("secretFeature1_enabled") From code if (secretFeature1_enabled) { // do something … }
  9. Kotlin: Delegated Properties | 1/3 There are certain common kinds

    of properties, that, though we can implement them manually every time we need them, would be very nice to implement once and for all, and put into a library. class Example { val myProperty: String by MyDelegate() }
  10. Kotlin: Delegated Properties | 2/3 Define getValue() in your MyDelegate

    class. class Example { val myProperty: String by MyDelegate() } class MyDelegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, thank you for delegating '${property.name}' to me!" } }
  11. Kotlin: Delegated Properties | 3/3 Providing a delegate (since Koltin

    1.1) class Example { val myProperty: String by MyDelegate() } class MyDelegate { operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): ReadOnlyProperty<Any?, T> { // init: check and create delegate } operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, thank you for delegating '${property.name}' to me!" } }
  12. The Goal Define a class with only properties and defaults

    object FRC { val signup_useHint by boolean(true) val secretFeature1_enabled by boolean(false) val logMaxLines by int(10) }
  13. The Goal | Singleton Define a class with only properties

    and defaults object FRC { // it’s a singleton! val signup_useHint by boolean(true) val secretFeature1_enabled by boolean(false) val logMaxLines by int(10) }
  14. The Goal | Declare config name once Define a class

    with only properties and defaults object FRC { val signup_useHint by boolean(true) // no need to declare string name! val secretFeature1_enabled by boolean(false) val logMaxLines by int(10) }
  15. The Goal | Static type-safe Define a class with only

    properties and defaults object FRC { val signup_useHint by boolean(true) // it’s a type-safe! val secretFeature1_enabled by boolean(false) val logMaxLines by int(10) }
  16. The Goal | Declare default Define a class with only

    properties and defaults object FRC { val signup_useHint by boolean(true) // has default! val secretFeature1_enabled by boolean(false) val logMaxLines by int(10) }
  17. The Goal | Easy to use Define a class with

    only properties and defaults object FRC { val signup_useHint by boolean(true) val secretFeature1_enabled by boolean(false) val logMaxLines by int(10) } Easy to use in our code (with static type-safe) if (FRC.secretFeature1_enabled) { // do something ... }
  18. Build EasyFRC | Auto setup for Firebase Remote Config abstract

    class EasyFRC { val _defaults = HashMap<String, Any?>() val frc by lazy { FirebaseRemoteConfig.getInstance().apply { setDefaults(_defaults) activateFetched() fetch() } } }
  19. Build EasyFRC | Our Delegate Class abstract class EasyFRC {

    val _defaults = HashMap<String, Any?>() val frc by lazy { FirebaseRemoteConfig.getInstance().apply { setDefaults(_defaults) activateFetched() fetch() } } class FRCDelegate<T>(val defaultValue: T, val getMethod: (String) -> T) { override fun getValue(thisRef: EasyFRC, property: KProperty<*>) = getMethod(property.name) } }
  20. Build EasyFRC | Method for build Delegates | 1/2 abstract

    class EasyFRC { val _defaults = HashMap<String, Any?>() val frc by lazy { FirebaseRemoteConfig.getInstance().apply { setDefaults(_defaults) activateFetched() fetch() } } fun string(defaultValue: String? = null) = FRCDelegate(defaultValue) { frc.getString(it) } class FRCDelegate<T>(val defaultValue: T, val getMethod: (String) -> T) { override fun getValue(thisRef: EasyFRC, property: KProperty<*>) = getMethod(property.name) } }
  21. Build EasyFRC | Method for build Delegates | 2/2 abstract

    class EasyFRC { // ... fun string(defaultValue: String? = null) = FRCDelegate(defaultValue) { frc.getString(it) } fun boolean(defaultValue: Boolean = false) = FRCDelegate(defaultValue) { frc.getBoolean(it) } // ... class FRCDelegate<T>(val defaultValue: T, val getMethod: (String) -> T) { override fun getValue(thisRef: EasyFRC, property: KProperty<*>) = getMethod(property.name) } }
  22. Build EasyFRC | provideDelegate() with Kotlin 1.1 abstract class EasyFRC

    { // ... fun string(defaultValue: String? = null) = FRCDelegate(defaultValue) { frc.getString(it) } fun boolean(defaultValue: Boolean = false) = FRCDelegate(defaultValue) { frc.getBoolean(it) } // ... class FRCDelegate<T>(val defaultValue: T, val getMethod: (String) -> T) { override fun getValue(thisRef: EasyFRC, property: KProperty<*>) = getMethod(property.name) operator fun provideDelegate(thisRef: EasyFRC, property: KProperty<*>): FRCDelegate<T> { thisRef._defaults.put(property.name, defaultValue) return this } } }
  23. Build EasyFRC | The result! object FRC : EasyFRC() {

    // Basic example val myBoolean by boolean(false) val myInt by int(1337) val myLong by long(134567890123456789) val myDouble by double(0.0) val myString by string("Ciao") val myByteArray by byteArray(ByteArray(0)) // Real world examples val signup_useHint by boolean(true) val secretFeature1_enabled by boolean(false) val secretFeature2_enabled by boolean(true) val cache_expireMax by long(TimeUnit.DAYS.toSeconds(7)) val logMaxLines by int(10) val logLevel by int(Log.INFO) }
  24. More complex example ... Added support for: devMode, custom keys,

    fetch() and fetchAndActivate() Full example: https://github.com/jacklt/jutils Import as dependency (with JitPack)! Gradle (v4 or later): implementation 'com.github.jacklt.jutils:easyfrc:1.0.1' or Gradle (before v4): compile 'com.github.jacklt.jutils:easyfrc:1.0.1'