Is it possible to use the snapshot system without compose UI? Possibly even without compose compiler...
n
Is it possible to use the snapshot system without compose UI? Possibly even without compose compiler? Are there any open source examples on how to set that up?
e
Yes, I believe you just need the runtime artifact. Check out https://github.com/cashapp/molecule
g
Depending on what you need, you could even just use StateFlow from the coroutines lib. It provides nearly the same functionality, and can interop states w/ compose if you need later https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-state-flow/
I've used StateFlows to solve a few "I need a compose like state but i need to generate it from outside compose" scenarios
n
Thanks. I’m mostly interested in using states (like mutableStateOf) outside of compose, the ability to use
snapshotFlow { }
on them, do something when they change. If I remember correctly, when you try to create state outside of composition you get an exception. I think compose is doing some setup work on the
Snapshot
class. I have to check Molecule but since it also uses compose, it might be leveraging that instead of dealing with
Snapshot
directly
z
There is a whole class of things that snapshots do that flows do not. But if you just need observability, flows should work fine for most cases.
But yes you definitely can use snapshots without the rest of compose, you don’t need the compose compiler at all.
n
Isn’t there something to be configured? It’s been a while but I remember getting crashes on
mutableStateOf()
when called outside of composition
1
c
Composition is not required to use the snapshot system and snapshots are independent of composition. Composition knows about snapshots but not the other way around. If you run into a crash using
mutableStateOf()
outside of composition please submit an example in a bug report. As for an example of observation you can either use
SnapshotStateObserver
or
snapshotFlow()
or use
Snapshot.registerApplyObserver()
directly which is what these use internally. In order to see changes made in the global snapshot you need something like
GlobalSnapshotManager
which is internal to the
UI
library but it uses public API, specifically
Snapshot.sendApplyNotifications()
and
Snapshot.registerGlobalWriteObserver()
. The
GlobalSnapshotManager
schedules a call to
Snapshot.sendApplyNotifications()
whenever a write is performed in the global snapshot to ensure that the change is eventually received by the snapshot apply observer. Using snapshots and observation outside of compose requires something similar as the global snapshot needs to be "applied" periodically to be seen by an apply observer. As Zach points out, the snapshot system does not rely on the compose compiler plugin and can be used without it.
🙏 1
g
Huh, I never knew that. I always assumed that mutableStateOf was marked @Composable, not just the remember function. Never occurred to me that you could just ditch the remember because the object outside of compose isn't going to get reassigned anyway.
n
Thanks a lot Chuck, that’s going to be very useful! I guess I misremembered the crash scenario, it’s been a while since last time.
c
Depending on how long "a while" was, you might have been running into an issue we fixed before 1.0. In pre-1.0, there were some issues with usability that was fixed by creating global snapshot. Essentially, if you tried to use
mutableSnapshotOf()
outside the mutable snapshot that was created by composition then you would get an exception that you were reading or writing to a snapshot object outside a snapshot. The global snapshot made using state objects outside an explicit snapshot a valid thing to do.
z
mutableStateOf actually could not be used inside remember if it were composable. Remember’s lambda isn’t a composable function.
👍 1