How to Use Device Mode Integrations

Add and use device mode integrations in the Android (Kotlin) and iOS (Swift) SDKs.

This guide shows you how to add device mode integrations in your Android (Kotlin) and iOS (Swift) apps to send events directly to third-party destinations. It covers how to:

Add a standard device mode integration

To add a standard integration, instantiate the integration plugin and add it to your Analytics instance.

Android (Kotlin)

kotlin
import com.rudderstack.sdk.kotlin.android.plugins.devicemode.IntegrationPlugin
import com.rudderstack.integration.kotlin.firebase.FirebaseIntegration

// Get reference to your integration plugin
val integrationPlugin: IntegrationPlugin = FirebaseIntegration()

// Add integration plugin to your analytics instance
analytics.add(integrationPlugin)

iOS (Swift)

swift
import RudderStackAnalytics
import RudderIntegrationFirebase

// Get reference to your integration plugin
let integrationPlugin: IntegrationPlugin = FirebaseIntegration()

// Add integration plugin to your analytics instance
analytics.add(integrationPlugin)
Always add the integrationPlugin to the Analytics instance before invoking any APIs on the integrationPlugin. Calling them earlier may lead to unexpected behavior.

Access destination instances

Device mode integrations provide the following APIs to interact with destination instances:

  • onDestinationReady(): Register a callback for destination initialization
  • getDestinationInstance(): Access the destination instance directly

See the Device Mode Integration APIs guide for the complete API specification.

onDestinationReady()

onDestinationReady() lets you perform actions only after the destination is successfully initialized. This is useful for fetching IDs or initializing resources that depend on the destination being ready.

Use the onDestinationReady() API for critical startup logic that requires the destination to be ready before proceeding.

Android (Kotlin)

kotlin
import com.rudderstack.sdk.kotlin.android.plugins.devicemode.IntegrationPlugin
import com.rudderstack.sdk.kotlin.core.internals.utils.Result
import com.rudderstack.integration.kotlin.firebase.FirebaseIntegration

val integrationPlugin: IntegrationPlugin = FirebaseIntegration()
analytics.add(integrationPlugin)

integrationPlugin.onDestinationReady { instance, result ->
    if (result is Result.Success) {
        // Destination is ready
        println("Destination is ready: $instance")
    } else {
        // Handle initialization failure
        println("Destination initialization failed: ${(result as Result.Failure).exception.message}")
    }
}

iOS (Swift)

swift
import RudderStackAnalytics
import RudderIntegrationFirebase

let integrationPlugin: IntegrationPlugin = FirebaseIntegration()
analytics.add(integrationPlugin)

integrationPlugin.onDestinationReady { instance, result in
    switch result {
    case .success:
        // Destination is ready, safe to send events
        print("Destination is ready: \(String(describing: instance))")
    case .failure(let error):
        // Handle initialization failure
        print("Destination initialization failed: \(error.localizedDescription)")
    }
}

Example: Fetch Firebase app instance ID

kotlin
import com.google.firebase.analytics.FirebaseAnalytics

integrationPlugin.onDestinationReady { instance, result ->
    if (result is Result.Success && instance is FirebaseAnalytics) {
        instance.appInstanceId.addOnSuccessListener { appInstanceId ->
            appInstanceId?.let {
                // Send to your backend for analytics correlation
                sendToBackend(it)
            }
        }
    }
}

getDestinationInstance()

getDestinationInstance() lets you directly access the destination SDK instance for advanced use cases like interacting with destination-specific APIs or features.

Use the getDestinationInstance() API for on-demand access later in the app lifecycle when you need to interact with the destination instance immediately.

Android (Kotlin)

kotlin
val destination = integrationPlugin.getDestinationInstance()
if (destination != null) {
    // Interact with the destination SDK
    println("Destination instance: $destination")
    // Cast to specific destination type if needed
    // val firebaseDestination = destination as? FirebaseAnalytics
}

iOS (Swift)

swift
if let destination = integrationPlugin.getDestinationInstance() {
    // Interact with the destination SDK
    print("Destination instance: \(destination)")
    // Cast to specific destination type if needed
    // let firebaseDestination = destination as? Analytics
}

Example 1: Set default event parameters for Firebase

kotlin
import android.os.Bundle
import com.google.firebase.analytics.FirebaseAnalytics

