Developer integration guide

Ship AppRainier in your app with one config file.

Use this guide to install the v1.0 SDKs, initialize AppRainier, identify users, track events, show surveys and announcements, render live cards, evaluate feature flags, and launch Message Center across Android, iOS, Flutter, React Native, and Web.

v1.0 SDKs Config-file setup Copyable examples Error handling
Quickstart
await AppRainier.initializeWithConfig(config);
await AppRainier.identify('user_123', { email: 'user@example.com' });
await AppRainier.trackEvent('checkout_started', { total: 89.99 });
await AppRainier.showSurvey('thumbs_up_down_feedback_survey');

How to use AppRainier

Start from the admin portal, then connect the SDK.

1

Create workspace

Create your organization, select a plan, and complete project setup in the AppRainier admin portal.

2

Create API key

Create a client SDK API key and download its config JSON file. Each API key gets its own config file.

3

Add SDK

Download the SDK for your platform and add the config file to the app bundle, assets, or public web path.

4

Initialize once

Initialize AppRainier when the app starts. Then identify users and set app/device properties.

5

Launch features

Create surveys, live cards, announcements, flags, experiments, and Message Center settings in the admin portal.

6

Measure and improve

Review analytics, responses, AI insights, engagement stats, and usage from the admin portal.

v1.0 downloads

Download the SDK package for your platform.

Config file rule: keep your downloaded config file private to your client app build or controlled web deployment. Do not paste config values into public docs, tickets, screenshots, or logs.

Reference test apps

Use the sample apps as working integration references.

Each test app shows a complete AppRainier setup for its platform: SDK installation, config-file initialization, user registration, event tracking, surveys, live cards, announcements, feature flags, Message Center, and callback handling.

Recommended workflow: clone the test app for your platform, replace its config file with the config downloaded from your AppRainier admin portal, run the app, then copy the integration pattern into your production app.

Common SDK flow

The same lifecycle applies to every platform.

InitializeLoad the SDK config and wait until the SDK is ready.
IdentifySet the user ID, email, plan, country, app version, and custom traits.
TrackSend important product events such as onboarding, checkout, support, and feature usage.
EngageShow eligible surveys, announcements, banners, live cards, and Message Center.
ExperimentEvaluate feature flags and variants, then track exposure and conversion.
FlushFlush queued events before logout, account switch, or app shutdown when possible.

Trigger IDs and events

Use stable IDs so product teams and engineers speak the same language.

Trigger IDs

Use readable snake_case IDs such as checkout_exit_survey, maintenance_alert_announcement, or promotions_showcase_carousel.

Event names

Track lifecycle events such as dashboard_opened, checkout_started, purchase_completed, and support_chat_opened.

User properties

Send traits that are useful for targeting, for example plan, country, signup date, purchase count, or role.

Fallbacks

Always pass safe defaults to feature flags so your app behavior stays predictable during first launch or offline use.

Android integration

Download Android SDK

Install the Android AAR and initialize from assets.

  1. Add the release AAR to your app module, for example app/libs/apprainier-android-sdk-release.aar.
  2. Add apprainier-config.json to app/src/main/assets/.
  3. Initialize once and call setCurrentActivity so the SDK can present native UI.
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        AppRainier.initialize(
            context = this,
            configAssetPath = "apprainier-config.json",
        ) { state ->
            if (state == SDKState.READY) {
                AppRainier.setCurrentActivity(this)
                AppRainier.setAppProperty("app_version", "1.0.0")
                AppRainier.setDeviceProperty("platform", "android")
            }
        }
    }

    override fun onResume() {
        super.onResume()
        if (AppRainier.isInitialized() && AppRainier.isReady()) {
            AppRainier.setCurrentActivity(this)
        }
    }
}
AppRainier.identify("user_123", mapOf("email" to "user@example.com"))
AppRainier.trackEvent("checkout_started", mapOf("cart_value" to 49.99))

AppRainier.showSurvey("thumbs_up_down_feedback_survey", this, surveyCallback)
AppRainier.showAnnouncement("maintenance_alert_announcement", this, announcementCallback)

