Integrations
These are recipes, not packages. Responsly does not ship plugins or destinations for analytics tools. The few lines below are the whole integration, and they work with any tool that has a track call.
There are two directions:
- Out: send survey events (shown, completed, dismissed) to your analytics tool.
- In: let the events and screens you already track start surveys.
Forward survey events to your analytics
Section titled “Forward survey events to your analytics”Register a listener and pass each event on. The examples use snake_case event names, which every tool accepts. Firebase Analytics allows only letters, digits and underscores in event names.
import ResponslySDK
final class SurveyAnalytics: ResponslyListener { func surveyShown(_ event: ResponslySurveyShownEvent) { send("survey_shown", ["survey_id": event.survey.id, "survey_name": event.survey.name]) }
func surveyCompleted(_ event: ResponslySurveyCompletedEvent) { send("survey_completed", ["survey_id": event.surveyId, "response_id": event.responseId]) }
func surveyDismissed(_ event: ResponslySurveyDismissedEvent) { send("survey_dismissed", ["survey_id": event.surveyId]) }
private func send(_ name: String, _ properties: [String: Any]) { // Keep the line for your tool: analytics.track(name: name, properties: properties) // Segment // amplitude.track(eventType: name, eventProperties: properties) // Amplitude // Mixpanel.mainInstance().track(event: name, properties: properties as? Properties) // Mixpanel // Analytics.logEvent(name, parameters: properties) // Firebase Analytics }}
// At app start, after initialize:let surveyAnalyticsToken = Responsly.shared.addListener(SurveyAnalytics())import com.responsly.sdk.*
class SurveyAnalytics : ResponslyListener { override fun onSurveyShown(event: SurveyShownEvent) { send("survey_shown", mapOf("survey_id" to event.survey.id, "survey_name" to event.survey.name)) }
override fun onSurveyCompleted(event: SurveyCompletedEvent) { send("survey_completed", mapOf("survey_id" to event.surveyId, "response_id" to event.responseId)) }
override fun onSurveyDismissed(event: SurveyDismissedEvent) { send("survey_dismissed", mapOf("survey_id" to event.surveyId)) }
private fun send(name: String, properties: Map<String, String>) { // Keep the line for your tool: analytics.track(name, buildJsonObject { properties.forEach { (key, value) -> put(key, value) } }) // Segment // amplitude.track(name, properties) // Amplitude // mixpanel.trackMap(name, properties) // Mixpanel // Firebase.analytics.logEvent(name) { properties.forEach { (key, value) -> param(key, value) } } // Firebase Analytics }}
// In Application.onCreate, after initialize:Responsly.addListener(SurveyAnalytics())import Responsly from '@responsly/react-native-sdk'
function send(name: string, properties: Record<string, string>) { // Keep the line for your tool: segmentClient.track(name, properties) // Segment // amplitude.track(name, properties) // Amplitude // mixpanel.track(name, properties) // Mixpanel // analytics().logEvent(name, properties) // Firebase Analytics (@react-native-firebase/analytics)}
Responsly.addListener({ onSurveyShown: ({ survey }) => send('survey_shown', { survey_id: survey.id, survey_name: survey.name }), onSurveyCompleted: ({ surveyId, responseId }) => send('survey_completed', { survey_id: surveyId, response_id: responseId }), onSurveyDismissed: ({ surveyId }) => send('survey_dismissed', { survey_id: surveyId }),})import 'package:responsly_sdk/responsly_sdk.dart';
void send(String name, Map<String, Object> properties) { // Keep the line for your tool: analytics.track(name, properties: properties); // Segment // amplitude.track(BaseEvent(name, eventProperties: properties)); // Amplitude // mixpanel.track(name, properties: properties); // Mixpanel // FirebaseAnalytics.instance.logEvent(name: name, parameters: properties); // Firebase Analytics}
final surveyAnalytics = Responsly.events.listen((event) { switch (event) { case SurveyShown(:final survey): send('survey_shown', {'survey_id': survey.id, 'survey_name': survey.name}); case SurveyCompleted(:final surveyId, :final responseId): send('survey_completed', {'survey_id': surveyId, 'response_id': responseId}); case SurveyDismissed(:final surveyId): send('survey_dismissed', {'survey_id': surveyId}); default: break; }});using System.Collections.Generic;using Responsly;using UnityEngine;
public static class SurveyAnalytics{ [RuntimeInitializeOnLoadMethod] private static void Init() { ResponslySDK.SurveyShown += e => Send("survey_shown", new Dictionary<string, object> { { "survey_id", e.Survey.Id }, { "survey_name", e.Survey.Name }, });
ResponslySDK.SurveyCompleted += e => Send("survey_completed", new Dictionary<string, object> { { "survey_id", e.SurveyId }, { "response_id", e.ResponseId }, });
ResponslySDK.SurveyDismissed += e => Send("survey_dismissed", new Dictionary<string, object> { { "survey_id", e.SurveyId }, }); }
private static void Send(string name, Dictionary<string, object> properties) { // Call your analytics SDK here, for example: // Amplitude.getInstance().logEvent(name, properties); // Amplitude // FirebaseAnalytics.LogEvent(name, new Parameter("survey_id", (string)properties["survey_id"])); // Firebase Analytics }}What to forward:
| Event | Forward it | Why |
|---|---|---|
surveyShown | yes | The top of the funnel. Add trigger.type and trigger.name as properties if you compare triggers. |
surveyCompleted | yes | responseId lets you find the response in Responsly later. |
surveyDismissed | yes | Completed divided by shown is your response rate. |
questionAnswered | with care | Answers are personal data. See the note below. |
surveyNotShown | no | It fires for every failed targeting check and would flood your analytics. Use it for debugging. |
surveyError | to your error reporting | Rare, and useful when it happens. |
Drive the SDK from your analytics calls
Section titled “Drive the SDK from your analytics calls”Most apps already have one helper that every track, screen and identify call goes through. Add the Responsly call there. From then on, any event the app tracks can be used as a survey trigger in the Mobile app tab, with no further app changes.
import ResponslySDK
enum AppAnalytics { static func track(_ event: String, properties: [String: Any] = [:]) { analytics.track(name: event, properties: properties) // your analytics SDK Responsly.shared.track(event, properties: ResponslyValue.properties(from: properties)) }
static func screen(_ name: String) { analytics.screen(title: name) // your analytics SDK Responsly.shared.screen(name) }
static func identify(userId: String, traits: [String: Any] = [:]) { analytics.identify(userId: userId, traits: traits) // your analytics SDK Responsly.shared.identify(userId: userId, traits: ResponslyValue.traits(from: traits)) }
static func reset() { analytics.reset() // your analytics SDK Responsly.shared.reset() }}ResponslyValue.properties(from:) and ResponslyValue.traits(from:) convert a loosely typed dictionary. They keep strings, numbers, booleans and dates and skip everything else.
import com.responsly.sdk.*
object AppAnalytics { fun track(event: String, properties: Map<String, Any> = emptyMap()) { amplitude.track(event, properties) // your analytics SDK Responsly.track(event, properties) }
fun screen(name: String) { // your analytics SDK's screen call Responsly.screen(name) }
fun identify(userId: String, traits: Map<String, Any> = emptyMap()) { amplitude.setUserId(userId) // your analytics SDK Responsly.identify(userId, traits) }
fun reset() { amplitude.reset() // your analytics SDK Responsly.reset() }}import Responsly from '@responsly/react-native-sdk'
type Values = Record<string, string | number | boolean>
export const appAnalytics = { track(event: string, properties: Values = {}) { segmentClient.track(event, properties) // your analytics SDK Responsly.track(event, properties) },
screen(name: string) { segmentClient.screen(name) // your analytics SDK Responsly.screen(name) },
identify(userId: string, traits: Values = {}) { segmentClient.identify(userId, traits) // your analytics SDK Responsly.identify(userId, traits) },
reset() { segmentClient.reset() // your analytics SDK Responsly.reset() },}import 'package:firebase_analytics/firebase_analytics.dart';import 'package:responsly_sdk/responsly_sdk.dart';
class AppAnalytics { static final _firebase = FirebaseAnalytics.instance;
static void track(String event, [Map<String, Object> properties = const {}]) { _firebase.logEvent(name: event, parameters: properties); // your analytics SDK Responsly.track(event, properties: properties); }
static void screen(String name) { _firebase.logScreenView(screenName: name); // your analytics SDK Responsly.screen(name); }
static void identify(String userId, [Map<String, Object> traits = const {}]) { _firebase.setUserId(id: userId); // your analytics SDK Responsly.identify(userId, traits: traits); }
static void reset() { _firebase.setUserId(id: null); // your analytics SDK Responsly.reset(); }}using System.Collections.Generic;using Responsly;
public static class AppAnalytics{ public static void Track(string name, Dictionary<string, object> properties) { // your analytics SDK's track call ResponslySDK.Track(name, properties); }
public static void Screen(string name) { // your analytics SDK's screen call ResponslySDK.Screen(name); }
public static void Identify(string userId, Dictionary<string, object> traits) { // your analytics SDK's identify call ResponslySDK.Identify(userId, traits); }
public static void Reset() { // your analytics SDK's reset call ResponslySDK.Reset(); }}Things to keep in mind:
- Nothing is uploaded.
trackandscreenonly count events and check triggers on the device, so forwarding every event costs no network traffic. See Privacy & data. - Use stable event names. The SDK keeps a count per event name. Names that contain IDs or other variable parts (
viewed_product_8841) create a new counter each time. Put the variable part in a property. - Pass flat values. Properties and traits are strings, numbers, booleans and dates. Leave nested objects and arrays out of what you pass to Responsly.
- Names must match exactly. A trigger uses the event name as the app sends it, including case. Copy names from your tracking plan.
- Reset together. When your analytics tool is reset on logout, reset Responsly in the same place.
If you use React Navigation or Flutter’s Navigator, screens can be reported without a helper. See Automatic screen tracking.