Store: Problems when working with a List
I’m trying to setup a pretty basic Store that works with a List<T> 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<T> and ensure that the get() method will always return?
About this issue
- Original URL
- State: open
- Created 4 years ago
- Reactions: 3
- Comments: 21 (7 by maintainers)
I think we need Store to let the collector know that both the source of truth (the local data storage, in this case) and the fetcher have returned an empty data set. I believe this is related to something mentioned in another issue: As mentioned here by @yigit, https://github.com/dropbox/Store/issues/185#issuecomment-653175626, we need some way of modeling an empty state.
But, without understanding the full internals of Store, it seems that maybe this is simply a bug that ought to be fixed and not so much that there needs to be a new empty state returned from Store to the collector. I would fully expect that if a source of truth (with an output of a List type) returns an empty list, then Store uses the Fetcher to fetch that list and the response from the Fetcher is also an empty list, then the collector should receive an empty list. Currently, it’s getting hung up and no
Datatype ever returns.Hey @Kernald - Thanks for checking in. Docs and sample apps are WIP. The larger issue is on our backlog. Will do our best to tackle it soon. PRs are very much welcomed!