Skip to content

Commit 6213ef9

Browse files
Refactor navigation to allow implementation in iOS side
1 parent 26d1e56 commit 6213ef9

File tree

7 files changed

+74
-34
lines changed

7 files changed

+74
-34
lines changed

androidApp/src/main/java/com/hicham/wcstoreapp/android/di/AppModule.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ import com.hicham.wcstoreapp.data.currencyformat.CurrencyFormatProvider
88
import com.hicham.wcstoreapp.data.currencyformat.StoreCurrencyFormatProvider
99
import com.hicham.wcstoreapp.di.AppCoroutineScopeQualifier
1010
import com.hicham.wcstoreapp.ui.NavigationManager
11-
import kotlinx.coroutines.GlobalScope
1211
import org.koin.dsl.module
13-
import org.koin.dsl.single
1412

1513
val appModule = module {
1614
single<NavigationManager> { get<AndroidNavigationManager>() }

androidApp/src/main/java/com/hicham/wcstoreapp/android/ui/navigation/AndroidNavigationManager.kt

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ import androidx.navigation.NavOptions
66
import androidx.navigation.NavOptionsBuilder
77
import androidx.navigation.navOptions
88
import com.hicham.wcstoreapp.ui.NavigationManager
9+
import io.ktor.utils.io.core.*
10+
import kotlinx.coroutines.CoroutineScope
11+
import kotlinx.coroutines.Dispatchers
12+
import kotlinx.coroutines.cancel
13+
import kotlinx.coroutines.channels.Channel
914
import kotlinx.coroutines.flow.*
15+
import kotlinx.coroutines.launch
16+
import kotlin.coroutines.EmptyCoroutineContext
1017

1118
class AndroidNavigationManager : NavigationManager {
1219
// We use a StateFlow here to allow ViewModels to start observing navigation results before the initial composition,
@@ -16,6 +23,7 @@ class AndroidNavigationManager : NavigationManager {
1623
private val navigationCommands =
1724
MutableSharedFlow<NavigationCommand>(extraBufferCapacity = Int.MAX_VALUE)
1825

26+
private val navigationResults = Channel<NavigationResult>(capacity = Channel.BUFFERED)
1927

2028
suspend fun handleNavigationCommands(navController: NavController) {
2129
navigationCommands
@@ -43,7 +51,7 @@ class AndroidNavigationManager : NavigationManager {
4351
navigationCommands.tryEmit(NavigationCommand.PopUpToRoute(route, inclusive))
4452
}
4553

46-
override fun <T> navigateBackWithResult(
54+
override fun <T: Any> navigateBackWithResult(
4755
key: String,
4856
result: T,
4957
destination: String?
@@ -57,23 +65,20 @@ class AndroidNavigationManager : NavigationManager {
5765
)
5866
}
5967

60-
override fun <T> observeResult(key: String, route: String?): Flow<T> {
61-
return navControllerFlow
62-
.filterNotNull()
63-
.flatMapLatest { navController ->
64-
val backStackEntry = route?.let { navController.getBackStackEntry(it) }
65-
?: navController.currentBackStackEntry
66-
67-
backStackEntry?.savedStateHandle?.let { savedStateHandle ->
68-
savedStateHandle.getLiveData<T?>(key)
69-
.asFlow()
70-
.filter { it != null }
71-
.onEach {
72-
// Nullify the result to avoid resubmitting it
73-
savedStateHandle.set(key, null)
74-
}
75-
} ?: emptyFlow()
68+
override fun <T: Any> observeResult(
69+
key: String,
70+
route: String?,
71+
onEach: (T) -> Unit
72+
): Closeable {
73+
val scope = CoroutineScope(Dispatchers.Main)
74+
navigationResults.receiveAsFlow()
75+
.filter { it.key == key }
76+
.onEach {
77+
(it.value as? T)?.let(onEach)
7678
}
79+
.launchIn(scope)
80+
81+
return Closeable { scope.cancel() }
7782
}
7883

7984
private fun NavController.handleNavigationCommand(navigationCommand: NavigationCommand) {
@@ -88,12 +93,11 @@ class AndroidNavigationManager : NavigationManager {
8893
navigationCommand.inclusive
8994
)
9095
is NavigationCommand.NavigateUpWithResult<*> -> {
91-
val backStackEntry =
92-
navigationCommand.destination?.let { getBackStackEntry(it) }
93-
?: previousBackStackEntry
94-
backStackEntry?.savedStateHandle?.set(
95-
navigationCommand.key,
96-
navigationCommand.result
96+
navigationResults.trySend(
97+
NavigationResult(
98+
navigationCommand.key,
99+
navigationCommand.result
100+
)
97101
)
98102

99103
navigationCommand.destination?.let {
@@ -104,14 +108,16 @@ class AndroidNavigationManager : NavigationManager {
104108
}
105109
}
106110
}
111+
112+
private data class NavigationResult(val key: String, val value: Any)
107113
}
108114

109115
sealed class NavigationCommand {
110116
object NavigateUp : NavigationCommand()
111117
data class NavigateToRoute(val route: String, val options: NavOptions? = null) :
112118
NavigationCommand()
113119

114-
data class NavigateUpWithResult<T>(
120+
data class NavigateUpWithResult<T: Any>(
115121
val key: String,
116122
val result: T,
117123
val destination: String? = null

iosApp/WCStoreApp.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@
304304
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
305305
MTL_FAST_MATH = YES;
306306
ONLY_ACTIVE_ARCH = YES;
307+
OTHER_LDFLAGS = "-lsqlite3";
307308
SDKROOT = iphoneos;
308309
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
309310
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -358,6 +359,7 @@
358359
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
359360
MTL_ENABLE_DEBUG_INFO = NO;
360361
MTL_FAST_MATH = YES;
362+
OTHER_LDFLAGS = "-lsqlite3";
361363
SDKROOT = iphoneos;
362364
SWIFT_COMPILATION_MODE = wholemodule;
363365
SWIFT_OPTIMIZATION_LEVEL = "-O";

iosApp/WCStoreApp/IOSNavigationManager.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ class IOSNavigationManager: NavigationManager, ObservableObject {
3737
navigationStack.pop(to: PopDestination.view(withId: route))
3838
}
3939

40+
func navigateBackWithResult(key: String, result: Any, destination: String?) {
41+
// TODO
42+
}
43+
44+
func observeResult(key: String, route: String?, onEach: @escaping (Any) -> Void) -> Ktor_ioCloseable {
45+
// TODO
46+
return closeable {
47+
48+
}
49+
}
50+
51+
4052
@ViewBuilder
4153
func getView(iOSScreen: IOSScreen, arguments: Dictionary<String, String>) -> some View {
4254
switch iOSScreen {
@@ -90,3 +102,13 @@ enum IOSScreen: String, CaseIterable {
90102
}
91103
}
92104
}
105+
106+
class closeable : Ktor_ioCloseable {
107+
private var closeCallback:() -> Void
108+
init(closeCallback: @escaping () -> Void) {
109+
self.closeCallback = closeCallback
110+
}
111+
func close() {
112+
self.closeCallback()
113+
}
114+
}
Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
package com.hicham.wcstoreapp.ui
22

3+
import io.ktor.utils.io.core.*
4+
import kotlinx.coroutines.channels.awaitClose
35
import kotlinx.coroutines.flow.Flow
6+
import kotlinx.coroutines.flow.callbackFlow
47

58
interface NavigationManager {
69
fun navigate(route: String)
710
fun navigateUp()
811
fun popUpTo(route: String, inclusive: Boolean = false)
9-
fun <T> navigateBackWithResult(
12+
fun <T : Any> navigateBackWithResult(
1013
key: String,
1114
result: T,
1215
destination: String? = null
1316
)
14-
fun <T> observeResult(key: String, route: String? = null): Flow<T>
17+
18+
fun <T : Any> observeResult(key: String, route: String? = null, onEach: (T) -> Unit): Closeable
19+
}
20+
21+
fun <T : Any> NavigationManager.observeResultAsFlow(key: String, route: String? = null): Flow<T> {
22+
return callbackFlow {
23+
val closeable = observeResult<T>(key, route) {
24+
trySend(it)
25+
}
26+
27+
awaitClose { closeable.close() }
28+
}
1529
}

shared/src/commonMain/kotlin/com/hicham/wcstoreapp/ui/checkout/CheckoutViewModel.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ import com.hicham.wcstoreapp.data.checkout.CheckoutRepository
66
import com.hicham.wcstoreapp.data.currencyformat.CurrencyFormatProvider
77
import com.hicham.wcstoreapp.models.Address
88
import com.hicham.wcstoreapp.models.PaymentMethod
9-
import com.hicham.wcstoreapp.ui.BaseViewModel
10-
import com.hicham.wcstoreapp.ui.CurrencyFormatter
11-
import com.hicham.wcstoreapp.ui.NavigationManager
12-
import com.hicham.wcstoreapp.ui.ShowSnackbar
9+
import com.hicham.wcstoreapp.ui.*
1310
import com.hicham.wcstoreapp.ui.checkout.address.AddAddressViewModel
1411
import com.hicham.wcstoreapp.ui.navigation.Screen
1512
import kotlinx.coroutines.flow.*
@@ -59,7 +56,7 @@ class CheckoutViewModel constructor(
5956

6057
private fun observeShippingAddress() {
6158
// Observe Added Address
62-
navigationManager.observeResult<Address>(AddAddressViewModel.ADDRESS_RESULT)
59+
navigationManager.observeResultAsFlow<Address>(AddAddressViewModel.ADDRESS_RESULT)
6360
.onEach {
6461
addressRepository.setPrimaryShippingAddress(it)
6562
}.launchIn(viewModelScope)

shared/src/commonMain/kotlin/com/hicham/wcstoreapp/ui/checkout/address/AddressListViewModel.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.hicham.wcstoreapp.ui.BaseViewModel
66
import com.hicham.wcstoreapp.ui.NavigationManager
77
import com.hicham.wcstoreapp.ui.ShowSnackbar
88
import com.hicham.wcstoreapp.ui.navigation.Screen
9+
import com.hicham.wcstoreapp.ui.observeResultAsFlow
910
import kotlinx.coroutines.flow.*
1011
import kotlinx.coroutines.launch
1112

@@ -35,7 +36,7 @@ class AddressListViewModel(
3536

3637
private fun observeSelectedAddress() {
3738
// Observe navigation result
38-
navigationManager.observeResult<Address>(AddAddressViewModel.ADDRESS_RESULT)
39+
navigationManager.observeResultAsFlow<Address>(AddAddressViewModel.ADDRESS_RESULT)
3940
.onStart {
4041
val defaultAddress = //savedStateHandle.get<Address>("address")
4142
addressRepository.primaryShippingAddress.first()

0 commit comments

Comments
 (0)