Step #8: Fixing the Repository
Now, we need to have the ToDoRepository
really use the ToDoEntity.Store
, rather than just get it in a constructor parameter.
However, we have problems. ToDoRepository
works with models. ToDoEntity.Store
works with entities. We are going to need to be able to convert between these two types.
To that end, add this constructor and function to ToDoEntity
:
constructor(model: ToDoModel) : this(
id = model.id,
description = model.description,
isCompleted = model.isCompleted,
notes = model.notes,
createdOn = model.createdOn
)
fun toModel(): ToDoModel {
return ToDoModel(
id = id,
description = description,
isCompleted = isCompleted,
notes = notes,
createdOn = createdOn
)
}
These offer bi-directional conversion between a ToDoModel
and a ToDoEntity
. If we needed more data conversion between things that Room knows how to store and how we wanted to represent them in the models, we could have that logic here as well.
Then, replace the contents of ToDoRepository
with the following:
package com.commonsware.todo.repo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
class ToDoRepository(
private val store: ToDoEntity.Store,
private val appScope: CoroutineScope
) {
fun items(): Flow<List<ToDoModel>> =
store.all().map { all -> all.map { it.toModel() } }
fun find(id: String?): Flow<ToDoModel?> = store.find(id).map { it?.toModel() }
suspend fun save(model: ToDoModel) {
withContext(appScope.coroutineContext) {
store.save(ToDoEntity(model))
}
}
suspend fun delete(model: ToDoModel) {
withContext(appScope.coroutineContext) {
store.delete(ToDoEntity(model))
}
}
}
items()
now calls all()
on our ToDoEntity.Store
, to retrieve all of the entities. We use the map()
operator on Flow
to convert the List
of ToDoEntity
into a corresponding List
of ToDoModel
. So, items()
now returns a Flow
for that list of models. Every time ToDoEntity.Store
emits a new list of entities, our repository emits the corresponding list of models.
Similarly, find()
now calls find()
on the ToDoEntity.Store
and uses map()
to convert the entity into a model.
We also delegate our save()
and delete()
calls to their corresponding ones on ToDoEntity.Store
. We use the constructor that we added to ToDoEntity
to map from our models to our entities. And, we wrap the actual DAO calls in withContext()
, using a CoroutineContext
obtained from our CoroutineScope
. This says “use this context (and job) to manage the work in this coroutine”. Since the scope and context are tied to that SupervisorJob
, that job manages the work, rather than any job that was set up by callers of these suspend
functions. We will see how this comes into play when we update our viewmodels, in the next tutorial.
So, right now, our app is broken. SingleModelMotor
and RosterMotor
are expecting the old, synchronous repository API, instead of this new coroutines-based one. We will fix that, and get our app working once again, in the next tutorial.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.