# Analytics-Kotlin Migration Guide

> \[!NOTE]
>
> This guide assumes you already have an Analytics-Android Source in your Segment workspace. If you need to create a new one, refer to the [source Overview](/docs/segment/connections/sources/catalog/libraries/mobile/kotlin-android#getting-started).

If you're using a previous Segment mobile library such as Analytics-Android, follow these steps to migrate to the Analytics-Kotlin library. Analytics-Kotlin is designed to work with your Java codebase as well.

1. [Import Analytics-Kotlin](#1-import-analytics-kotlin).
2. [Upgrade your Destinations](#2-upgrade-your-destinations).
3. [Advanced: Upgrade your Middleware](#3-upgrade-middleware-to-plugins).
4. [Upgrade Notes](#4-upgrade-notes).

## 1. Import Analytics-Kotlin

### 1. Add the dependencies to your app.

In your top-level build.gradle:

```java
repositories {
    mavenCentral()
}
```

In your app module's build.gradle:

```java
dependencies {
    implementation 'com.segment.analytics.kotlin:android:<latest_version>'
}
```

You have now added Analytics-Kotlin to your project. You can remove the Analytics-Android SDK from your app.

### 2. Modify your initialized instance

## Kotlin

```java
	    val analytics = Analytics("YOUR_WRITE_KEY", context) {
        trackApplicationLifecycleEvents = true
    }
```

## Java

```java
    // Initialize an Analytics object with the Kotlin Analytics method
    Analytics androidAnalytics = AndroidAnalyticsKt.Analytics("YOUR_WRITE_KEY", context, configuration -> {
        configuration.setTrackApplicationLifecycleEvents(true);
        return Unit.INSTANCE;
        }
    );

    // Wrap the object with JavaAnalytics for Java Compatibility.
    // You can also choose not to wrap the object, but some of the Analytics methods may not be accessible.
    JavaAnalytics analytics = new JavaAnalytics(androidAnalytics);   
```

### 3. Update your import statements

You need to update the imports for Analytics-Kotlin.

Before example:

```java
  import com.segment.analytics.Analytics;
  import com.segment.analytics.Middleware;   
```

After example:

```java
  import com.segment.analytics.kotlin.core.Analytics;
  import com.segment.analytics.kotlin.android.AndroidAnalyticsKt; // Only for calling from Android
  import com.segment.analytics.kotlin.core.compat.JavaAnalytics; // Only for calling from Java
  import com.segment.analytics.kotlin.core.platform.Plugin; // Replaces Middleware
```

> \[!NOTE]
>
> Analytics-Kotlin supports running multiple instances of the analytics object, so it does not assume a singleton. However, if you're migrating from Analytics-Android and all your track calls are routed to the `Analytics.shared()` singleton, you can these calls to your new Analytics-Kotlin object.

Add this extension to your code to ensure that tracking calls written for Analytics-Android work with Analytics-Kotlin.

```java

// Application's onCreate
...

sharedAnalytics = Analytics(...)...

fun Analytics.with {
    // TODO: Finish this
        return MyApplication.sharedAnalytics; // or whatever variable name you're using
}
```

## 2. Upgrade your destinations

If your app uses Segment to route data to Destinations via Segment-cloud (i.e. Cloud-mode destinations), you can skip this step. Analytics-Kotlin treats Device-mode Destinations as [plugins](/docs/segment/connections/sources/catalog/libraries/mobile/kotlin-android/kotlin-android-plugin-architecture), and simplifies the process in integrating them into your app. Analytics-Kotlin supports these [Device-Mode Destinations](/docs/segment/connections/sources/catalog/libraries/mobile/kotlin-android/destination-plugins) with more to come.

### 1. Import the destination plugin

```java
  implementation '<owner>:<project>:<version>'
```

### 2. Add plugin to your Analytics instance:

Import the plugin:

```java
  import com.example.SomeDestinationPlugin
```

Add the pluging to the Analytics Instance:

## Java

```java
    analytics.add(new SomeDestinationPlugin());
```

## Kotlin

```java
    analytics.add(SomeDestinationPlugin())
```

Your events will now begin to flow to the added destination in Device-Mode.

## 3. Upgrade middleware to plugins

Middlewares are a powerful mechanism that can augment events collected by the Analytics Android (Classic) SDK. A middleware is a simple function that is invoked by the Segment SDK and can be used to monitor, modify, augment or reject events. Analytics-Kotlin replaces the concept of middlewares with Enrichment Plugins to give you even more control over your event data. Refer to the [Plugin Architecture Overview](/docs/segment/connections/sources/catalog/libraries/mobile/kotlin-android/kotlin-android-plugin-architecture) for more information.

### 1. Upgrading source middleware

Before example:

## Java

```java
    builder
        .useSourceMiddleware(new Middleware() {
            @Override
            public void intercept(Chain chain) {
            // Get the payload.
            BasePayload payload = chain.payload();

            // Set the device year class on the context object.
            int year = YearClass.get(getApplicationContext());
            Map<String, Object> context = new LinkedHashMap<>(payload.context());
            context.put("device_year_class", year);

            // Build our new payload.
            BasePayload newPayload = payload.toBuilder()
                .context(context)
                .build();

            // Continue with the new payload.
            chain.proceed(newPayload);
           }
        })
```

## Kotlin

```java
      builder
   .useSourceMiddleware(
     Middleware { chain ->
         // Get the payload.
         val payload = chain.payload()

         // Set the device year class on the context object.
         val year = YearClass.get(getApplicationContext())
         val context = LinkedHashMap<String, Object>(payload.context())
         context.put("device_year_class", year)

         // Build our new payload.
         val newPayload = payload.toBuilder()
               .context(context)
               .build();

         // Continue with the new payload.
         chain.proceed(newPayload)
     })
```

After example:

## Java

```java
  analytics.add(new Plugin() {
     private Analytics analytics;

     @Override
     public BaseEvent execute(@NonNull BaseEvent event) {
         // Set the device year class on the context object.
         int year = YearClass.get(getApplicationContext());
         EventTransformer.putInContext(event, "device_year_class", year);
         return event;
     }

     @Override
     public void setup(@NonNull Analytics analytics) {
         setAnalytics(analytics);
     }

     @NonNull
     @Override
     public Type getType() {
         return Plugin.Type.Enrichment;
     }

     @NonNull
     @Override
     public Analytics getAnalytics() {
         return analytics;
     }

     @Override
     public void setAnalytics(@NonNull Analytics analytics) {
         this.analytics = analytics;
     }
  });
```

## Kotlin

```java
    analytics.add(object: Plugin {
        override lateinit var analytics: Analytics
        override val type = Plugin.Type.Enrichment

        override fun execute(event: BaseEvent): BaseEvent? {
         // Set the device year class on the context object.
            val year = YearClass.get(getApplicationContext())
            event.context = updateJsonObject(event.context) {
             it["device_year_class"] = year
        }
        return event
     }
 })
```

### 2. Upgrading destination middleware

If you don't need to transform all of your Segment calls, and only want to transform the calls going to specific, device-mode destinations, use Destination plugins.

Before example:

<br />

## Java

```java
      builder
   .useDestinationMiddleware("Segment.io", new Middleware() {
       @Override
       public void intercept(Chain chain) {
         // Get the payload.
         BasePayload payload = chain.payload();

         // Set the device year class on the context object.
         int year = YearClass.get(getApplicationContext());
         Map<String, Object> context = new LinkedHashMap<>(payload.context());
         context.put("device_year_class", year);

         // Build our new payload.
         BasePayload newPayload = payload.toBuilder()
             .context(context)
             .build();

         // Continue with the new payload.
         chain.proceed(newPayload);
       }
     })
```

## Kotlin

```java
 builder
   .useDestinationMiddleware(
    "Segment.io",
    Middleware { chain ->
         // Get the payload.
         val payload = chain.payload()

         // Set the device year class on the context object.
         val year = YearClass.get(getApplicationContext())
         val context = LinkedHashMap<String, Object>(payload.context())
         context.put("device_year_class", year)

         // Build our new payload.
         val newPayload = payload.toBuilder()
               .context(context)
               .build();

         // Continue with the new payload.
         chain.proceed(newPayload)
    })
```

After example:

<br />

## Java

```java
         SegmentDestination segmentDestination = analytics.find(SegmentDestination.class);

    segmentDestination.add(new Plugin() {
        private Analytics analytics;

        @Override
        public BaseEvent execute(@NonNull BaseEvent event) {
            // Set the device year class on the context object.
            int year = YearClass.get(getApplicationContext());
             EventTransformer.putInContext(event, "device_year_class", year);
            return event;
        }

        @Override
        public void setup(@NonNull Analytics analytics) {
            setAnalytics(analytics);
        }

        @NonNull
        @Override
        public Type getType() {
            return Plugin.Type.Enrichment;
        }

        @NonNull
        @Override
        public Analytics getAnalytics() {
            return analytics;
        }

        @Override
        public void setAnalytics(@NonNull Analytics analytics) {
            this.analytics = analytics;
        }
    });
```

## Kotlin

```java
    val segmentDestination: DestinationPlugin = analytics.find(SegmentDestination::class)

    segmentDestination.add(object: Plugin {
        override lateinit var analytics: Analytics
        override val type = Plugin.Type.Enrichment

        override fun execute(event: BaseEvent): BaseEvent? {
            // Set the device year class on the context object.
            val year = YearClass.get(getApplicationContext())
            event.context = updateJsonObject(event.context) {
                it["device_year_class"] = year
            }
            return event
        }
    })
```

## 4. Upgrade notes

> \[!NOTE]
>
> To preserve the userId for users identified prior to your migration to Kotlin, you must make a one-off Identify call. This is due to a storage format change between the Analytics-Android and the Analytics-Kotlin libraries.

### 1. Changes to the configuration object

The following option was renamed in Analytics-Kotlin:

| Before                             | After                                  |
| ---------------------------------- | -------------------------------------- |
| context                            | Name changed to `application`          |
| defaultAPIHost                     | Name changed to `apiHost`              |
| defaultProjectSettings             | Name changed to `defaultSettings`      |
| experimentalUseNewLifecycleMethods | Name changed to `useLifecycleObserver` |

The following option was added in Analytics-Kotlin:

| Added option              | Details                                                                                                                                                                                             |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| autoAddSegmentDestination | The analytics client automatically adds the Segment Destination. Set this to `false`, if you want to customize the initialization of the Segment Destination, such as, add destination middleware). |

<br /> The following option was removed in Analytics-Kotlin:

| Removed option       | Details                                                                                                                                                     |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| defaultOptions       | Removed in favor of a plugin that adds the default data to the event payloads. Segment doesn't provide a plugin example since it's dependent on your needs. |
| recordScreenViews    | Removed in favor of the `AndroidRecordScreenPlugin` that provides the same functionality.                                                                   |
| trackAttributionData | This feature no longer exists.                                                                                                                              |

### 2. Properties

Properties have been replaced by JsonElement. Since Properties are essentially a `Map<String, Object>` we provide the ability to pass a map into our core tracking methods:

## Java

```java
    Map<String, Object> map = new HashMap<>();
    map.put("feature", "chat");
    Map<String, Object> miniMap = new HashMap<>();
    miniMap.put("colorChoice", "green");
    map.put("prefs", miniMap);
    analytics.track("UseFeature", map);;
```

## Kotlin

```java
    val map = HashMap<String, Any>()
    map.put("feature", "chat")
    map.put("prefs", buildJsonObject { put("colorChoice", JsonPrimitive("green")) })
    analytics.track("UseFeature", map)
```

### 3. Options support removed

Options are not supported and should be converted into plugins.

### 4. Traits are no longer attached to `analytics.track()` events automatically

To prevent sending unwanted or unnecessary PII, traits collected in `analytics.identify()` events are no longer automatically attached to `analytics.track()` events. To achieve this, you can write a `before` plugin:

```kotlin
import com.segment.analytics.kotlin.core.Analytics
import com.segment.analytics.kotlin.core.Plugin
import com.segment.analytics.kotlin.core.PluginType
import com.segment.analytics.kotlin.core.platform.Plugin
import com.segment.analytics.kotlin.core.events.RawEvent

class InjectTraits : Plugin {

    override val type: PluginType = PluginType.Enrichment
    var analytics: Analytics? = null

    override fun <T : RawEvent> execute(event: T?): T? {
        if (event?.type == "identify") {
            return event
        }

        var workingEvent = event
        val context = event?.context?.toMutableMap()

        if (context != null) {
            context["traits"] = analytics?.traits()

            workingEvent?.context = context
        }
        return workingEvent
    }
}
```

## Conclusion

Once you're up and running, you can take advantage of Analytics-Kotlin's additional features, like [Destination Filters](/docs/segment/connections/sources/catalog/libraries/mobile/kotlin-android/kotlin-android-destination-filters/), [Functions](/docs/segment/connections/functions/), and [Typewriter](/docs/segment/connections/sources/catalog/libraries/mobile/kotlin-android/kotlin-android-typewriter/) support.