val firebaseAnalytics = firebaseIntegration.getDestinationInstance() as? FirebaseAnalytics

firebaseAnalytics?.let {
    val parameters = Bundle().apply {
        putString("app_variant", "premium")
        putString("global_source", "mobile_app")
    }
    // This ensures these parameters are included in every subsequent Firebase event
    it.setDefaultEventParameters(parameters)
}

Example 2: Use Braze content cards

kotlin
import com.braze.Braze

val braze = brazeIntegration.getDestinationInstance() as? Braze

braze?.let {
    // Manually request a refresh of Content Cards
    it.requestContentCardsRefresh()
    
    // Access current content cards for custom UI rendering
    val unreadCount = it.getContentCardCount()
    println("User has $unreadCount unread Content Cards.")
} ?: run {
    println("Braze integration is not yet ready or is disabled.")
}

Add custom plugins to integrations

Device mode integrations support adding custom plugins to modify or enhance event data before sending it to a specific destination. This enables filtering, transformation, or enrichment of events for specific destinations.

With this feature, you can:

  • Add different plugins to different device mode integrations (for example, Firebase, Amplitude, Braze)
  • Apply unique transformations, filtering, or enrichment logic for each destination
  • Handle destination-specific requirements without affecting other integrations

See the Plugin Architecture guide for more information on custom plugins.

Android (Kotlin)

kotlin
import com.rudderstack.sdk.kotlin.android.plugins.devicemode.IntegrationPlugin
import com.rudderstack.sdk.kotlin.core.internals.plugins.Plugin
import com.rudderstack.sdk.kotlin.core.internals.plugins.EventPlugin
import com.rudderstack.sdk.kotlin.core.internals.models.Event
import com.rudderstack.sdk.kotlin.core.internals.models.TrackEvent
import com.rudderstack.integration.kotlin.firebase.FirebaseIntegration

// Step 1: Create a custom plugin
class MyCustomPlugin : EventPlugin {
    override val pluginType = Plugin.PluginType.PreProcess
    
    override suspend fun intercept(event: Event): Event? {
        // Filter out debug events
        return if (event is TrackEvent && event.event.startsWith("Debug_")) {
            null // This will drop the event
        } else {
            event // Pass through all other events
        }
    }
}

// Step 2: Add integration to analytics
val integrationPlugin: IntegrationPlugin = FirebaseIntegration()
analytics.add(integrationPlugin)

// Step 3: Add custom plugin to integration
val customPlugin = MyCustomPlugin()
integrationPlugin.add(customPlugin)

iOS (Swift)

swift
import RudderStackAnalytics
import RudderIntegrationFirebase

// Step 1: Create a custom plugin
class MyCustomPlugin: EventPlugin {
    var pluginType: PluginType = .preProcess
    var analytics: Analytics?
    
    func intercept(event: any Event) -> (any Event)? {
        // Filter out debug events
        if let trackEvent = event as? TrackEvent,
           let eventName = trackEvent.event,
           eventName.hasPrefix("Debug_") {
            return nil // This will drop the event
        }
        return event // Pass through all other events
    }
}

// Step 2: Add integration to analytics
let integrationPlugin: IntegrationPlugin = FirebaseIntegration()
analytics.add(plugin: integrationPlugin)

// Step 3: Add custom plugin to integration
let customPlugin = MyCustomPlugin()
integrationPlugin.add(plugin: customPlugin)

Troubleshooting

IssueSolution
Integration not initializing
  • Ensure you have added the integration plugin to the Analytics instance before calling any integration APIs
  • Check that the destination is enabled in your RudderStack dashboard
  • Verify that required dependencies are properly installed
Destination instance is null
  • The destination may not be initialized yet. Use onDestinationReady() to wait for initialization
  • Check if the integration failed to initialize by handling the failure case in onDestinationReady()
  • Ensure the integration is properly configured in your RudderStack dashboard
Events not reaching destination
  • Verify the integration is added to your Analytics instance
  • Check that events are being tracked using the standard RudderStack APIs (track, identify, etc.)
  • Review destination-specific requirements in the destination’s documentation

See more

Questions? Let's figure it out together.

Join the RudderStack Slack community to connect with other users, customers, and the RudderStack team — or reach out for direct support.