Quickstart · iOS · Android · React Native · Flutter

Quickstart

Add the SDK, start it with your live key and check that your phone registers. About 15 minutes per platform. Your own app needs a paid plan.

Your own app needs a paid plan. On the Free plan you can build campaigns in the dashboard and see them in Bubbl's Showcase demo app, which you connect to your workspace from See it on your phone in the dashboard. See Plans and going live.

You need a Bubbl dashboard login with access to Configuration (owners, admins and developers have it), a paid plan, and a real phone: Android 8.1 or later with Google Play services, or an iPhone on iOS 17 or later. Choose your platform in the sidebar and follow its steps. There's no test workspace: the key is your live key, and what you create in the dashboard is live.

BUILD.GRADLE.KTS
android {
    compileSdk = 35
}

dependencies {
    implementation("tech.bubbl.sdk:bubbl-sdk:5.1.0")
}
PACKAGE
Xcode › File › Add Package Dependencies…

Package URL:      https://github.com/bubbl-public/bubbl-ios-sdk
Dependency rule:  Up to Next Major Version  5.1.0
Product:          BubblSDK  (add it to your app target)
PUBSPEC.YAML
dependencies:
  flutter:
    sdk: flutter
  bubbl_flutter_sdk: 5.1.0
INSTALL
npm install @bubblsdk/react-native-sdk@5.1.0
cd ios && pod install
APP.KT
import android.app.Application
import tech.bubbl.sdk.Bubbl
import tech.bubbl.sdk.BubblOptions

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        Bubbl.start(this, "pk_live_…", BubblOptions(baseUrl = "https://api.bubbl.tech"))
    }
}
ANDROIDMANIFEST.XML
<application
    android:name=".App"
    ...>
MYAPP.SWIFT
import BubblSDK
import SwiftUI

@main
struct MyApp: App {
    init() {
        Bubbl.start(apiKey: "pk_live_…", options: BubblOptions(baseUrl: "https://api.bubbl.tech"))
    }

    var body: some Scene {
        WindowGroup { ContentView() }
    }
}
APPDELEGATE.SWIFT
import BubblSDK
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        Bubbl.start(apiKey: "pk_live_…", options: BubblOptions(baseUrl: "https://api.bubbl.tech"))
        return true
    }
}
MAIN.DART
import 'package:bubbl_flutter_sdk/bubbl_flutter_sdk.dart';
import 'package:flutter/widgets.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Bubbl.start(
    apiKey: 'pk_live_…',
    options: const BubblOptions(baseUrl: 'https://api.bubbl.tech'),
  );
  runApp(const MyApp());
}
INDEX.JS
import { Bubbl } from '@bubblsdk/react-native-sdk';

Bubbl.start('pk_live_…', { baseUrl: 'https://api.bubbl.tech' });

// …then your app's usual AppRegistry.registerComponent(…)
INFO.PLIST
<key>NSLocationWhenInUseUsageDescription</key>
<string>We use your location to show offers for places near you.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We use your location, even when the app is closed, to show offers when you arrive at a place.</string>
INFO.PLIST
<key>NSLocationWhenInUseUsageDescription</key>
<string>We use your location to show offers for places near you.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We use your location, even when the app is closed, to show offers when you arrive at a place.</string>
INFO.PLIST
<key>NSLocationWhenInUseUsageDescription</key>
<string>We use your location to show offers for places near you.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We use your location, even when the app is closed, to show offers when you arrive at a place.</string>
MAINACTIVITY.KT
// From a coroutine, for example lifecycleScope.launch { … } after your onboarding screen
val notifications = Bubbl.permissions.requestNotifications()   // Android 13+; nothing to ask before that
val location = Bubbl.permissions.requestLocation(always = true)

// Or with a callback (also from Java)
Bubbl.permissions.requestLocation(true) { status -> /* BubblPermissionStatus? */ }
SWIFT
let notifications = await Bubbl.permissions.requestNotifications()
let location = await Bubbl.permissions.requestLocation(always: true)