AppRainier.createLiveCardView("live_card_discount_product", this) { view ->
    if (view != null) container.addView(view)
}

val promoEnabled: Boolean = AppRainier.getFeatureFlag("promo_status", false)
AppRainier.trackExperimentExposure("button_color_experiment")
AppRainier.trackExperimentConversion("button_color_experiment", "button_tapped", 1)

val unread = AppRainier.getUnreadMessageCount()
if (AppRainier.canShowMessageCenter()) {
    AppRainier.openMessageCenter(this, initialTab = "messages")
}
private val surveyCallback = object : SurveyCallback {
    override fun onSurveySubmitted(
        surveyId: String,
        responses: Map<String, Any>,
        targetScreen: String?,
        deepLink: String?
    ) {
        Log.d("AppRainier", "Survey submitted: $surveyId")
    }

    override fun onSurveyCancelled(
        surveyId: String,
        targetScreen: String?,
        deepLink: String?
    ) {
        Log.d("AppRainier", "Survey cancelled: $surveyId")
    }

    override fun onSurveyDismissed(surveyId: String) {
        Log.d("AppRainier", "Survey dismissed: $surveyId")
    }
}

private val announcementCallback = object : AnnouncementCallback {
    override fun onAnnouncementSubmitted(
        announcementId: String,
        responses: Map<String, Any>,
        targetScreen: String?,
        deepLink: String?
    ) {
        Log.d("AppRainier", "Announcement action: $announcementId")
    }

    override fun onAnnouncementCancelled(
        announcementId: String,
        targetScreen: String?,
        deepLink: String?
    ) {
        Log.d("AppRainier", "Announcement cancelled: $announcementId")
    }

    override fun onAnnouncementDismissed(announcementId: String) {
        Log.d("AppRainier", "Announcement dismissed: $announcementId")
    }
}
For push notifications, apps can use the SDK-owned messaging service or forward tokens and notification payloads from their existing Firebase messaging service.

iOS integration

Download iOS SDK

Add the XCFramework and initialize from the app bundle.

  1. Add AppRainierSdk.xcframework to your Xcode project.
  2. Add apprainier-config.json to the app target bundle and enable target membership.
  3. Wrap your SwiftUI root with AppRainierOverlayHost so SDK UI can be presented.
import AppRainierSdk
import SwiftUI

@main
struct YourApp: App {
    @StateObject private var appRainier = AppRainier.shared

    var body: some Scene {
        WindowGroup {
            AppRainierOverlayHost(sdk: appRainier) {
                ContentView()
            }
            .task {
                appRainier.initialize(configFileName: "apprainier-config") { state in
                    if state.isReady {
                        appRainier.setAppProperty("app_version", value: "1.0.0")
                        appRainier.setDeviceProperty("platform", value: "ios")
                    }
                }
            }
        }
    }
}
AppRainier.shared.identify(
    userId: "user_123",
    traits: ["email": "user@example.com"]
)

AppRainier.shared.trackEvent(
    "checkout_started",
    properties: ["cart_value": 49.99]
)

let survey = await AppRainier.shared.showSurvey("thumbs_up_down_feedback_survey")
let announcement = await AppRainier.shared.showAnnouncement("maintenance_alert_announcement")

let hasCard = await AppRainier.shared.hasLiveCard(triggerId: "live_card_discount_product")
let promoEnabled: Bool = await AppRainier.shared.getFeatureFlag("promo_status", defaultValue: false)

await AppRainier.shared.trackExperimentExposure("button_color_experiment")
await AppRainier.shared.trackExperimentConversion("button_color_experiment", goalId: "button_tapped", value: 1)

let unread = AppRainier.shared.getUnreadMessageCount()
await AppRainier.shared.refreshMessageCenter()
AppRainier.shared.openMessageCenter(initialTab: "messages")
struct MySurveyCallback: SurveyCallback {
    func onSurveySubmitted(_ result: SurveyPresentationResult) {
        print("Survey submitted", result.surveyId)
    }

