Dowel
A Kotlin Symbol Processor to generate Compose PreviewParameterProviders
Install / Use
/learn @jayasuryat/DowelREADME
In Jetpack Compose, we use something called Previews, which are Composable functions written specifically to preview (or interact with) the UI rendered by Compose in the editor itself without needing to run the app on a device.
And Composable methods generally have some parameters based on which the UI is rendered. More often than not, Composable methods have a significant amount of data inputs which are needed to be passed from the preview methods for the previews to render.
In cases like these PreviewParameterProvider (a class from the Compose tooling library) can be used to provide data for the previews.
PreviewParameterProvider makes the Preview methods less verbose and easy to read by abstracting away the input data construction logic. And these providers can be reused across various Preview methods to render different previews.
2. Why Dowel?
Due to the amount of sheer verbosity involved in writing PreviewParameterProvider by hand, it becomes tedious to write PreviewParameterProvider for each and every UI model. And as writing PreviewParameterProvider for every model becomes an uninteresting task it becomes a barrier to entry for writing Previews for all the Composables.
That is where Dowel comes in and takes care of generating all of the boilerplate PreviewParameterProvider logic for your UI models.
This makes writing Previews simple and hence encourages writing more Previews for Composables in general. Apart from that, with Dowel you can also Fuzz test your Composables with all of the random values of random length or range being generated for all of the properties of the inputs.
Note : These random lengths or ranges can also be regulated, read more at "4. How do I use Dowel?" section.
3. Gradle setup
3.1. Add KSP plugin to your module's build.gradle file
plugins {
id("com.google.devtools.ksp") version "1.7.0-1.0.6"
}
Note : Make sure your project's
Kotlinversion andKSP versionare the same. Learn more about the available versions here
3.2. Add jitpack.io to the repositories blocks in your project's settings.gradle file
pluginManagement {
repositories {
// Other repos
maven { url 'https://jitpack.io' } // <----- This is the line to add
}
}
dependencyResolutionManagement {
repositories {
// Other repos
maven { url 'https://jitpack.io' } // <----- This is the line to add
}
}
3.3. Add the Dowel dependencies in your module's build.gradle file
dependencies {
implementation("com.github.jayasuryat.dowel:dowel:0.8.0")
ksp("com.github.jayasuryat.dowel:dowel-processor:0.8.0")
}
3.4. Add KSP generated files as sources in your module's build.gradle file
kotlin {
sourceSets.configureEach {
kotlin.srcDir("$buildDir/generated/ksp/$name/kotlin/")
}
}
4. How?
Dowel uses Kotlin Symbol Processing API under the hood to read, parse, and process source code to generate appropriate PreviewParameterProviders.
The primary entry point into Dowel is with @Dowel annotation.
Dowel goes through all the classes annotated with @Dowel annotation and generates PreviewParameterProvider for each class.
For example
https://user-images.githubusercontent.com/37530409/192849433-42d4d116-4d68-4147-8ba2-c5781f34af8f.mp4
// File : NewsArticle.kt
import androidx.compose.runtime.State
import com.jayasuryat.dowel.annotation.Dowel
import kotlinx.coroutines.flow.Flow
@Dowel(count = 2)
data class NewsArticle(
val title: String,
val description: String,
val likes: Int,
val authors: List<String>,
val liveComments: Flow<List<String>>,
val isExpanded: State<Boolean>,
val status: Status,
val onArticleClicked: () -> Unit,
) {
enum class Status { Draft, Accepted, Posted }
}
Generates 👇
// File in generated sources : NewsArticlePreviewParamProvider.kt
package com.yourapp.module
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import kotlin.sequences.Sequence
import kotlinx.coroutines.flow.flowOf
public class NewsArticlePreviewParamProvider : PreviewParameterProvider<NewsArticle> {
public override val values: Sequence<NewsArticle> = sequenceOf(
NewsArticle(
title = "AdipiscingDuis ac porttitor et",
description = "Phasellusmassa suscipit iaculi",
likes = 94,
authors = listOf(
"Velamet ultricies malesuada co",
"Consequatmassa malesuada sapie",
"Ameta et at bibendum ut neque ",
"Mimollis ac consectetur Praese",
"Namimperdiet massa bibendum po",
),
liveComments = flowOf(listOf(
"Malesuadasit Duis dapibus cong",
"Metusluctus nec congue congue ",
"InPraesent est tempus ac ultri",
"Malesuadaquis est Lorem sapien",
"FinibusCras mattis imperdiet n",
)),
isExpanded = mutableStateOf(false),
status = NewsArticle.Status.values().random(),
onArticleClicked = {},
),
NewsArticle(
title = "Tempuspurus congue elit euismo",
description = "Conguemetus Duis enim tincidun",
likes = 3,
authors = listOf(
"Namcondimentum lobortis et ali",
"Congueeu ultrices lacinia sed ",
"Lectussuscipit nisi eu quis se",
"Utnisi sapien mi ex magna magn",
"Proinipsum malesuada enim sed ",
),
liveComments = flowOf(listOf(
"CongueProin nec metus metus ma",
"Antenisi consectetur ac purus ",
"Eualiquet malesuada turpis rho",
"LobortisDuis mollis ac a lacus",
"Magnaet Donec libero Lorem sap",
)),
isExpanded = mutableStateOf(false),
status = NewsArticle.Status.values().random(),
onArticleClicked = {},
),
)
}
5. How do I use Dowel?
There are only 3 Dowel annotations you need to know about:
@Dowel: The primary entry point intoDowel, triggers generationPreviewParameterProviderfor that class.@DowelList: Same as@Dowel, but generates aPreviewParameterProviderof typeList<T>whereTis the class annotated with@DowelListannotation. Rest of the behavior is same as the@Dowelannotation.@ConsiderForDowel: If you want to add support for an unsupported type, or override provider logic for a particular type, then you can do that with@ConsiderForDowelannotation.
Apart from that if you want to control range / length / size of the values being generated, you can do that with androidx.annotations. Currently these 3 are the only supported ones:
androidx.annotation.IntRange: Control the range ofIntandLongpropertiesandroidx.annotation.FloatRange: Control the range ofFloatandDoublepropertiesandroidx.annotation.Size: Control the size ofString,ListandMapproperties
6. What all is possible?
Dowel is quite flexible with the types it already supports, but there a