// Or with a completion handler (called on the main thread)
Bubbl.permissions.requestLocation(always: true) { status in /* BubblPermissionStatus? */ }
DART
await Bubbl.permissions.requestNotifications();
await Bubbl.permissions.requestLocation(always: true);
TS
await Bubbl.permissions.requestNotifications();
await Bubbl.permissions.requestLocation({ always: true });
KOTLIN
Bubbl.diagnostics { d ->
    Log.i("App", "installId=${d.installId} active=${d.active} registered=${d.registered} " +
        "push=${d.hasPushToken} geofences=${d.geofences} lastError=${d.lastError}")
}
SWIFT
let d = await Bubbl.diagnostics()
print(d.installId ?? "none", d.active, d.registered, d.hasPushToken, d.geofences, d.lastError ?? "none")
DART
final d = await Bubbl.diagnostics();
debugPrint('installId=${d.installId} active=${d.active} registered=${d.registered} '
    'push=${d.hasPushToken} geofences=${d.geofences} lastError=${d.lastError}');
TS
const d = await Bubbl.diagnostics();
console.log(d.installId, d.active, d.registered, d.hasPushToken, d.geofences, d.lastError);

Android#

  1. 1
    Install the SDK
    Check that mavenCentral() and google() are in your repositories in settings.gradle.kts, then add tech.bubbl.sdk:bubbl-sdk to your app module and compile with SDK 35. You don't edit your manifest: the SDK's own manifest merges in the permissions, receivers and screens it needs, and it needs no R8 or ProGuard rules.
  2. 2
    Start Bubbl in Application.onCreate
    In the dashboard, open Configuration › Developers, copy the SDK API key (it starts pk_live_) and note the API address shown under it. Call Bubbl.start with both in your Application class's onCreate, and register the class in AndroidManifest.xml. Start it there, not in an activity: Android wakes your app in the background for geofences and pushes, and only Application.onCreate runs then.
  3. 3
    Run the app
    Run the app on your phone. Bubbl registers the install with your live key. If it doesn't, read lastError in the diagnostics below, and see Troubleshooting. Bubbl logs natively, in Logcat under the tag BubblSDK.
  4. 4
    Ask for permissions
    Ask for notifications and location when it suits your app, for example after your own onboarding. requestLocation(always = true) also handles Android's separate "Allow all the time" step, which geofences need to work with the app closed. Bubbl shows its privacy view first when the dashboard's privacy setting says so, then Android's prompt.
  5. 5
    Connect push
    Upload your Firebase project's service account under Configuration › Push › Credentials. Then either keep your own google-services.json with the com.google.gms.google-services Gradle plugin, or upload the file under Android apps on the same page and leave Firebase out of your app. You don't write any push code. See Push notifications for the details.
  6. 6
    Check it works
    Read Bubbl.diagnostics and check that active and registered are true, and that hasPushToken becomes true once you've allowed notifications. In the dashboard, Configuration › Developers shows the device registered and its last check-in, and the Active users card counts it once it has geofence activity. If something isn't working, start Bubbl with logLevel = BubblOptions.LogLevel.DEBUG in BubblOptions and filter Logcat on BubblSDK: see Turn on debug logs. Leave it off in release builds.

