I've created a class to wrap around Kotlin GraphQL queries:
class GraphQuery<DataType : Query.Data>(
private val api: Api,
private val queryFactory: suspend () -> Query<DataType>,
) {
private val control: MutableStateFlow<GraphQueryState<DataType>> = MutableStateFlow(GraphQueryState.NotStarted())
public val flow = control
.onStart { emit(GraphQueryState.Loading()) }
.flatMapConcat {
api.client
.query(queryFactory())
.fetchPolicy(FetchPolicy.NetworkOnly)
.watch()
.map { response ->
when (response.hasErrors()) {
true -> GraphQueryState.Error(response.errors!!)
else -> GraphQueryState.Ready(response.data!!)
}
}
}
public fun update(data: DataType) {
control.value = GraphQueryState.Updating(data)
}
public fun refresh() {
control.value = GraphQueryState.Loading()
}
}
The general intent is to reduce boilerplate code and give my application a consistent approach to retrieving data.
I consume an instance of this class via my ViewModel:
@HiltViewModel
class EditWebPaymentSettingsViewModel @Inject constructor (
appSettings: DataStore<Settings>,
api: Api,
) : ViewModel() {
private val settingsQuery = GraphQuery(api) {
val settings = appSettings.data.last()
SettingsForAdminQuery(
UUID.fromString(settings.currentOrganizationId),
SupportedSettingPurpose.WebPayment.value,
)
}
public val settings = settingsQuery.flow
}
Which is then consumed by a Composable in my application:
@Composable
fun EditWebPaymentSettingsScreen(
done: () -> Unit,
viewModel: EditWebPaymentSettingsViewModel = hiltViewModel()
) {
val webPaymentSettings by viewModel.settings.collectAsStateWithLifecycle(GraphQueryState.NotStarted())
when (webPaymentSettings) {
is GraphQueryState.Loading,
is GraphQueryState.NotStarted -> Loading()
is GraphQueryState.Ready,
is GraphQueryState.Updating -> TODO()
is GraphQueryState.Error -> TODO()
}
}
My problem is, I never get to any of the TODO() blocks. My webPaymentSettings remains the initial NotStarted value indefinitely and my query never runs.
Is there any easy way to tell why my query never runs? My utility class does a flatMapConcat which makes me think that the query should be issued after the very first value. I'm under the impression it's a hot flow because I'm using a MutableStateFlow, but maybe there's something I'm missing?
If we look at your flow chain, we have:
which is a flow that emits first
Loading, thenNotStarted(probably not what you intended), thenwhich takes emissions and replaces them with the Apollo Kotlin call.
(Noteworthy:
watch()never completes.)So you should never get
LoadingnorNotStarted, but onlyReadyorError.But you never get
ReadynorError- could it be thatappSettings.data.last()blocks?