    func onSurveyCancelled(surveyId: String, targetScreen: String?, deepLink: String?) {
        print("Survey cancelled", surveyId)
    }

    func onSurveyDismissed(surveyId: String) {
        print("Survey dismissed", surveyId)
    }
}

let surveyShown = await AppRainier.shared.showSurvey(
    "thumbs_up_down_feedback_survey",
    callback: MySurveyCallback()
)

struct MyAnnouncementCallback: AnnouncementCallback {
    func onAnnouncementSubmitted(_ result: AnnouncementPresentationResult) {
        print("Announcement action", result.announcementId)
    }

    func onAnnouncementCancelled(announcementId: String, targetScreen: String?, deepLink: String?) {
        print("Announcement cancelled", announcementId)
    }

    func onAnnouncementDismissed(announcementId: String) {
        print("Announcement dismissed", announcementId)
    }
}

let announcementShown = await AppRainier.shared.showAnnouncement(
    "maintenance_alert_announcement",
    callback: MyAnnouncementCallback()
)
For push notifications, register with your notification provider and forward token refreshes and notification taps to the SDK helper APIs.

Flutter integration

Download Flutter SDK

Add the plugin and initialize from a Flutter asset.

flutter:
  assets:
    - assets/apprainier-config.json
import 'package:flutter_apprainier_plugin/flutter_apprainier_plugin.dart';

await AppRainier.initializeFromConfigAsset('assets/apprainier-config.json');

await AppRainier.setUserProfile(
  const AppRainierUserProfile(
    userId: 'user_123',
    userType: 'registered',
    userProperties: {'email': 'user@example.com'},
  ),
);

await AppRainier.trackEvent('checkout_started', properties: {'cartValue': 42});
final shown = await AppRainier.showSurvey('thumbs_up_down_feedback_survey');
final opened = await AppRainier.showAnnouncement('maintenance_alert_announcement');
SizedBox(
  height: 220,
  child: AppRainierLiveCardView(
    triggerId: 'promotions_showcase_carousel',
    onCardClick: (payload) {
      debugPrint('Live card clicked: $payload');
    },
  ),
);

final unread = await AppRainier.getUnreadMessageCount();
await AppRainier.openMessageCenter();
final surveySubscription = AppRainier.addSurveyCallback(
  AppRainierSurveyCallback(
    onSurveySubmitted: (payload) => debugPrint('Survey submitted: $payload'),
    onSurveyDismissed: (payload) => debugPrint('Survey dismissed: $payload'),
  ),
);

final announcementSubscription = AppRainier.addAnnouncementCallback(
  AppRainierAnnouncementCallback(
    onAnnouncementSubmitted: (payload) => debugPrint('Announcement action: $payload'),
  ),
);

// Later, when the widget is disposed:
surveySubscription.remove();
announcementSubscription.remove();
Android Flutter hosts should use FlutterFragmentActivity. iOS Flutter hosts should run pod install after adding the plugin.

React Native integration

Download React Native SDK

Install the plugin and pass the config object.

import AppRainier, { AppRainierLiveCardView } from 'react-native-apprainier-plugin';
import config from './apprainier-config.json';

await AppRainier.initializeWithConfig(config);

await AppRainier.setUserProfile({
  userId: 'user_123',
  userType: 'registered',
  userProperties: { email: 'user@example.com' },
});

await AppRainier.trackEvent('checkout_started', { cartValue: 42 });
await AppRainier.showSurvey('thumbs_up_down_feedback_survey');
await AppRainier.showAnnouncement('maintenance_alert_announcement');
<AppRainierLiveCardView
  triggerId="live_card_discount_product"
  style={{ width: '100%', height: 180 }}
  onCardClick={({ nativeEvent }) => {
    console.log('Live card clicked', nativeEvent);
  }}
/>

