Create custom device mode integrations for unsupported destinations in the Android (Kotlin) and iOS (Swift) SDKs.
9 minute read
This guide shows you how to create custom integration plugins to send events to third-party destinations that are not officially supported by RudderStack.
Overview
Custom integrations let you wrap any third-party SDK and send event data directly to destinations that are not officially supported. You can extend RudderStack’s capabilities by integrating any third-party SDK into your mobile apps.
Key differences from standard integrations
Aspect
Standard integrations
Custom integrations
Maintenance
Maintained by RudderStack
Maintained by you
Configuration
Uses dashboard configuration
Uses hardcoded or custom configuration
Source configuration
Receives destination configuration from the RudderStack dashboard
Receives an empty configuration object
Updates
Automatic (via RudderStack releases)
Manual (updates made by the developer)
Implementation guide
The following example creates an integration plugin called MyCustomIntegrationPlugin that wraps a destination called CustomDestinationSdk. All APIs are implemented for demonstration purposes. Apart from the mandatory APIs (key, create, and getDestinationInstance), you can implement other APIs as needed.
Android (Kotlin)
This section shows you how to create a custom integration plugin for Android (Kotlin).
1. Create the integration plugin
importcom.rudderstack.sdk.kotlin.android.plugins.devicemode.IntegrationPluginimportcom.rudderstack.sdk.kotlin.core.internals.logger.LoggerAnalyticsimportcom.rudderstack.sdk.kotlin.core.internals.models.*importkotlinx.serialization.json.JsonObject/**
* Custom integration plugin for CustomDestination
*/classMyCustomIntegrationPlugin:IntegrationPlugin(){/**
* The instance of the destination SDK which is wrapped with this integration plugin.
* Here, `CustomDestinationSdk` is a sample third party SDK class which you can wrap with an integration plugin.
*/privatevardestinationSdk:CustomDestinationSdk?=null// Unique identifier for your integration
overridevalkey:String="CustomDestination"/**
* Initialize your destination SDK
* Note: destinationConfig will be empty for custom integrations
*/overridefuncreate(destinationConfig:JsonObject){// Initialize with your custom configuration
valapiKey="your-api-key"valserverUrl="https://api.yourdestination.com"destinationSdk=CustomDestinationSdk.initialize(apiKey,serverUrl)LoggerAnalytics.debug("MyCustomDestination: SDK initialized")}/**
* Return the destination SDK instance
*/overridefungetDestinationInstance():Any?{returndestinationSdk}/**
* Implement event methods to forward events to your destination SDK.
* Call the appropriate methods on your destination SDK instance.
*/overridefuntrack(payload:TrackEvent){destinationSdk?.trackEvent(eventName=payload.event,properties=payload.properties.toMap())}overridefunidentify(payload:IdentifyEvent){destinationSdk?.identifyUser(userId=payload.userId,traits=payload.context["traits"]?.jsonObject?.toMap()?:emptyMap())}overridefunscreen(payload:ScreenEvent){destinationSdk?.trackScreen(screenName=payload.screenName,properties=payload.properties.toMap())}overridefungroup(payload:GroupEvent){destinationSdk?.setGroup(groupId=payload.groupId,traits=payload.traits.toMap())}overridefunalias(payload:AliasEvent){destinationSdk?.aliasUser(newUserId=payload.userId,previousUserId=payload.previousId)}/**
* Optional: Implement flush method if supported by your destination SDK
*/overridefunflush(){destinationSdk?.flush()LoggerAnalytics.debug("MyCustomDestination: Flushed events")}/**
* Optional: Implement reset method if supported by your destination SDK
*/overridefunreset(){destinationSdk?.reset()LoggerAnalytics.debug("MyCustomDestination: Reset user data")}}
importandroidx.annotation.NonNull;importandroidx.annotation.Nullable;importcom.rudderstack.sdk.kotlin.android.plugins.devicemode.IntegrationPlugin;importcom.rudderstack.sdk.kotlin.core.internals.logger.LoggerAnalytics;importcom.rudderstack.sdk.kotlin.core.internals.models.*;importjava.util.HashMap;importjava.util.Map;importkotlinx.serialization.json.JsonObject;importkotlinx.serialization.json.JsonElement;/**
* Custom integration plugin for CustomDestination
*/publicclassMyCustomIntegrationPluginextendsIntegrationPlugin{/**
* The instance of the destination SDK which is wrapped with this integration plugin.
* Here, `CustomDestinationSdk` is a sample third party SDK class which you can wrap with an integration plugin.
*/privateCustomDestinationSdkdestinationSdk;// Unique identifier for your integration
privatestaticfinalStringKEY="CustomDestination";@NonNull@OverridepublicStringgetKey(){returnKEY;}/**
* Initialize your destination SDK
* Note: destinationConfig will be empty for custom integrations
*/@Overridepublicvoidcreate(@NonNullJsonObjectdestinationConfig){// Initialize with your custom configuration
StringapiKey="your-api-key";StringserverUrl="https://api.yourdestination.com";destinationSdk=CustomDestinationSdk.initialize(apiKey,serverUrl);LoggerAnalytics.INSTANCE.debug("MyCustomDestination: SDK initialized");}/**
* Return the destination SDK instance
*/@Nullable@OverridepublicObjectgetDestinationInstance(){returndestinationSdk;}/**
* Implement event methods to forward events to your destination SDK.
* Call the appropriate methods on your destination SDK instance.
*/@Overridepublicvoidtrack(@NonNullTrackEventpayload){if(destinationSdk!=null){Map<String,Object>properties=convertJsonObjectToMap(payload.getProperties());destinationSdk.trackEvent(payload.getEvent(),properties);}}@Overridepublicvoididentify(@NonNullIdentifyEventpayload){if(destinationSdk!=null){Map<String,Object>traits=newHashMap<>();JsonElementtraitsElement=payload.getContext().get("traits");if(traitsElement!=null&&traitsElement.isJsonObject()){traits=convertJsonObjectToMap(traitsElement.getAsJsonObject());}destinationSdk.identifyUser(payload.getUserId(),traits);}}@Overridepublicvoidscreen(@NonNullScreenEventpayload){if(destinationSdk!=null){Map<String,Object>properties=convertJsonObjectToMap(payload.getProperties());destinationSdk.trackScreen(payload.getScreenName(),properties);}}@Overridepublicvoidgroup(@NonNullGroupEventpayload){if(destinationSdk!=null){Map<String,Object>traits=payload.getTraits()!=null?convertJsonObjectToMap(payload.getTraits()):newHashMap<>();destinationSdk.setGroup(payload.getGroupId(),traits);}}@Overridepublicvoidalias(@NonNullAliasEventpayload){if(destinationSdk!=null){destinationSdk.aliasUser(payload.getUserId(),payload.getPreviousId());}}/**
* Optional: Implement flush method if supported by your destination SDK
*/@Overridepublicvoidflush(){if(destinationSdk!=null){destinationSdk.flush();LoggerAnalytics.INSTANCE.debug("MyCustomDestination: Flushed events");}}/**
* Optional: Implement reset method if supported by your destination SDK
*/@Overridepublicvoidreset(){if(destinationSdk!=null){destinationSdk.reset();LoggerAnalytics.INSTANCE.debug("MyCustomDestination: Reset user data");}}/**
* Utility method to convert JsonObject to Map for Java compatibility
*/privateMap<String,Object>convertJsonObjectToMap(JsonObjectjsonObject){Map<String,Object>map=newHashMap<>();// Implementation depends on your JsonObject structure
// This is a simplified example - you may need more robust conversion
// You would typically iterate through the JsonObject and convert each element
returnmap;}}
2. Add the plugin to Analytics
importcom.rudderstack.sdk.kotlin.android.Analyticsimportcom.rudderstack.sdk.kotlin.android.ConfigurationclassMyApp:Application(){lateinitvaranalytics:AnalyticsoverridefunonCreate(){super.onCreate()// Initialize Analytics
analytics=Analytics(configuration=Configuration(writeKey="your-write-key",application=this,dataPlaneUrl="your-data-plane-url"))// Create and add your custom integration
valcustomIntegration=MyCustomIntegrationPlugin()// Add the integration to analytics
analytics.add(customIntegration)}}
importandroid.app.Application;importcom.rudderstack.sdk.kotlin.android.Configuration;importcom.rudderstack.sdk.kotlin.android.javacompat.ConfigurationBuilder;importcom.rudderstack.sdk.kotlin.android.javacompat.JavaAnalytics;publicclassMyAppextendsApplication{privateJavaAnalyticsanalytics;@OverridepublicvoidonCreate(){super.onCreate();// Initialize Analytics
Configurationconfig=newConfigurationBuilder(this,"your-write-key","your-data-plane-url").build();analytics=newJavaAnalytics(config);// Create and add your custom integration
MyCustomIntegrationPlugincustomIntegration=newMyCustomIntegrationPlugin();// Add the integration to analytics
analytics.add(customIntegration);}}
iOS (Swift)
This section shows you how to create a custom integration plugin for iOS (Swift).
1. Create the integration plugin
importFoundationimportRudderStackAnalytics/**
* Custom integration plugin for MyCustomDestination
*/classMyCustomIntegrationPlugin:IntegrationPlugin{varpluginType:PluginType=.terminalvaranalytics:Analytics?varkey:String="MyCustomDestination"/**
* The instance of the destination SDK which is wrapped with this integration plugin.
* Here, `CustomDestinationSdk` is a sample third party SDK class which you can wrap with an integration plugin.
*/privatevardestinationSdk:MyCustomDestinationSdk?funcgetDestinationInstance()->Any?{returndestinationSdk}/**
* Initialize your destination SDK
* Note: destinationConfig will be empty for custom integrations
*/funccreate(destinationConfig:[String:Any])throws{// Initialize with your custom configurationletapiKey="your-api-key"letserverUrl="https://api.yourdestination.com"destinationSdk=MyCustomDestinationSdk.initialize(apiKey:apiKey,serverUrl:serverUrl)print("MyCustomDestination: SDK initialized")}/**
* Implement event methods to forward events to your destination SDK.
* Call the appropriate methods on your destination SDK instance.
*/funcidentify(payload:IdentifyEvent){destinationSdk?.identifyUser(userId:payload.userId??"",traits:payload.traits??[:])}functrack(payload:TrackEvent){destinationSdk?.trackEvent(eventName:payload.event??"",properties:payload.properties??[:])}funcscreen(payload:ScreenEvent){destinationSdk?.trackScreen(screenName:payload.name??"",properties:payload.properties??[:])}funcgroup(payload:GroupEvent){destinationSdk?.setGroup(groupId:payload.groupId??"",traits:payload.traits??[:])}funcalias(payload:AliasEvent){destinationSdk?.aliasUser(newUserId:payload.userId??"",previousUserId:payload.previousId??"")}/**
* Optional: Implement flush method if supported by your destination SDK
*/funcflush(){destinationSdk?.flush()print("MyCustomDestination: Flushed events")}/**
* Optional: Implement reset method if supported by your destination SDK
*/funcreset(){destinationSdk?.reset()print("MyCustomDestination: Reset user data")}}
#import <Foundation/Foundation.h>
@importRudderStackAnalytics;// MyCustomIntegrationPlugin.h
@interfaceMyCustomIntegrationPlugin : NSObject<RSSIntegrationPlugin>@end// MyCustomIntegrationPlugin.m
@interfaceMyCustomIntegrationPlugin()@propertySampleDestination*destination;@end@implementationMyCustomIntegrationPlugin@synthesizepluginType;@synthesizekey;-(NSString*)key{return@"MyCustomDestination";}-(RSSPluginType)pluginType{returnRSSPluginTypeTerminal;}/**
* The instance of the destination SDK which is wrapped with this integration plugin.
*/-(id_Nullable)getDestinationInstance{returnself.destination;}/**
* Initialize your destination SDK
* Note: destinationConfig will be empty for custom integrations
*/-(BOOL)createWithDestinationConfig:(NSDictionary<NSString*,id>*_Nonnull)destinationConfigerror:(NSError*_Nullable__autoreleasing*_Nullable)error{if(_destination==nil){_destination=[SampleDestinationcreateWithApiKey:@"MyCustomDeviceDestination"];}returnYES;}/**
* Implement event methods to forward events to your destination SDK.
* Call the appropriate methods on your destination SDK instance.
*/-(void)identify:(RSSIdentifyEvent*)payload{[_destinationidentifyUser:payload.userId?:@""traits:payload.traits?:@{}];}-(void)track:(RSSTrackEvent*)payload{[_destinationtrackEvent:payload.eventNameproperties:payload.properties?:@{}];}-(void)screen:(RSSScreenEvent*)payload{[_destinationscreen:payload.screenNameproperties:payload.properties?:@{}];}-(void)group:(RSSGroupEvent*)payload{[_destinationgroup:payload.groupIdtraits:payload.traits?:@{}];}-(void)alias:(RSSAliasEvent*)payload{[_destinationaliasUser:payload.userId?:@""previousId:payload.previousId];}/**
* Optional: Implement flush method if supported by your destination SDK
*/-(void)flush{[_destinationflush];}/**
* Optional: Implement reset method if supported by your destination SDK
*/-(void)reset{[_destinationreset];}@end
2. Add the plugin to Analytics
importUIKitimportRudderStackAnalyticsclassAppDelegate:UIResponder,UIApplicationDelegate{varanalytics:Analytics!funcapplication(_application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[UIApplication.LaunchOptionsKey:Any]?)->Bool{// Initialize Analyticsletconfig=Configuration(writeKey:"your-write-key",dataPlaneUrl:"your-data-plane-url")analytics=Analytics(configuration:config)// Create and add your custom integrationletcustomIntegration=MyCustomIntegrationPlugin()// Add the integration to analyticsanalytics.add(plugin:customIntegration)returntrue}}
#import <UIKit/UIKit.h>
@importRudderStackAnalytics;// AppDelegate.m
@interfaceAppDelegate()@propertyRSSAnalytics*analytics;@end@implementationAppDelegate-(BOOL)application:(UIApplication*)applicationdidFinishLaunchingWithOptions:(NSDictionary*)launchOptions{// Initialize Analytics
NSString*writeKey=@"your-write-key";NSString*dataPlaneUrl=@"your-data-plane-url";RSSConfigurationBuilder*builder=[[RSSConfigurationBuilderalloc]initWithWriteKey:writeKeydataPlaneUrl:dataPlaneUrl];self.analytics=[[RSSAnalyticsalloc]initWithConfiguration:[builderbuild]];// Create and add your custom integration
MyCustomIntegrationPlugin*customIntegration=[MyCustomIntegrationPluginnew];// Add the integration to analytics
[self.analyticsaddPlugin:customIntegration];returnYES;}@end
Important consideration
The pluginType for a custom integration plugin in iOS (Swift) SDK should always be .terminal.
Required and optional methods
This section lists the required and optional methods for custom integration plugins.
Required methods
Method
Description
key
Unique identifier for your integration
create(destinationConfig)
Initializes your destination SDK
getDestinationInstance()
Returns the destination SDK instance
Optional methods
Method
Description
track(payload)
Forwards track events to your destination SDK
identify(payload)
Forwards identify events to your destination SDK
screen(payload)
Forwards screen events to your destination SDK
group(payload)
Forwards group events to your destination SDK
alias(payload)
Forwards alias events to your destination SDK
flush()
Flushes events to your destination SDK (if supported)
reset()
Resets user data to your destination SDK (if supported)
This site uses cookies to improve your experience while you navigate through the website. Out of
these
cookies, the cookies that are categorized as necessary are stored on your browser as they are as
essential
for the working of basic functionalities of the website. We also use third-party cookies that
help
us
analyze and understand how you use this website. These cookies will be stored in your browser
only
with
your
consent. You also have the option to opt-out of these cookies. But opting out of some of these
cookies
may
have an effect on your browsing experience.
Necessary
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. This
category only includes cookies that ensures basic functionalities and security
features of the website. These cookies do not store any personal information.
This site uses cookies to improve your experience. If you want to
learn more about cookies and why we use them, visit our cookie
policy. We'll assume you're ok with this, but you can opt-out if you wish Cookie Settings.