What is the correct approach of using Store5 with ...
# store
e
What is the correct approach of using Store5 with API, which returns not a single item by a key, but a list of items all at once (or paginated)? In this case basically the fetcher gets List<Entity> and the corresponding methods of SourceOfTruth read and write a list. But in this case looks like I can’t access individual items.
Our recommendation is basically to model your key and data with
StoreKey
and
StoreData
and provide a
StoreMultiCache
implementation to
StoreBuilder
Happy to review code, although docs are in progress, I won't have them published soon
e
@Matthew Ramotar As I understood the
StoreMultiCache
refers to the
memoryCache
parameter of the
Store
, correct? The
fetcher
and
sourceOfTruth
parameters both share the same
Key
type, so if the API returns a list (no key) should this
Key
be
Unit
or
Nothing
?
m
Yes,
StoreMultiCache
is passed in as the memory cache. The list should be associated with a key. Your fetcher and SOT should switch on the key and handle singles and collections
I think
Trails
has an example of this, 1 min
e
@Matthew Ramotar thank you for the example, diving into it right now 🙂
@Matthew Ramotar Why does StoreKey.Collection.Page has
val page: Int
instead of
val page: Any
? In my case the pagination is implemented in DynamoDb and it uses composite String keys (
nextPage
) which I have to utilise in the API. Is it possible to somehow use paging in this case?
m
The
StoreKey.Collection.Page
key is intended for page-based fetches, where page number is typically an integer. Is
nextPage
a string representation of an integer? What are example values? If you could share a sample API response, that would be helpful
e
@Matthew Ramotar This is a sample API response:
Copy code
{"count":1,"nextKey":"2024-02-17T19:34:28.722Z|4e6dc288-2cb0-441d-ba2b-9f2208adbf46", media: [...]}
I am not backend developer, but as I understood the key is a composite of columns used in the index, by which the pagination is performed. In this particular case pagination uses the key=date+guid, that’s why the nextKey value is basically a String with concatenation of values in these columns of the row, which should be the first in the next page.
So in general the page number as an Integer is just a special case (most common but still special) of pagination. It would be great if Any value could be used.
m
Thanks for the context. The API response you've shared demonstrates an implementation of cursor-based pagination. I think you should use our
Cursor
key
e
@Matthew Ramotar What is the semantical difference?
@Matthew Ramotar Also on another project we had to use pagination by DateTime instead of Integer, because it was a list of events and they could be updated pretty frequently (insert/delete) and in case of Int-based pagination that could produce duplicates on the client side. On the other hand the use of DateTime instead in most cases avoids duplication because it is unlikely that 2 events will have identical start date/time.
@Matthew Ramotar I tried to use StoreKey with
Cursor
but came across another issue. In
StoreKey
the
Id
type should be the same for Cursor and Single. In my case they are both String technically, but have absolutely different semantics (for cursor - it is a composite string from DynamoDb and for single it is an media ID, which is GUID. Just as an experiment I tried to set different type parameters for
Cursor
and
Id
and naturally got an error that the types are inconsistent. What is the motivation behind having the same
Id
type for the
Cursor
and
Single
? In general they can be different imho and are different in my case.
m
Thanks for surfacing this! I created a ticket https://github.com/MobileNativeFoundation/Store/issues/604
e
@Matthew Ramotar Thank you for your feedback and help!
👍 1
I still don’t totally follow the difference between Page and Cursor. In my understanding it is something very similar. Do we actually need both? Is there any difference in internal implementation for them?
@Matthew Ramotar could you please advise, is there any workaround for now to use pagination with my types?
m
It needs to implement
StoreKey.Collection
, but otherwise our impl doesn't care if it's custom
👀 1
e
@Matthew Ramotar Thank you! I am checking this 🙂
👍 1
@Matthew Ramotar When do you plan to release this changes?
m
#603 - I need to address Yigit’s comments. Very busy week and will be out of town this weekend. Probably not until next week sometime #604 - I don’t have this planned rn, but am happy to review a PR
e
@Matthew Ramotar Thank you! I have added the classes from that branch locally for now. I have another semantical question. When I setup a SOT and create a reader it doesn’t make much sense to read something from DB, because the only parameter is a page identifier (cursor) and it is not present in the database. Should I return empty list in this case in the reader?
The same question is valid for the fetcher. If my API doesn’t provide methods to manipulate with single records, just pagination method, what should return the fetcher for a Single key? Should it return an empty result or error?
@Matthew Ramotar Also I have a thought regarding the API. Is it possible to get Single key with Collection data and vice versa Collection/Page key with Single data? Both combinations probably doesn’t make sense, but are allowed in the current API.
Also it normally can be situation when the app already has a number of pages cached. Say, we just started the app and we want to show the cached items, then refresh them (optional) and only when user scrolls to the end of this already downloaded items we are going to request a new page. It looks like for this initial request we need a separate type of
StoreKey
, because both
Page
and
Single
are unsuitable. And if we introduce third type of key, let’s say
Heap
we will get even more pairs of incompatible pairs key-data in both fetcher and SOT.
In other words,
Page
makes sense only for
fetcher
and
writer
, not for
reader
.
Single
-
fetcher
(only if we have API),
writer
and
reader
.
Heap
- only for
reader
in general but probably could be enabled for
fetcher
(and
writer
as well with some effort.
Another thing I didn’t find yet is how the Pager signals about errors (both expected like NoConnection and unexpected)
👍 1
m
Thanks for this feedback 👍
e
@Matthew Ramotar I am not sure if it is the best approach and if it is common case. In my app I had to implement a state machine for pagination, which uses MutableStore and Pager under the hood. From my point of view Pager and this state machine have intersection in responsibility, so maybe the best approach would be to implement a single stateful component replacing current Pager and giving the functionality of error handling and state management. If you wish, let’s discuss in DM, since here it would be too much noise.
m
I have a refactor in progress that I think addresses this. I'll push it up soon. Happy to chat more. Perhaps we could use a GitHub discussion - Then we can reference the discussion in the future if needed without creating too much noise rn
I haven’t addressed keys yet, but this separates responsibilities better (e.g., state management, error management)
e
m
Hey, sorry to be slow. Busy week. I will take a second look at this over the weekend
e
@Matthew Ramotar sure, don’t worry. I really appreciate your help, thank you!
m
Thanks for all the feedback! I pushed what I have so far. I won’t be able to return to this until tn most likely. Will need to re read this discussion and self review code. Feel free to take an early look, but it’s rough and I expect doesn’t address everything
e
Thank you, I will answer in the discussion.