https://kotlinlang.org logo
#compose
Title
# compose
a

Alex Crafford

10/02/2023, 10:31 PM
Hey all, I’m looking for a way to traverse the layout tree at runtime. Something like findById in test tools, does such a thing exist?
z

Zach Klippenstein (he/him) [MOD]

10/02/2023, 10:32 PM
Generally it’s best to use
Modifier.testTag
for that sort of thing
But there’s no way to explicitly walk the subtree of a node.
a

Alex Crafford

10/02/2023, 10:33 PM
It’s more complex unfortunately, I’m needing to create custom “test tags” and then reference them at runtime. We have custom compose components that we want to tag for analytics
How does the layout inspector do this?
Or accessibility for that matter? They have to have some way to traverse the subtree
z

Zach Klippenstein (he/him) [MOD]

10/02/2023, 10:35 PM
Layout inspector uses APIs like`CompositionData`’s
asTree
and
mapTree
methods, you could try that but those are only intended for tooling so i’m not sure how reliable they’ll be for tests
Accessibility info is generated from semantics using internal APIs
a

Alex Crafford

10/02/2023, 10:36 PM
Oh thats what I’m looking for I think, we have the tests worked out, its scanning the tree at runtime for our custom components that I’m stuck on
z

Zach Klippenstein (he/him) [MOD]

10/02/2023, 10:37 PM
so at runtime the
CompositionData
stuff might not work at all? I think some of that data is only generated in debug mode, and even then only when a certain flag is set, and it has a huge performance cost
i think this is very much the wrong approach. You probably want to use something like ModifierLocals or the new TraversableNode API
although there might be other approaches – hard to say without knowing more about your system
a

Alex Crafford

10/02/2023, 10:41 PM
It very well may be the wrong approach I’m newer to this. Im more accustomed to view binding where you reference the id lol
z

Zach Klippenstein (he/him) [MOD]

10/02/2023, 10:42 PM
1. How do you generate tags? 2. How do you actually need to consume the tags? E.g. do you have some logic that runs a callback when a tagged view becomes visible, or something else?
a

Alex Crafford

10/02/2023, 10:51 PM
Right now, tags will be strings in our composable functions within our library. Not sure which approach is best here but leaning towards a modifier extension function. Needing to pass Library project name, library version and component name all as strings. When that library is used in an application (and analytic calls are triggered) we need to check the screen for our custom components by capturing those tags. We are the first project to create a pure compose library and app at my company
Trying to capture usage and adoption metrics
z

Zach Klippenstein (he/him) [MOD]

10/02/2023, 10:56 PM
Ok, so you have some external polling mechanism that asks “what tagged views are on screen right now” – correct?
a

Alex Crafford

10/02/2023, 10:59 PM
Not yet, but that’s the idea.
It’s Adobe Analytics
z

Zach Klippenstein (he/him) [MOD]

10/02/2023, 11:06 PM
So what I would probably do is build a modifier that registers for that signal and uses its LayoutCoordinates to lazily figure out if it’s visible or not and report itself.
Something like this
🌟 1
a

Alex Crafford

10/02/2023, 11:22 PM
Oh ok, then apply that modifier to each component I want to track
z

Zach Klippenstein (he/him) [MOD]

10/02/2023, 11:23 PM
exactly
And wrap your whole app in something like this to provide the `AnalyticsController`:
Copy code
@Composable fun App(analyticsController: AnalyticsController) {
  CompositionLocalProvider(LocalAnalyticsController provides analyticsController) {
    RestOfApp()
  }
}
a

Alex Crafford

10/02/2023, 11:25 PM
Can I apply that to the component at the library level or would I need to do that in the application that consumes the lib?
z

Zach Klippenstein (he/him) [MOD]

10/02/2023, 11:28 PM
Does your library already have some root composable wrapper it expects the app to use? If so, you could put it in there. Otherwise, I would either: 1. Expose
LocalAnalyticsController
and
AnalyticsController
from your library and ask the consuming app to provide an instance however it likes, or 2. Make
LocalAnalyticsController
private, expose
AnalyticsController
, and expose a
@Composable fun AnalyticsControllerProvider(controller: AnalyticsController)
function that your consuming app must use. (1) is simpler, and also allows your library consumers to consume the
LocalAnalyticsController
which may be good (for flexibility) or bad (if you want to control how it’s used).
a

Alex Crafford

10/02/2023, 11:31 PM
Beautiful, I’ll give it a go! You mind if I DM you if I get too deep?
z

Zach Klippenstein (he/him) [MOD]

10/02/2023, 11:31 PM
go ahead
2 Views