iOS#

  1. 1
    Add the package
    In Xcode, choose File › Add Package Dependencies…, enter https://github.com/bubbl-public/bubbl-ios-sdk, set the rule to Up to Next Major Version from the version shown in the pane (so you get 5.x fixes), and add the BubblSDK product to your app target. SDK 5 comes through Swift Package Manager only. It needs Xcode 16 or later.
  2. 2
    Add location strings and the push capability
    Add the two location usage strings to your app's Info.plist: iOS shows them in its prompts. Then add the Push Notifications capability under Signing & Capabilities. You don't need any Background Modes for Bubbl: geofences and significant location changes relaunch the app without them.
  3. 3
    Start Bubbl at launch
    In the dashboard, open Configuration › Developers, copy the SDK API key (it starts pk_live_) and note the API address shown under it. Call Bubbl.start with both in your SwiftUI App's init, or in application(_:didFinishLaunchingWithOptions:) in UIKit. Bubbl never crashes your app: a mistake such as an empty key or an http:// address is logged, not thrown.
  4. 4
    Run the app
    Run a signed build on your iPhone (iOS gives an unsigned build no Keychain, and Bubbl can't keep its credential without one). Bubbl registers the install with your live key. If it doesn't, read lastError in the diagnostics below, and see Troubleshooting.
  5. 5
    Ask for permissions
    Ask for notifications and location when it suits your app. requestLocation(always: true) asks for "While Using" and then the second step to "Always", which geofences need to work with the app closed. You don't call registerForRemoteNotifications() yourself: Bubbl asks iOS for the device token once notifications are allowed.
  6. 6
    Connect push
    Under Configuration › Push, add your app with its Bundle ID, an APNs auth key (.p8) from developer.apple.com › Keys with Apple Push Notifications enabled, its Key ID and your Team ID, then choose Check and save. Bubbl sends iOS pushes straight to Apple, without Firebase. See Push notifications for the details.
  7. 7
    Check it works
    Read Bubbl.diagnostics() and check that active and registered are true, and that hasPushToken becomes true once you've allowed notifications. In the dashboard, Configuration › Developers shows the device registered and its last check-in, and the Active users card counts it once it has geofence activity. If something isn't working, start Bubbl with logLevel: .debug in BubblOptions and watch the tech.bubbl.sdk subsystem in Console.app: see Turn on debug logs. Leave it off in release builds.

Flutter#

  1. 1
    Add the package
    Add bubbl_flutter_sdk to pubspec.yaml at the version shown (a range like ^5.0.1 works), then run flutter pub get. The plugin carries the native SDKs, so there's no separate Gradle dependency, pod or Swift package to add.
  2. 2
    Start Bubbl in main()
    In the dashboard, open Configuration › Developers, copy the SDK API key (it starts pk_live_) and note the API address shown under it. Call Bubbl.start with both in main(), before runApp. When the OS wakes your app for a geofence or a push, the native SDK starts itself from its saved settings before any Dart runs.
  3. 3
    iOS: add location strings and the push capability
    Add the two location usage strings to ios/Runner/Info.plist, and the Push Notifications capability to the Runner target under Signing & Capabilities. Android needs nothing extra: the SDK's manifest merges in what it needs.
  4. 4
    Run the app
    Run the app on your phone. Bubbl registers the install with your live key. Bubbl logs natively: in Logcat under the tag BubblSDK, or in the Xcode console, not the Dart console. If it doesn't register, read lastError in the diagnostics below.
  5. 5
    Ask for permissions
    Ask for notifications and location when it suits your app. requestLocation(always: true) also asks for the second step ("Allow all the time" on Android, "Always" on iOS), which geofences need to work with the app closed. Bubbl shows its privacy view first when the dashboard's privacy setting says so.
  6. 6
    Connect push
    For Android, upload your Firebase service account under Configuration › Push › Credentials, then upload google-services.json under Android apps (no Firebase files in your app) or keep it in android/app with the Google services Gradle plugin. For iOS, add your app and APNs key under Configuration › Push. See Push notifications for the details.
  7. 7
    Check it works
    Read Bubbl.diagnostics() and check that active and registered are true, and that hasPushToken becomes true once you've allowed notifications. In the dashboard, Configuration › Developers shows the device registered and its last check-in, and the Active users card counts it once it has geofence activity. If something isn't working, start Bubbl with logLevel: BubblLogLevel.debug in BubblOptions and read the native logs (Logcat tag BubblSDK, or the tech.bubbl.sdk subsystem in Console.app on iOS): see Turn on debug logs. Leave it off in release builds.

React Native#

These steps are for a React Native CLI app. For Expo, see Expo below.

  1. 1
    Install the package
    Install @bubblsdk/react-native-sdk at the version shown, then run pod install for iOS. It needs React Native 0.76 or later with the New Architecture on, the default from 0.76. The Bubbl iOS SDK is built into the package's pod, and Android needs nothing extra.
  2. 2
    iOS: add location strings and the push capability
    Add the two location usage strings to your app's Info.plist, and the Push Notifications capability under Signing & Capabilities in Xcode.
  3. 3
    Start Bubbl in index.js
    In the dashboard, open Configuration › Developers, copy the SDK API key (it starts pk_live_) and note the API address shown under it. Call Bubbl.start with both once, at the top of index.js. A mistake is logged natively, never thrown, and calls before start do nothing. When the OS wakes your app for a geofence or a push, the native SDK starts itself without JavaScript.
  4. 4
    Run the app
    Rebuild and run the app on your phone. Bubbl registers the install with your live key. Bubbl logs natively, in Logcat under the tag BubblSDK or in the Xcode console. If it doesn't register, read lastError in the diagnostics below.
  5. 5
    Ask for permissions
    Ask for notifications and location when it suits your app. requestLocation({ always: true }) also asks for the second step ("Allow all the time" on Android, "Always" on iOS), which geofences need to work with the app closed. Bubbl shows its privacy view first when the dashboard's privacy setting says so.
  6. 6
    Connect push
    For Android, upload your Firebase service account under Configuration › Push › Credentials, then upload google-services.json under Android apps (no Firebase files in your app) or keep it in android/app with the Google services Gradle plugin. For iOS, add your app and APNs key under Configuration › Push. See Push notifications for the details.
  7. 7
    Check it works
    Read Bubbl.diagnostics() and check that active and registered are true, and that hasPushToken becomes true once you've allowed notifications. In the dashboard, Configuration › Developers shows the device registered and its last check-in, and the Active users card counts it once it has geofence activity. If something isn't working, start Bubbl with Bubbl.start(key, { baseUrl, logLevel: 'debug' }) and read the native logs (Logcat tag BubblSDK, or the tech.bubbl.sdk subsystem in Console.app on iOS): see Turn on debug logs. Leave it off in release builds.

Expo#

Bubbl works in Expo SDK 52 or later, in a development build (npx expo prebuild or EAS Build) with the New Architecture on. Expo Go can't load Bubbl: there, and in your app's web build, Bubbl's calls do nothing and Bubbl.diagnostics() answers with started false and the reason in lastError. For iOS, Expo SDK 57 needs Xcode 26.4 or later.

INSTALL
npx expo install @bubblsdk/react-native-sdk@5.1.0
APP.JSON
{
  "expo": {
    "ios": { "bundleIdentifier": "com.example.myapp" },
    "android": { "package": "com.example.myapp" },
    "plugins": [
      ["@bubblsdk/react-native-sdk", {
        "ios": {
          "locationWhenInUse": "We use your location to show offers for places near you.",
          "locationAlways": "We use your location, even when the app is closed, to show offers when you arrive."
        }
      }]
    ]
  }
}
_LAYOUT.TSX
import { Bubbl } from '@bubblsdk/react-native-sdk';

Bubbl.start('pk_live_…', { baseUrl: 'https://api.bubbl.tech' });

export default function RootLayout() {
  // …
}
BUILD
npx expo prebuild
npx expo run:android   # or: npx expo run:ios
  1. 1
    Install the package
    Install @bubblsdk/react-native-sdk at the exact version shown with npx expo install.
  2. 2
    Add the config plugin
    Set ios.bundleIdentifier and android.package in app.json and add Bubbl's config plugin. Its options are all optional: "plugins": ["@bubblsdk/react-native-sdk"] is enough. At prebuild it adds the push entitlement (aps-environment) and the two location usage strings (yours from the options, otherwise your app's own, otherwise Bubbl's defaults).
  3. 3
    Start Bubbl once, early
    With Expo Router, call Bubbl.start at the top of app/_layout.tsx, outside the component; otherwise at the top of your entry file. Use the SDK API key and API address from Configuration › Developers.
  4. 4
    Build a development build
    Run npx expo prebuild, then npx expo run:android or npx expo run:ios, or build with EAS Build. Then ask for permissions, connect push and check diagnostics as in the React Native steps above.

Next steps#