Server-Driven UI Android Implementation

Shubham Agrawal
4 min readMay 19, 2024

--

Have you ever wondered how quickly fewer applications in your smartphone change their user interface overnight without having to update their apps from the Play Store? We will take the example of any big tech giant platform such as Flipkart Amazon or Swiggy. They change their UI based on various factors such as location. Should be wondering how they are doing it?

Swiggy Server-Driven UI example
Server-driven UI for Swiggy based on location

What is Server-Driven UI?

Server-driven UI refers to a scenario in which the client is informed by the API about which components and content to show. All three of the major platforms — iOS, Android, and the web — can be used for this. I believe that this kind of development improves the responsiveness and manageability of native apps.

You can read more about the benefits of server-driven UI on Google. But, today we are going to discuss how we can achieve it.

How to achieve Server-Driven UI in Android?

There are multiple ways to achieve this in Android but we are going to use the most popular and the easiest data type using JSON.

Since JSON is easy to read and manage. Let’s have a look at an example of a JSON file.

{
"card": {
"log_id": "div2_sample_card",
"states": [
{
"state_id": 0,
"div": {
"items": [
{
"type": "container",
"width": {
"type": "match_parent"
},
"height": {
"type": "match_parent"
},
"items": [
{
"type": "image",
"image_url": "https://upload.wikimedia.org/wikipedia/commons/2/2f/Google_2015_logo.svg",
"width": {
"type": "fixed",
"value": 50
},
"height": {
"type": "fixed",
"value": 50
},
"preload_required": true,
"scale": "fit"
},
{
"type": "text",
"text": "Hello Server Driven UI ",
"width": {
"type": "match_parent"
},
"text_alignment_horizontal": "center"
}
],
"content_alignment_horizontal": "center",
"content_alignment_vertical": "center",
"background": [
{
"type": "solid",
"color": "#ffeeff"
}
]
}
],
"background": [
{
"color": "#F1EBDC",
"type": "solid"
}
],
"height": {
"type": "match_parent"
},
"orientation": "overlap",
"type": "container"
}
}
]
}
}

On the left-hand side of the image, you can see the JSON schema which beautifully creates a UI on the right-hand side with an image, text, and a background color.

Let me explain these JSON lines a little bit. Every div is used as a container inside which there will be a list of items.

Now there are some basic types. Let’s see some of them in detail.

  1. Container — It acts as a holder. Take it as a Canvas on which UI is drawn. A simple layout with a list of items. In the above example, we use a container to hold a list of images and text with a background. From the Android perspective, it uses width and height as match_parent but you can read more about this for iOS and Web.
  2. Image — As it states it holds the image with properties like image_url, width, height, and scale.
  3. Text — A simple text to be displayed with width, height, text, and alignment as its properties.

There are more types but for this blog, we are going to need this to understand the basics of server-driven UI.

Now, with UI data is created, how to draw it in apps.

Let's dive into the coding part of it so that it can be inflated on Android, iOS, and the web.

There is a cool library named DivKit developed by the team of Yandex that beautifully does this work. It’s in the production as well.

You can find the Github link to the sample project of Divkit here, Alternatively, you can see my project link for the clean code here.

I will explain how easy it is to integrate without writing any code in XML.

Step 1 — Implement the Divkit in build.gradle

dependencies {
implementation "com.yandex.div:div:29.14.0"
implementation "com.yandex.div:div-core:29.14.0"
implementation "com.yandex.div:div-json:29.14.0"
}

Step 2 — Inside your activity main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" />

Step 3 — Write this code inside of MainActivity.kt


class MainActivity : AppCompatActivity() {

private lateinit var context: Div2Context

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val divConfiguration = DivConfiguration.Builder(CoilDivImageLoader(this))
.actionHandler(object : DivActionHandler() {
override fun getUseActionUid(): Boolean {
Log.i("TAG", "getUseActionUid: ")
return super.getUseActionUid()
}

override fun handleAction(action: DivAction, view: DivViewFacade): Boolean {
Log.i("TAG", "handleAction: " + action.url?.evaluate(view.expressionResolver))
val stringUri = action.url?.evaluate(view.expressionResolver)!!
when (stringUri.scheme) {
"submit" -> {
// Write your code here on button press (actions in JSON)
}
}
return super.handleAction(action, view)
}

override fun handlePayload(payload: JSONObject) {
Log.i("TAG", "handlePayload: $payload")
super.handlePayload(payload)
}
})
.build()
this.context = Div2Context(
baseContext = this,
configuration = divConfiguration,
lifecycleOwner = this
)

updateUIWithJSON("YOUR JSON FILE NAME GOES HERE!!")
}

private fun updateUIWithJSON(jsonFileName: String) {
runOnUiThread {
val linearLayout = findViewById<LinearLayout>(R.id.container)
linearLayout.removeAllViews()

val divJson = DivAssetReader(context).read("$jsonFileName.json")
var templateJson = divJson.optJSONObject("templates")
val cardJson = divJson.getJSONObject("card")
val div = DivViewFactory(context, templateJson).createView(cardJson)

linearLayout.addView(div)
div.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
div.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
}
}
}

class DivViewFactory(
private val context: Div2Context,
private val templatesJson: JSONObject? = null
) {

private val environment = DivParsingEnvironment(ParsingErrorLogger.ASSERT).apply {
if (templatesJson != null) parseTemplates(templatesJson)
}

fun createView(cardJson: JSONObject): Div2View {
val divData = DivData(environment, cardJson)
return Div2View(context).apply {
setData(divData, DivDataTag(divData.logId))
}
}
}

That’s it. Once you run the code. The same above UI will show. No need to write any code and the best part is this JSON can be changed from the server with any additions and deletions without having to change the app code.

This is a very basic example display of server-drive UI. You can read more about this and handle and create any complex UI using DivKit.

That’s it. Happy Coding 😁

--

--