Skip to content

Commit 52a90c0

Browse files
committed
Refactor retrieving ViewModels
1 parent 7913816 commit 52a90c0

File tree

15 files changed

+92
-117
lines changed

15 files changed

+92
-117
lines changed

Fruitties/androidApp/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
android:dataExtractionRules="@xml/data_extraction_rules"
2323
android:supportsRtl="true"
2424
android:theme="@style/AppTheme"
25-
android:name=".di.App">
25+
android:name=".FruittiesAndroidApp">
2626
<activity
2727
android:name=".MainActivity"
2828
android:exported="true">

Fruitties/androidApp/src/main/java/com/example/fruitties/android/di/App.kt renamed to Fruitties/androidApp/src/main/java/com/example/fruitties/android/FruittiesAndroidApp.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.example.fruitties.android.di
17+
package com.example.fruitties.android
1818

1919
import android.app.Application
20+
import androidx.compose.runtime.staticCompositionLocalOf
2021
import com.example.fruitties.di.AppContainer
2122
import com.example.fruitties.di.Factory
2223

23-
class App : Application() {
24+
class FruittiesAndroidApp : Application() {
2425
/** AppContainer instance used by the rest of classes to obtain dependencies */
2526
lateinit var container: AppContainer
2627

@@ -29,3 +30,11 @@ class App : Application() {
2930
container = AppContainer(Factory(this))
3031
}
3132
}
33+
34+
/**
35+
* Allows retrieving the AppContainer, which represents a DI graph everywhere from a composable.
36+
* Because the [AppContainer] is effectively a singleton, we can use static composition local,
37+
* because it won't change during the app execution.
38+
*/
39+
val LocalAppContainer =
40+
staticCompositionLocalOf<AppContainer> { error("No AppContainer provided!") }

Fruitties/androidApp/src/main/java/com/example/fruitties/android/MainActivity.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.fillMaxSize
2424
import androidx.compose.material3.MaterialTheme
2525
import androidx.compose.material3.Surface
2626
import androidx.compose.runtime.Composable
27+
import androidx.compose.runtime.CompositionLocalProvider
2728
import androidx.compose.ui.Modifier
2829
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
2930
import androidx.navigation3.runtime.NavKey
@@ -36,6 +37,7 @@ import androidx.navigation3.ui.rememberSceneSetupNavEntryDecorator
3637
import com.example.fruitties.android.ui.CartScreen
3738
import com.example.fruitties.android.ui.FruittieScreen
3839
import com.example.fruitties.android.ui.ListScreen
40+
import com.example.fruitties.android.ui.FruittiesTheme
3941
import kotlinx.serialization.Serializable
4042

4143
@Serializable
@@ -54,12 +56,16 @@ class MainActivity : ComponentActivity() {
5456
super.onCreate(savedInstanceState)
5557
enableEdgeToEdge()
5658
setContent {
57-
MyApplicationTheme {
58-
Surface(
59-
modifier = Modifier.fillMaxSize(),
60-
color = MaterialTheme.colorScheme.background,
61-
) {
62-
NavApp()
59+
CompositionLocalProvider(
60+
LocalAppContainer provides (this.applicationContext as FruittiesAndroidApp).container,
61+
) {
62+
FruittiesTheme {
63+
Surface(
64+
modifier = Modifier.fillMaxSize(),
65+
color = MaterialTheme.colorScheme.background,
66+
) {
67+
NavApp()
68+
}
6369
}
6470
}
6571
}

Fruitties/androidApp/src/main/java/com/example/fruitties/android/ui/CartScreen.kt

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,35 +50,23 @@ import androidx.compose.runtime.getValue
5050
import androidx.compose.ui.Alignment
5151
import androidx.compose.ui.Modifier
5252
import androidx.compose.ui.graphics.Color
53-
import androidx.compose.ui.platform.LocalContext
5453
import androidx.compose.ui.res.stringResource
5554
import androidx.compose.ui.text.style.TextAlign
5655
import androidx.compose.ui.tooling.preview.Preview
5756
import androidx.compose.ui.unit.dp
5857
import androidx.lifecycle.viewmodel.compose.viewModel
59-
import com.example.fruitties.android.MyApplicationTheme
6058
import com.example.fruitties.android.R
61-
import com.example.fruitties.android.di.App
59+
import com.example.fruitties.android.LocalAppContainer
6260
import com.example.fruitties.model.CartItemDetails
6361
import com.example.fruitties.model.Fruittie
6462
import com.example.fruitties.viewmodel.CartUiState
6563
import com.example.fruitties.viewmodel.CartViewModel
66-
import com.example.fruitties.viewmodel.creationExtras
6764

6865
@Composable
69-
fun CartScreen(onNavBarBack: () -> Unit) {
70-
// Instantiate a ViewModel with a dependency on the AppContainer.
71-
// To make ViewModel compatible with KMP, the ViewModel factory must
72-
// create an instance without referencing the Android Application.
73-
// Here we put the KMP-compatible AppContainer into the extras
74-
// so it can be passed to the ViewModel factory.
75-
val app = LocalContext.current.applicationContext as App
76-
77-
val viewModel: CartViewModel = viewModel(
78-
factory = CartViewModel.Factory,
79-
extras = creationExtras(app.container),
80-
)
81-
66+
fun CartScreen(
67+
onNavBarBack: () -> Unit,
68+
viewModel: CartViewModel = viewModel(factory = LocalAppContainer.current.cartViewModelFactory)
69+
) {
8270
val cartState by viewModel.cartUiState.collectAsState()
8371

8472
CartScreen(
@@ -200,7 +188,7 @@ fun CartItem(
200188
@Preview
201189
@Composable
202190
private fun CartScreenPreview() {
203-
MyApplicationTheme {
191+
FruittiesTheme {
204192
CartScreen(
205193
onNavBarBack = {},
206194
cartState = CartUiState(
@@ -240,7 +228,7 @@ private fun CartScreenPreview() {
240228
@Preview
241229
@Composable
242230
private fun CartItemPreview() {
243-
MyApplicationTheme {
231+
FruittiesTheme {
244232
CartItem(
245233
cartItem = CartItemDetails(
246234
fruittie = Fruittie(

Fruitties/androidApp/src/main/java/com/example/fruitties/android/ui/FruittieScreen.kt

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@ import androidx.compose.runtime.Composable
2222
import androidx.compose.runtime.collectAsState
2323
import androidx.compose.ui.Alignment
2424
import androidx.compose.ui.Modifier
25-
import androidx.compose.ui.platform.LocalContext
2625
import androidx.compose.ui.res.stringResource
2726
import androidx.compose.ui.unit.dp
2827
import androidx.lifecycle.viewmodel.compose.viewModel
28+
import com.example.fruitties.android.LocalAppContainer
2929
import com.example.fruitties.android.R
30-
import com.example.fruitties.android.di.App
3130
import com.example.fruitties.model.Fruittie
3231
import com.example.fruitties.viewmodel.FruittieViewModel
3332
import com.example.fruitties.viewmodel.FruittieViewModel.Companion.FRUITTIE_ID_KEY
@@ -38,16 +37,13 @@ import com.example.fruitties.viewmodel.creationExtras
3837
fun FruittieScreen(
3938
fruittieId: Long,
4039
onNavBarBack: () -> Unit,
41-
) {
42-
val app = LocalContext.current.applicationContext as App
43-
44-
val viewModel: FruittieViewModel = viewModel(
45-
factory = FruittieViewModel.Factory,
46-
extras = creationExtras(app.container) {
40+
viewModel: FruittieViewModel = viewModel(
41+
factory = LocalAppContainer.current.fruittieViewModelFactory,
42+
extras = creationExtras {
4743
set(FRUITTIE_ID_KEY, fruittieId)
4844
},
4945
)
50-
46+
) {
5147
val state = viewModel.state.collectAsState().value
5248

5349
FruittieScreen(

Fruitties/androidApp/src/main/java/com/example/fruitties/android/MyApplicationTheme.kt renamed to Fruitties/androidApp/src/main/java/com/example/fruitties/android/ui/FruittiesTheme.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.example.fruitties.android
16+
package com.example.fruitties.android.ui
1717

1818
import androidx.compose.foundation.isSystemInDarkTheme
1919
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -31,7 +31,7 @@ import androidx.compose.ui.unit.dp
3131
import androidx.compose.ui.unit.sp
3232

3333
@Composable
34-
fun MyApplicationTheme(
34+
fun FruittiesTheme(
3535
darkTheme: Boolean = isSystemInDarkTheme(),
3636
content: @Composable () -> Unit,
3737
) {

Fruitties/androidApp/src/main/java/com/example/fruitties/android/ui/ListScreen.kt

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,35 +45,25 @@ import androidx.compose.runtime.getValue
4545
import androidx.compose.ui.Alignment
4646
import androidx.compose.ui.Modifier
4747
import androidx.compose.ui.input.nestedscroll.nestedScroll
48-
import androidx.compose.ui.platform.LocalContext
4948
import androidx.compose.ui.res.stringResource
5049
import androidx.compose.ui.text.style.TextOverflow
5150
import androidx.compose.ui.tooling.preview.Preview
5251
import androidx.compose.ui.unit.dp
5352
import androidx.lifecycle.viewmodel.compose.viewModel
53+
import com.example.fruitties.android.LocalAppContainer
5454
import com.example.fruitties.android.R
55-
import com.example.fruitties.android.di.App
5655
import com.example.fruitties.model.Fruittie
5756
import com.example.fruitties.viewmodel.MainViewModel
58-
import com.example.fruitties.viewmodel.creationExtras
5957

6058
@OptIn(ExperimentalMaterial3Api::class)
6159
@Composable
6260
fun ListScreen(
6361
onClickViewCart: () -> Unit,
6462
onFruittieClick: (Fruittie) -> Unit,
65-
) {
66-
// Instantiate a ViewModel with a dependency on the AppContainer.
67-
// To make ViewModel compatible with KMP, the ViewModel factory must
68-
// create an instance without referencing the Android Application.
69-
// Here we put the KMP-compatible AppContainer into the extras
70-
// so it can be passed to the ViewModel factory.
71-
val app = LocalContext.current.applicationContext as App
72-
val viewModel: MainViewModel = viewModel(
73-
factory = MainViewModel.Factory,
74-
extras = creationExtras(app.container),
63+
viewModel: MainViewModel = viewModel(
64+
factory = LocalAppContainer.current.mainViewModelFactory,
7565
)
76-
66+
) {
7767
val uiState by viewModel.homeUiState.collectAsState()
7868
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
7969
Scaffold(
@@ -135,7 +125,8 @@ fun FruittieItem(
135125
modifier = modifier
136126
.clickable {
137127
onClick(item)
138-
}.padding(16.dp),
128+
}
129+
.padding(16.dp),
139130
verticalAlignment = Alignment.CenterVertically,
140131
) {
141132
Column(

Fruitties/iosApp/iosApp/ui/CartView.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ struct CartView: View {
3030
/// The `CartViewModel.Factory` and `creationExtras` are provided to enable dependency injection
3131
/// and proper initialization of the ViewModel with its required `AppContainer`.
3232
let cartViewModel: CartViewModel = viewModelStoreOwner.viewModel(
33-
factory: CartViewModel.companion.Factory,
34-
extras: creationExtras(appContainer: appContainer.value)
33+
factory: appContainer.value.cartViewModelFactory,
3534
)
3635

3736
/// Observes the `cartUiState` `StateFlow` from the `CartViewModel` using SKIE's `Observing` utility.

Fruitties/iosApp/iosApp/ui/ContentView.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ struct ContentView: View {
3030
/// The `MainViewModel.Factory` and `creationExtras` are provided to enable dependency injection
3131
/// and proper initialization of the ViewModel with its required `AppContainer`.
3232
let mainViewModel: MainViewModel = viewModelStoreOwner.viewModel(
33-
factory: MainViewModel.companion.Factory,
34-
extras: creationExtras(appContainer: appContainer.value)
33+
factory: appContainer.value.mainViewModelFactory,
3534
)
3635
NavigationStack {
3736
Observing(mainViewModel.homeUiState) { homeUIState in

Fruitties/iosApp/iosApp/ui/FruittieScreen.swift

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,13 @@ struct FruittieScreen: View {
2626
var body: some View {
2727
let fruittieViewModel: FruittieViewModel =
2828
viewModelStoreOwner.viewModel(
29-
factory: FruittieViewModel.companion.Factory,
30-
extras: creationExtras(
31-
appContainer: appContainer.value,
32-
additional: { extras in
33-
extras.set(
34-
key: FruittieViewModel.companion.FRUITTIE_ID_KEY,
35-
t: fruittie.id
36-
)
37-
}
38-
)
29+
factory: appContainer.value.fruittieViewModelFactory,
30+
extras: creationExtras { extras in
31+
extras.set(
32+
key: FruittieViewModel.companion.FRUITTIE_ID_KEY,
33+
t: fruittie.id
34+
)
35+
}
3936
)
4037

4138
Observing(fruittieViewModel.state) { uiState in

0 commit comments

Comments
 (0)