π² Kotlin Symbol Processor to auto-generate extensive sealed classes and interfaces for Android and Kotlin.
SealedX generates extensive sealed classes & interfaces based on common sealed classes for each different model. You can reduce writing repeated sealed classes for every different model by auto-generating based on KSP (Kotlin Symbol Processor).
You can massively reduce writing repeated files such as _UiState
sealed interfaces if your project is based on MVI architecture.
If you want to learn more about how to migrate and use cases, check out the repositories below:
To use KSP (Kotlin Symbol Processing) and SealedX library in your project, you need to follow steps below.
Add the KSP plugin below into your module's build.gradle
file:
Kotlin (KTS)
plugins {
id("com.google.devtools.ksp") version "1.9.0-1.0.12"
}
Groovy
plugins {
id("com.google.devtools.ksp") version "1.9.0-1.0.12"
}
Note: Make sure your current Kotlin version and KSP version is the same.
Add the dependency below into your module's build.gradle
file:
dependencies {
implementation("com.github.skydoves:sealedx-core:1.0.2")
ksp("com.github.skydoves:sealedx-processor:1.0.2")
}
To access generated codes from KSP, you need to set up the source path like the below into your module's build.gradle
file:
Android Kotlin (KTS)
kotlin {
sourceSets.configureEach {
kotlin.srcDir("$buildDir/generated/ksp/$name/kotlin/")
}
}
Android Groovy
android {
applicationVariants.all { variant ->
kotlin.sourceSets {
def name = variant.name
getByName(name) {
kotlin.srcDir("build/generated/ksp/$name/kotlin")
}
}
}
}
Pure Kotlin (KTS)
kotlin {
sourceSets.main {
kotlin.srcDir("build/generated/ksp/main/kotlin")
}
sourceSets.test {
kotlin.srcDir("build/generated/ksp/test/kotlin")
}
}
Pure Kotlin Groovy
kotlin {
sourceSets {
main.kotlin.srcDirs += 'build/generated/ksp/main/kotlin'
test.kotlin.srcDirs += 'build/generated/ksp/test/kotlin'
}
}
@ExtensiveSealed
annotation is the main trigger of the Kotlin Symbol Processor to run a sealed-extensive processor on compile time.
@ExtensiveSealed
must be annotated to sealed classes or interfaces, which should be a common model to generate extensive sealed classes and interfaces.@ExtensiveSealed
receives an array of@ExtensiveModel
annotations, which include the extensive model types.- If you build your project, extensive sealed classes or interfaces will be generated based on those extensive models.
Let's see a common UiState
sealed interface below that is annotated with @ExtensiveSealed
annotation:
@ExtensiveSealed(
models = [
ExtensiveModel(Poster::class),
ExtensiveModel(PosterDetails::class)
]
)
sealed interface UiState {
data class Success(val data: Extensive) : UiState
object Loading : UiState
object Error : UiState
}
The example codes above will generate PosterUiState
and PosterDetailsUiState
sealed interfaces below:
PosterUiState (generated):
public sealed interface PosterUiState {
public object Error : PosterUiState
public object Loading : PosterUiState
public data class Success(
public val `data`: Poster,
) : PosterUiState
}
PosterDetailsUiState (generated):
public sealed interface PosterDetailsUiState {
public object Error : PosterDetailsUiState
public object Loading : PosterDetailsUiState
public data class Success(
public val `data`: PosterDetails,
) : PosterDetailsUiState
}
See further sealed class examples
In the case of the sealed classes, it's not different fundamentally from sealed interface examples.
@ExtensiveSealed(
models = [ ExtensiveModel(type = Poster::class) ]
)
sealed class UiState {
data class Success(val data: Extensive) : UiState()
object Loading : UiState()
object Error : UiState()
}
The example codes above will generate the PosterUiState
sealed class below:
PosterUiState (generated):
public sealed class PosterUiState {
public object Error : PosterUiState()
public object Loading : PosterUiState()
public data class Success(
public val `data`: Poster,
) : PosterUiState()
}
@ExtensiveModel
annotation class contains information on extensive models like model type and a custom name, which decides the name of generated classes.
Basically, (the simple name of the type
) + (the name of common sealed classes) will be used to name of generated classes, but you can modify the prefix with the name
parameter like the example below:
@ExtensiveSealed(
models = [ ExtensiveModel(type = PosterExtensive::class, name = "Movie") ]
)
sealed interface UiState {
data class Success(val data: Extensive) : UiState
object Loading : UiState
object Error : UiState
}
The example codes above will generate MovieUiState
file instead of PosterExtensiveUiState
like the below:
MovieUiState (generated):
public sealed interface MovieUiState {
public object Error : MovieUiState
public object Loading : MovieUiState
public data class Success(
public val `data`: PosterExtensive,
) : MovieUiState
}
Basically, you can't set a collection type like a List
to the type
parameter of the @ExtensiveModel
annotation.
So if you need to set a collection type as an extensive model, you need to write a wrapper class like the below:
data class PosterExtensive(
val posters: List<Poster>
)
@ExtensiveSealed(
models = [ ExtensiveModel(type = PosterExtensive::class) ]
)
sealed interface UiState {
..
}
Extensive
is an interface that is used to represent extensive model types of sealed classes and interfaces.
When you need to use an extensive model type in your primary constructor of data class, you can use the Extensive
extensive type on your common sealed classes and interfaces.
It will be replaced by the extensive model type on compile time.
@ExtensiveSealed(
models = [ ExtensiveModel(type = PosterExtensive::class) ]
)
sealed interface UiState {
// You need to use the Extensive type if you want to use an extensive model type in the generated code.
data class Success(val data: Extensive) : UiState
..
}
The example codes above will generate PosterExtensiveUiState
sealed interface like the below:
PosterExtensiveUiState (generated):
public sealed interface PosterExtensiveUiState {
public object Error : PosterExtensiveUiState
public object Loading : PosterExtensiveUiState
public data class Success(
public val `data`: PosterExtensive,
) : PosterExtensiveUiState
}
As you can see from the example above, the Extensive
interface type will be replaced with the extensive model by the SealedX processor on compile time.
Support it by joining stargazers for this repository. β
Also, follow me on GitHub for my next creations! π€©
Designed and developed by 2022 skydoves (Jaewoong Eum)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.