MobileNativeFoundation / MobileNativeFoundation/Store
Problems when working with a List<T>
@digitalbuddha is already working on this.
Since Nov 16, 2021.
- Dominant language
- Kotlin
- Stars
- 3.4k
- Forks
- 217
- Avg merge
- 18m
- Merged PRs (30d)
- 4
Description
I'm trying to setup a pretty basic Store that works with a List that is backed by a (Room) SoT, but I am running into a few problems, outlined below. The crux of the problem is that a call to the store's get() method never returns in some cases when the list is empty.
My setup is fairly straight forward:
val myStore = StoreBuilder.from(
fetcher = Fetcher.of { postType: PostType ->
// Retrofit api call that returns a list of posts
val response = myApi.getPostsByType(postType)
// convert the api response json into a List<Post>
return when {
response.isSuccessful -> Post.fromWordpress(response.body())
else -> listOf()
}
},
sourceOfTruth = SourceOfTruth.of(
reader = { postType ->
//Dao method returns a Flow<List<Post>> from the "posts" table
db.postsDao().postsByTypeObservable(postType)
},
writer = { postType, postList ->
// writing the saved list to the Room DB is a bit more complicated,
// because we have some many-many relationships with categories...
db.postsDao().withTransaction {
val posts = postList.map { it.post }
val categories = postList.flatMap { it.categories }.distinct()
val postCategories = postList.flatMap { it.postCategories }.distinct()
db.postsDao().upsert(posts)
db.categoriesDao().upsert(categories)
db.postCategoriesDao().upsert(postCategories)
}
},
delete = db.postsDao()::deletePostsByType,
deleteAll = db.postsDao()::deleteAllPosts
)
).build()
This setup works OK, unless/until there are no posts to work with. I've specifically run into 2 problems:
- If you call
get()with this setup and there is no data in the SoT, the fetcher will not be called.
From reading the source code, it appears that the Fetcher only gets called if the reader returns null. But when working with a list, the reader will return an empty list, and not null. Because of that, the fetcher never gets called.
We can actually fix this problem fairly easily by converting an empty list to null in the reader:
reader = { postType ->
//Dao method returns a Flow<List<Post> from the "posts" table
db.postsDao().postsByTypeObservable(postType).map { entries ->
when {
entries.isEmpty() -> null
else -> entries
}
},
Now, an empty list is treated as null, and thus the fetcher gets called.
- But now we have a second problem. If you call
get()and there are no entries in the SoT, and the Fetcher also returns no entries, the suspendedget()method will never finish/complete.
That is because once the fetcher finishes up, there are no new entries to put into the database. The get() is ultimately (via a stream) observing the Flow from the reader, and that flow will not emit anything (since there were no writes). Because of that, there is never any value emitted on the Flow (due to the filters setup in the get()) method. And since there is no value ever emitted, the suspended get() method never finishes/completes.
Is there a way to setup a Store that works with a List and ensure that the get() method will always return?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.