I’ve seen this asked &amp; answered in the <#CJLTW...
# compose
t
I’ve seen this asked & answered in the #compose channel plenty of times (myself included), but I haven’t quite got my head around a good implementation. Let’s say I’m using route-based navigation, and I want to pass some complex data between routes. The suggestion is to stash that object somewhere, and then pass an identifier via navigation args, then use that identifier to retrieve the object. I’m fine with this when my data has an identifier, and exists in a repository somewhere. But, let’s say I have two Composable screens, StoreList and StoreDetails. And, let’s say each screen has its own ViewModel,
StoreListViewModel
and
StoreDetailsViewModel
StoreList lets me retrieve a list of
Store(val name: String, val lat: Double, val long: Double)
And StoreDetails renders some UI for a particular
Store
. There’s no real ‘identifier’ for a Store, though I could use the hashcode or something. And there’s currently no cache for Stores. Where do I stash this Store object? I don’t particularly want to share the
StoreList's
ViewModel
with the
StoreDetails
screen, as it contains properties and functions that don’t relate to StoreDetails. Do I create a 3rd ViewModel for sharing between the two screens, and set the Store on that VM?
Or, maybe I store the most recent list of stores in a Singleton repository, and pass the Store hashcode, as I mentioned. But - this seems a bit wasteful in terms of memory
c
I'd put it in a repository, and if you are worried about memory consumption, scope it narrower than singleton (i.e. create your own scope, I assume you are using som DI framework that allows that)
t
I’m not sure how you’d narrow the scope of a repository to the lifecycle of two screens - it feels like this is beyond the concerns of the repository
I think a shared ViewModel is probably the right tool for this, it just seems a bit ugly
c
yeah shared viewmodel is the easier approach but makes it more complex to test potentially (if you have to create/mock dependencies that are not used by the details screen)... but hey, who's testing anyway 😆
i
Or just pass the object, if it really only exists at the UI layer: https://developer.android.com/guide/navigation/navigation-kotlin-dsl#custom-types
A better way to start the conversation is figuring out how StoreList is going to recreate its list after process death and recreation. Your objects have been wiped from memory entirely, which means that ViewModel needs to be talking to the actual source of truth (i.e., your repository layer - note that doesn't mean "save to disk" or "database", it just means the layer responsible for getting your data)
Once you have a repository layer anyways (since a ViewModel shouldn't be doing network calls or disk access directly), that's also the layer that can do in memory caching
Keep in mind that every list already has a way to uniquely define each item by the index in the list
So at that point you already have everything you need - a repository both destinations can talk to that is the single source of truth for Store objects and an index into the list that you can pass to the details screen to define you element
And if you start persisting those objects into a database, the rest of the layers don't have to care
(of course, you probably will add a unique ID to each element if you do add it to a database at some point, which then makes the index based approach unnecessary)
t
I think what you're saying Ian, is that in order to survive process death, the repository is going to need a database or disk persistence layer anyway, and once it has that, it may as well have an in-memory cache as well, and now it can serve multiple screens as required..?
i
More that if the repository changes from an in memory cache (and requerying your server or whatever when it finds it doesn't have any data loaded) to a persisted database, the rest of your layers don't need to care - your repository, in either case, is the single source of truth for all screens to retrieve those objects
t
OK. So, if the data is somewhat transient (let’s say, held for the duration of a couple of screens and never used again) - is it just premature optimisation to worry about how long this data lives in the cache?
This was my initial hesitation about storing this stuff in a repository / in-memory - that it’s maybe not the most scalable way - if you had a huge dataset or something. Since the repository is not (easily) scoped to the lifecycle of just the screens that are interested in it
OK, just reading the Google architecture guide again, and it does mention that scoping the repository to the nav graph is how you’d manage this - as Carl was saying