# How to use CompositionLocal to implement Analytics in Jetpack Compose

> A Jetpack Compose article that teaches you how to utilize CompositionLocal to provide analytics deep into the Composition tree.

Published 24 July 2022 by Alex Styl.

Each composable function accepts data from its function parameters. As analytics are usually instantiated ahead of time of tracking, we need to pass down the analytics instance from the top-level (screen) composable down to the composable function where the tracking takes place. This means that every in between composable function will have an extra analytics function parameter, only so that they can pass it downwards to the composable that will actually use it.

This blog will show you how to utilize `CompositionLocal`s in order to gain access to the `Analytics` object of your app from any composable function without polluting your composable functions with unneeded parameters.

## What is CompositionLocals

[CompositionLocal](https://developer.android.com/jetpack/compose/compositionlocal) is a Jetpack Compose API which allow you to access data from any composable function without passing them via the function's parameters. You have probably already used a `CompositionLocal` (with a bit of syntactic sugar on top) while using theme values in your Jetpack Compose app. What the `MaterialTheme` object does is providing easy access to your application's theme from any composable function.

We can use the same technique to implement analytics in your app.

## Basic setup

For simplicity shake our analytics consists of a single tracking function. It is defined by the following interface:

```kotlin
interface Analytics {
    fun logEvent(eventName: String)
}
```

We will need two implementations of the interface. One will be used as the default implementation:

```kotlin
class LoggingAnalytics: Analytics {
    override fun logEvent(eventName: String) {
        println(eventName)
    }
}
```

while the other will use the analytics framework of your choice:

```kotlin
class FirebaseAnalytics: Analytics {
    override fun logEvent(eventName: String) {
        Firebase.analytics.logEvent(eventName)
    }
}
```

Let's create our `CompositionLocal`. As we are not expecting the Analytics object to be updated, we will use the `staticCompositionLocalOf { }` to create it.

The `CompositionLocal` needs to be created outside of any activity or object, as it needs to be visible from any composable function:

```kotlin
val LocalAnalytics = staticCompositionLocalOf<Analytics> {
    LoggingAnalytics()
}
```

In our top level composable, wrap our content with a `CompositionLocalProvider`. As at this point we are either in an `Activity` or `Fragment` we can inject the `Analytics` of our choice (using [Hilt](https://developer.android.com/training/dependency-injection/hilt-jetpack) or any other dependency injection frameworks):

```kotlin
val analytics = FirebaseAnalytics()
setContent {
    AppTheme {
        CompositionLocalProvider(LocalAnalytics provides analytics) {
            HomeScreen()
        }
    }
}
```

you can now use `Analytics` from any composable function wrapped by the `CompositionLocalProvider`:

```kotlin
@Composable
fun HomeScreen(viewModel : MyViewModel = hiltViewModel()) {
    val state =
    val analytics = LocalAnalytics.current
    Button(onClick = { analytics.logEvent("buttonClick") }) {
        Text("Click me!")
    }
}
```

you can now access analytics from any composable function deep in your Composition tree 👏

## Be aware of the downsides and code clarity

Just because you can use `CompositionLocal` to access Analytics from any composable function, it does not mean you should. The above example showcases how to use the `CompositionLocal` API so that you do not need to pass the `Analytics` instance down via parameters.

Using a `CompositionLocal` creates implicit dependencies and it can make your app harder to reason and maintain. You still need to be cautious to access analytics from the right layer of your app such as a screen level composable function instead of a single element (such as buttons).

`CompositionLocal` is a powerful API that has its usages such as the case described. In fact, Jetpack Compose makes heavy use of it. However, you need to be aware that it is easy to abuse and can mess your project's code clarify if not careful.

As you cannot be certain of the `current` value of the `CompositionLocal`, it can make your code hard to read and follow. This is because it depends on the value passed to the `CompositionLocalProvider` in an other place of your app. This makes code harder to debug as you will have to find in which place of your app the value of the `CompositionLocal` was modified.
