Fork me on GitHub

Monday, July 13, 2015

Type-safe SharedPreference

Scaloid provides a concise way to access SharedPreference using type dynamic of Scala language. A sample code that demonstrate it looks like this:
val pref = Preferences()
val ec = pref.executionCount(0) // read with default value 0
pref.executionCount = ec + 1 // write
pref.remove("executionCount") // remove
It is clearly better than old-Android-API, but it still has some limitations:
  • No compile-time name checks
    If there are a typo on the key name of the preferences, compiler does not warn you. It will be a nightmare if some preference name is used in multiple places and only one has a typo.
  • Key names should be a Scala symbol name
    For example, a key.name@with:special#chars cannot be accessed with Scala type dynamic
  • Inconsistencies with method and preference access
    In the above example, pref.remove(...) is a pre-defined method call, while pref.executionCount(...) is a preference access.
Scaloid 4.0-RC2 contains a new way to access Preferences. Let's look into it:
val executionCount = preferenceVar(0) // default value 0
val ec = executionCount() // read
executionCount() = ec + 1 // write
executionCount.remove() // remove
It is cleaner, performs compile-time validation, and better semantics. Scala macro accesses the value name to be assigned, executionCount in this example. If you want a keyname has special characters, we provide an alternative:
val executionCount = preferenceVar("execution.count", 0)
Preference values does not have a dependency on android.context.Context when it is created. A Context is only needed when it is actually being accessed (e.g. reading, writing, removing). So, it can be a property of plain object (static method in Java terms), and can be defined only once:
object Prefs {  // It ensures compile-time name checking
  val executionCount = preferenceVar(0)
  val showTips = preferenceVar("show-tips", true)
}

class MyActivity extends SActivity {
  // access it anywhere having implicit Context
  if(Prefs.showTips()) displayTips() 
}
Let's compare it with the same code written in plain-old Android API:
object Prefs { // No idea about types, awful naming convention
  val EXECUTION_COUNT = "executionCount"
  val SHOW_TIPS = "show-tips"
}

class MyActivity extends SActivity {
  // Wordy and not type-safe
  if(defaultSharedPreference.getBoolean(Prefs.SHOW_TIPS, true)) displayTips() 
}
The full source code is listed in the Scaloid github repository. This is tested on a millionth-downloaded production app, Soundcorset.