Skip to content

Commit a0b5f57

Browse files
authored
Merge pull request #267 from fethij/generate-ui-and-presenter-factories
generate ui and presenter factories to ease boilerplate
2 parents 36857a8 + 414a58d commit a0b5f57

File tree

9 files changed

+37
-119
lines changed

9 files changed

+37
-119
lines changed

common/build.gradle.kts

+8
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ kotlin {
5050

5151
api(libs.bundles.kotlinInject)
5252
api(libs.circuit.foundation)
53+
api(libs.circuit.codegen.annotations)
5354

5455
implementation(libs.bundles.ktor.common)
5556
implementation(libs.androidx.room.runtime)
@@ -136,12 +137,19 @@ dependencies {
136137
ksp(libs.androidx.room.compiler)
137138
ksp(libs.kotlinInject.compiler)
138139
ksp(libs.kotlinInject.anvil.compiler)
140+
ksp(libs.circuit.codegen)
139141
}
140142

141143
room {
142144
schemaDirectory("$projectDir/schemas")
143145
}
144146

147+
ksp {
148+
arg("circuit.codegen.lenient", "true")
149+
arg("circuit.codegen.mode", "kotlin_inject_anvil")
150+
arg("kotlin-inject-anvil-contributing-annotations", "com.slack.circuit.codegen.annotations.CircuitInject")
151+
}
152+
145153

146154
configurations.configureEach {
147155
exclude("androidx.window.core", "window-core")

common/src/commonMain/kotlin/dev/johnoreilly/common/countrylist/CountryListPresenter.kt

+4-19
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,19 @@ package dev.johnoreilly.common.countrylist
33
import androidx.compose.runtime.Composable
44
import androidx.compose.runtime.collectAsState
55
import androidx.compose.runtime.getValue
6-
import com.slack.circuit.runtime.CircuitContext
6+
import com.slack.circuit.codegen.annotations.CircuitInject
77
import com.slack.circuit.runtime.Navigator
88
import com.slack.circuit.runtime.presenter.Presenter
9-
import com.slack.circuit.runtime.screen.Screen
10-
import dev.johnoreilly.common.screens.CountryListScreen
11-
import dev.johnoreilly.common.screens.NetworkListScreen
129
import dev.johnoreilly.common.getCountryName
1310
import dev.johnoreilly.common.repository.CityBikesRepository
11+
import dev.johnoreilly.common.screens.CountryListScreen
12+
import dev.johnoreilly.common.screens.NetworkListScreen
1413
import dev.johnoreilly.common.viewmodel.Country
1514
import me.tatarka.inject.annotations.Assisted
1615
import me.tatarka.inject.annotations.Inject
1716
import software.amazon.lastmile.kotlin.inject.anvil.AppScope
18-
import software.amazon.lastmile.kotlin.inject.anvil.ContributesBinding
19-
20-
21-
@Inject
22-
@ContributesBinding(AppScope::class, multibinding = true)
23-
class CountryListPresenterFactory(
24-
private val presenterFactory: (Navigator) -> CountryListPresenter,
25-
) : Presenter.Factory {
26-
override fun create(screen: Screen, navigator: Navigator, context: CircuitContext): Presenter<*>? {
27-
return when (screen) {
28-
CountryListScreen -> presenterFactory(navigator)
29-
else -> null
30-
}
31-
}
32-
}
3317

18+
@CircuitInject(CountryListScreen::class, AppScope::class)
3419
@Inject
3520
class CountryListPresenter(
3621
@Assisted private val navigator: Navigator,

common/src/commonMain/kotlin/dev/johnoreilly/common/countrylist/CountryListUI.kt

+3
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ import androidx.compose.ui.Modifier
1818
import androidx.compose.ui.unit.dp
1919
import bikeshare.common.generated.resources.Res
2020
import bikeshare.common.generated.resources.allDrawableResources
21+
import com.slack.circuit.codegen.annotations.CircuitInject
2122
import dev.johnoreilly.common.screens.CountryListScreen
2223
import dev.johnoreilly.common.viewmodel.Country
2324
import org.jetbrains.compose.resources.ExperimentalResourceApi
2425
import org.jetbrains.compose.resources.painterResource
26+
import software.amazon.lastmile.kotlin.inject.anvil.AppScope
2527

2628

29+
@CircuitInject(CountryListScreen::class, AppScope::class)
2730
@Composable
2831
fun CountryListUi(state: CountryListScreen.State, modifier: Modifier = Modifier) {
2932
Scaffold(modifier = modifier, topBar = { TopAppBar(title = { Text("Countries") }) }) { innerPadding ->

common/src/commonMain/kotlin/dev/johnoreilly/common/networklist/NetworkListPresenter.kt

+4-18
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,18 @@ package dev.johnoreilly.common.networklist
33
import androidx.compose.runtime.Composable
44
import androidx.compose.runtime.collectAsState
55
import androidx.compose.runtime.getValue
6-
import com.slack.circuit.runtime.CircuitContext
6+
import com.slack.circuit.codegen.annotations.CircuitInject
77
import com.slack.circuit.runtime.Navigator
88
import com.slack.circuit.runtime.presenter.Presenter
9-
import com.slack.circuit.runtime.screen.Screen
10-
import dev.johnoreilly.common.screens.NetworkListScreen
11-
import dev.johnoreilly.common.screens.StationListScreen
129
import dev.johnoreilly.common.getCountryName
1310
import dev.johnoreilly.common.repository.CityBikesRepository
11+
import dev.johnoreilly.common.screens.NetworkListScreen
12+
import dev.johnoreilly.common.screens.StationListScreen
1413
import me.tatarka.inject.annotations.Assisted
1514
import me.tatarka.inject.annotations.Inject
1615
import software.amazon.lastmile.kotlin.inject.anvil.AppScope
17-
import software.amazon.lastmile.kotlin.inject.anvil.ContributesBinding
18-
19-
@Inject
20-
@ContributesBinding(AppScope::class, multibinding = true)
21-
class NetworkListPresenterFactory(
22-
private val presenterFactory: (NetworkListScreen, Navigator) -> NetworkListPresenter
23-
) : Presenter.Factory {
24-
override fun create(screen: Screen, navigator: Navigator, context: CircuitContext): Presenter<*>? {
25-
return when (screen) {
26-
is NetworkListScreen -> presenterFactory(screen, navigator)
27-
else -> null
28-
}
29-
}
30-
}
3116

17+
@CircuitInject(NetworkListScreen::class, AppScope::class)
3218
@Inject
3319
class NetworkListPresenter(
3420
@Assisted private val screen: NetworkListScreen,

common/src/commonMain/kotlin/dev/johnoreilly/common/networklist/NetworkListUI.kt

+3
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ import androidx.compose.ui.Alignment
2222
import androidx.compose.ui.Modifier
2323
import androidx.compose.ui.text.style.TextOverflow
2424
import androidx.compose.ui.unit.dp
25+
import com.slack.circuit.codegen.annotations.CircuitInject
2526
import dev.johnoreilly.common.model.Network
2627
import dev.johnoreilly.common.screens.NetworkListScreen
28+
import software.amazon.lastmile.kotlin.inject.anvil.AppScope
2729

30+
@CircuitInject(NetworkListScreen::class, AppScope::class)
2831
@Composable
2932
fun NetworkListUi(state: NetworkListScreen.State, modifier: Modifier = Modifier) {
3033
Scaffold(
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
11
package dev.johnoreilly.common.screens
22

3-
import com.slack.circuit.runtime.CircuitContext
43
import com.slack.circuit.runtime.CircuitUiEvent
54
import com.slack.circuit.runtime.CircuitUiState
65
import com.slack.circuit.runtime.screen.Screen
7-
import com.slack.circuit.runtime.ui.Ui
8-
import com.slack.circuit.runtime.ui.ui
96
import dev.johnoreilly.common.model.Network
107
import dev.johnoreilly.common.remote.Station
11-
import dev.johnoreilly.common.countrylist.CountryListUi
12-
import dev.johnoreilly.common.networklist.NetworkListUi
13-
import dev.johnoreilly.common.stationlist.StationListUI
148
import dev.johnoreilly.common.viewmodel.Country
15-
import me.tatarka.inject.annotations.Inject
16-
import software.amazon.lastmile.kotlin.inject.anvil.AppScope
17-
import software.amazon.lastmile.kotlin.inject.anvil.ContributesBinding
189

1910

2011
@Target(AnnotationTarget.CLASS)
@@ -60,50 +51,4 @@ data class StationListScreen(val networkId: String) : Screen {
6051
sealed class Event : CircuitUiEvent {
6152
data object BackClicked : Event()
6253
}
63-
}
64-
65-
66-
67-
// TODO move these somewhere else
68-
69-
@Inject
70-
@ContributesBinding(AppScope::class, multibinding = true)
71-
class CountryListUiFactory : Ui.Factory {
72-
override fun create(screen: Screen, context: CircuitContext): Ui<*>? = when (screen) {
73-
is CountryListScreen -> {
74-
ui<CountryListScreen.State> { state, modifier ->
75-
CountryListUi(state, modifier)
76-
}
77-
}
78-
else -> null
79-
}
80-
}
81-
82-
83-
@Inject
84-
@ContributesBinding(AppScope::class, multibinding = true)
85-
class NetworkListUiFactory : Ui.Factory {
86-
override fun create(screen: Screen, context: CircuitContext): Ui<*>? = when (screen) {
87-
is NetworkListScreen -> {
88-
ui<NetworkListScreen.State> { state, modifier ->
89-
NetworkListUi(state, modifier)
90-
}
91-
}
92-
else -> null
93-
}
94-
}
95-
96-
@Inject
97-
@ContributesBinding(AppScope::class, multibinding = true)
98-
class StationListUiFactory : Ui.Factory {
99-
override fun create(screen: Screen, context: CircuitContext): Ui<*>? = when (screen) {
100-
is StationListScreen -> {
101-
ui<StationListScreen.State> { state, modifier ->
102-
StationListUI(state, modifier)
103-
}
104-
}
105-
else -> null
106-
}
107-
}
108-
109-
54+
}

common/src/commonMain/kotlin/dev/johnoreilly/common/stationlist/StationListPresenter.kt

+3-18
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,16 @@ package dev.johnoreilly.common.stationlist
33
import androidx.compose.runtime.Composable
44
import androidx.compose.runtime.collectAsState
55
import androidx.compose.runtime.getValue
6-
import com.slack.circuit.runtime.CircuitContext
6+
import com.slack.circuit.codegen.annotations.CircuitInject
77
import com.slack.circuit.runtime.Navigator
88
import com.slack.circuit.runtime.presenter.Presenter
9-
import com.slack.circuit.runtime.screen.Screen
10-
import dev.johnoreilly.common.screens.StationListScreen
119
import dev.johnoreilly.common.repository.CityBikesRepository
10+
import dev.johnoreilly.common.screens.StationListScreen
1211
import me.tatarka.inject.annotations.Assisted
1312
import me.tatarka.inject.annotations.Inject
1413
import software.amazon.lastmile.kotlin.inject.anvil.AppScope
15-
import software.amazon.lastmile.kotlin.inject.anvil.ContributesBinding
16-
17-
18-
@Inject
19-
@ContributesBinding(AppScope::class, multibinding = true)
20-
class StationListPresenterFactory(
21-
private val presenterFactory: (StationListScreen, Navigator) -> StationListPresenter
22-
) : Presenter.Factory {
23-
override fun create(screen: Screen, navigator: Navigator, context: CircuitContext): Presenter<*>? {
24-
return when (screen) {
25-
is StationListScreen -> presenterFactory(screen, navigator)
26-
else -> null
27-
}
28-
}
29-
}
3014

15+
@CircuitInject(StationListScreen::class, AppScope::class)
3116
@Inject
3217
class StationListPresenter(
3318
@Assisted private val screen: StationListScreen,

common/src/commonMain/kotlin/dev/johnoreilly/common/stationlist/StationListUI.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,18 @@ import androidx.compose.ui.text.font.FontWeight
3131
import androidx.compose.ui.unit.dp
3232
import bikeshare.common.generated.resources.Res
3333
import bikeshare.common.generated.resources.ic_bike
34+
import com.slack.circuit.codegen.annotations.CircuitInject
3435
import dev.johnoreilly.common.remote.Station
3536
import dev.johnoreilly.common.remote.freeBikes
3637
import dev.johnoreilly.common.screens.StationListScreen
37-
import me.tatarka.inject.annotations.Inject
3838
import org.jetbrains.compose.resources.painterResource
39+
import software.amazon.lastmile.kotlin.inject.anvil.AppScope
3940

4041
val lowAvailabilityColor = Color(0xFFFF8C00)
4142
val highAvailabilityColor = Color(0xFF008000)
4243

4344

44-
@Inject
45+
@CircuitInject(StationListScreen::class, AppScope::class)
4546
@Composable
4647
fun StationListUI(state: StationListScreen.State, modifier: Modifier = Modifier) {
4748
Scaffold(

gradle/libs.versions.toml

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
[versions]
2-
kotlin = "2.1.0"
3-
ksp = "2.1.0-1.0.29"
2+
kotlin = "2.1.10-RC2"
3+
ksp = "2.1.10-RC-1.0.29"
44

55
androidGradlePlugin = "8.7.3"
66
androidxActivity = "1.9.3"
77
androidxComposeBom = "2024.12.01"
88
androidxLifecycle = "2.8.7"
99
androidxNavigationCompose = "2.8.5"
1010
androidxRoom = "2.7.0-alpha12"
11-
circuit = "0.24.0"
11+
circuit = "0.25.0"
1212
compose-multiplatform = "1.7.3"
1313
composeAdaptiveLayout = "1.0.0"
1414
coroutines = "1.9.0"
1515
junit = "4.13.2"
1616
kmpNativeCoroutines = "1.0.0-ALPHA-38"
1717
kmpObservableViewModel = "1.0.0-BETA-7"
18-
kotlin-inject-anvil = "0.1.1"
18+
kotlin-inject-anvil = "0.1.2"
1919
kotlininject = "0.7.2"
2020
kotlinxSerialization = "1.7.3"
2121
ktor = "3.0.1"
@@ -53,15 +53,17 @@ androidx-navigation-compose = { module = "androidx.navigation:navigation-compose
5353
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "androidxRoom" }
5454
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "androidxRoom" }
5555
circuit-foundation = { module = "com.slack.circuit:circuit-foundation", version.ref = "circuit" }
56+
circuit-codegen-annotations = { module = "com.slack.circuit:circuit-codegen-annotations", version.ref = "circuit" }
57+
circuit-codegen = { module = "com.slack.circuit:circuit-codegen", version.ref = "circuit" }
58+
kotlinInject-compiler = { module = "me.tatarka.inject:kotlin-inject-compiler-ksp", version.ref = "kotlininject" }
59+
kotlinInject-runtime = { module = "me.tatarka.inject:kotlin-inject-runtime", version.ref = "kotlininject" }
5660
compose-adaptive = { module = "org.jetbrains.compose.material3.adaptive:adaptive", version.ref = "composeAdaptiveLayout" }
5761
compose-adaptive-layout = { module = "org.jetbrains.compose.material3.adaptive:adaptive-layout", version.ref = "composeAdaptiveLayout" }
5862
junit = { module = "junit:junit", version.ref = "junit" }
5963
kmpObservableViewModel = { module = "com.rickclephas.kmp:kmp-observableviewmodel-core", version.ref = "kmpObservableViewModel" }
6064
kotlinInject-anvil-compiler = { group = "software.amazon.lastmile.kotlin.inject.anvil", name = "compiler", version.ref = "kotlin-inject-anvil" }
6165
kotlinInject-anvil-runtime = { group = "software.amazon.lastmile.kotlin.inject.anvil", name = "runtime", version.ref = "kotlin-inject-anvil" }
6266
kotlinInject-anvil-runtime-optional = { group = "software.amazon.lastmile.kotlin.inject.anvil", name = "runtime-optional", version.ref = "kotlin-inject-anvil" }
63-
kotlinInject-compiler = { module = "me.tatarka.inject:kotlin-inject-compiler-ksp", version.ref = "kotlininject" }
64-
kotlinInject-runtime = { module = "me.tatarka.inject:kotlin-inject-runtime", version.ref = "kotlininject" }
6567
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" }
6668
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
6769
ktor-client-ios = { group = "io.ktor", name = "ktor-client-ios", version.ref = "ktor" }

0 commit comments

Comments
 (0)