await AppRainier.refreshFeatureFlags(true);
const enabled = await AppRainier.getFeatureFlag('promo_status', false);
await AppRainier.openMessageCenter({ initialTab: 'messages' });
const unread = await AppRainier.getUnreadMessageCount();
const subscription = AppRainier.addSurveyCallback({
  onSurveySubmitted: payload => console.log(payload),
  onSurveyDismissed: payload => console.log(payload),
});

subscription.remove();

Web integration

Download Web SDK

Initialize from a served config file.

import { AppRainier } from '@apprainier/web-sdk';

await AppRainier.initializeFromConfigUrl('/apprainier-config.json');

await AppRainier.identify('user_123', { email: 'user@example.com' });
await AppRainier.trackEvent('checkout_completed', { total: 89.99 });

await AppRainier.showSurvey('survey_star_rating_prompt');
await AppRainier.showAnnouncement('maintenance_alert_announcement');

const card = await AppRainier.createLiveCard('live_card_discount_product');
document.querySelector('#live-card-slot').replaceChildren(card);

const promoEnabled = await AppRainier.getFeatureFlag('promo_status', false);
await AppRainier.openMessageCenter({ initialTab: 'messages' });
AppRainier.addSurveyCallback({
  onSurveySubmitted: payload => console.log('survey submitted', payload),
  onSurveyDismissed: payload => console.log('survey dismissed', payload),
});

AppRainier.addAnnouncementCallback({
  onAnnouncementSubmitted: payload => console.log('announcement action', payload),
});
Serve the config file from a controlled path and restrict usage to your approved web domains.

Callbacks and app behavior

Use callbacks to keep your product flow native.

Survey submittedThank the user, unlock a reward, refresh a screen, or send an internal product event.
Survey dismissedDo not treat dismissals as errors. They can happen when no eligible survey exists or the user closes the prompt.
Announcement actionRoute primary and secondary CTA actions to deep links, screens, upgrade pages, or support flows.
Live card clickNavigate to product pages, offers, feature education, or custom in-app destinations.
Message Center unreadShow badges only for unread messages received from your team, not messages sent by the user.
Experiment conversionTrack meaningful goals such as trial started, purchase completed, message sent, or onboarding completed.

API reference

Common SDK APIs.

initialize

Loads the SDK config and prepares runtime settings, local cache, event queue, and UI surfaces.

setUserProfile / identify

Associates activity with a known user and sends user, app, device, and custom properties.

trackEvent

Queues product events. Use stable event names such as checkout_started or dashboard_opened.

flush

Attempts to upload queued telemetry. Call before logout or when the app is about to close.

showSurvey

Shows an eligible survey for the trigger ID. Returns false/nil when no eligible survey exists.

showAnnouncement

Shows an eligible announcement or banner and reports CTA actions through callbacks.

Live Card APIs

Refresh cards, check eligibility, render SDK card UI, and receive click payloads.

getFeatureFlag

Returns a boolean/string/variant value with the supplied fallback when unavailable.

trackExperimentExposure

Records that a user saw an experiment or variant.

trackExperimentConversion

Records conversion goals and optional values for experiment results.

Message Center APIs

Refresh settings, open Message Center, and read unread message count.

Push helpers

Forward push tokens and AppRainier-owned notification payloads from the host app.

Errors and troubleshooting

Handle SDK states clearly.

SDK is not initialized

Initialize once before calling feature APIs. Make sure the config file is present and included in the app build.

No eligible survey or announcement

The item may be archived, unpublished, outside its schedule, over impression limits, blocked by cooldown, or not matching targeting.

Feature flag returns fallback

Check the flag key, published status, audience rules, default value type, and whether the SDK has refreshed runtime config.

Live card does not render

Confirm the trigger ID, targeting rules, published status, image access, dimensions, and platform-specific native view setup.

Message Center does not open

Enable Message Center in settings, identify the user, refresh Message Center settings, and verify chat support is enabled if using messages.

Events arrive late

Events are batched for efficiency. Call flush() before logout or when you need immediate upload.

Production tip: wrap SDK calls in small helper functions in your app so you can log failures, apply fallbacks, and keep product code clean.

Production release checklist

Before shipping to customers.