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.
android {
compileSdk = 35
}
dependencies {
implementation("tech.bubbl.sdk:bubbl-sdk:5.1.0")
}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)dependencies:
flutter:
sdk: flutter
bubbl_flutter_sdk: 5.1.0npm install @bubblsdk/react-native-sdk@5.1.0
cd ios && pod installimport 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"))
}
}<application
android:name=".App"
...>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() }
}
}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
}
}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());
}import { Bubbl } from '@bubblsdk/react-native-sdk';
Bubbl.start('pk_live_…', { baseUrl: 'https://api.bubbl.tech' });
// …then your app's usual AppRegistry.registerComponent(…)<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><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><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>// 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? */ }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? */ }await Bubbl.permissions.requestNotifications();
await Bubbl.permissions.requestLocation(always: true);await Bubbl.permissions.requestNotifications();
await Bubbl.permissions.requestLocation({ always: true });Bubbl.diagnostics { d ->
Log.i("App", "installId=${d.installId} active=${d.active} registered=${d.registered} " +
"push=${d.hasPushToken} geofences=${d.geofences} lastError=${d.lastError}")
}let d = await Bubbl.diagnostics()
print(d.installId ?? "none", d.active, d.registered, d.hasPushToken, d.geofences, d.lastError ?? "none")final d = await Bubbl.diagnostics();
debugPrint('installId=${d.installId} active=${d.active} registered=${d.registered} '
'push=${d.hasPushToken} geofences=${d.geofences} lastError=${d.lastError}');const d = await Bubbl.diagnostics();
console.log(d.installId, d.active, d.registered, d.hasPushToken, d.geofences, d.lastError);Android#
- 1Install the SDKCheck that
mavenCentral()andgoogle()are in your repositories insettings.gradle.kts, then addtech.bubbl.sdk:bubbl-sdkto 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. - 2Start Bubbl in Application.onCreateIn the dashboard, open Configuration › Developers, copy the SDK API key (it starts
pk_live_) and note the API address shown under it. CallBubbl.startwith both in yourApplicationclass'sonCreate, and register the class inAndroidManifest.xml. Start it there, not in an activity: Android wakes your app in the background for geofences and pushes, and onlyApplication.onCreateruns then. - 3Run the appRun the app on your phone. Bubbl registers the install with your live key. If it doesn't, read
lastErrorin the diagnostics below, and see Troubleshooting. Bubbl logs natively, in Logcat under the tagBubblSDK. - 4Ask for permissionsAsk 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. - 5Connect pushUpload your Firebase project's service account under Configuration › Push › Credentials. Then either keep your own
google-services.jsonwith thecom.google.gms.google-servicesGradle 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. - 6Check it worksRead
Bubbl.diagnosticsand check thatactiveandregisteredare true, and thathasPushTokenbecomes 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 withlogLevel = BubblOptions.LogLevel.DEBUGinBubblOptionsand filter Logcat onBubblSDK: see Turn on debug logs. Leave it off in release builds.
iOS#
- 1Add the packageIn 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. - 2Add location strings and the push capabilityAdd 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. - 3Start Bubbl at launchIn the dashboard, open Configuration › Developers, copy the SDK API key (it starts
pk_live_) and note the API address shown under it. CallBubbl.startwith both in your SwiftUIApp'sinit, or inapplication(_:didFinishLaunchingWithOptions:)in UIKit. Bubbl never crashes your app: a mistake such as an empty key or anhttp://address is logged, not thrown. - 4Run the appRun 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
lastErrorin the diagnostics below, and see Troubleshooting. - 5Ask for permissionsAsk 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 callregisterForRemoteNotifications()yourself: Bubbl asks iOS for the device token once notifications are allowed. - 6Connect pushUnder 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.
- 7Check it worksRead
Bubbl.diagnostics()and check thatactiveandregisteredare true, and thathasPushTokenbecomes 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 withlogLevel: .debuginBubblOptionsand watch thetech.bubbl.sdksubsystem in Console.app: see Turn on debug logs. Leave it off in release builds.
Flutter#
- 1Add the packageAdd
bubbl_flutter_sdktopubspec.yamlat the version shown (a range like^5.0.1works), then runflutter pub get. The plugin carries the native SDKs, so there's no separate Gradle dependency, pod or Swift package to add. - 2Start 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. CallBubbl.startwith both inmain(), beforerunApp. 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. - 3iOS: add location strings and the push capabilityAdd 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. - 4Run the appRun 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, readlastErrorin the diagnostics below. - 5Ask for permissionsAsk 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. - 6Connect pushFor Android, upload your Firebase service account under Configuration › Push › Credentials, then upload
google-services.jsonunder Android apps (no Firebase files in your app) or keep it inandroid/appwith the Google services Gradle plugin. For iOS, add your app and APNs key under Configuration › Push. See Push notifications for the details. - 7Check it worksRead
Bubbl.diagnostics()and check thatactiveandregisteredare true, and thathasPushTokenbecomes 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 withlogLevel: BubblLogLevel.debuginBubblOptionsand read the native logs (Logcat tagBubblSDK, or thetech.bubbl.sdksubsystem 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.
- 1Install the packageInstall
@bubblsdk/react-native-sdkat the version shown, then runpod installfor 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. - 2iOS: add location strings and the push capabilityAdd the two location usage strings to your app's
Info.plist, and the Push Notifications capability under Signing & Capabilities in Xcode. - 3Start Bubbl in index.jsIn the dashboard, open Configuration › Developers, copy the SDK API key (it starts
pk_live_) and note the API address shown under it. CallBubbl.startwith both once, at the top ofindex.js. A mistake is logged natively, never thrown, and calls beforestartdo nothing. When the OS wakes your app for a geofence or a push, the native SDK starts itself without JavaScript. - 4Run the appRebuild and run the app on your phone. Bubbl registers the install with your live key. Bubbl logs natively, in Logcat under the tag
BubblSDKor in the Xcode console. If it doesn't register, readlastErrorin the diagnostics below. - 5Ask for permissionsAsk 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. - 6Connect pushFor Android, upload your Firebase service account under Configuration › Push › Credentials, then upload
google-services.jsonunder Android apps (no Firebase files in your app) or keep it inandroid/appwith the Google services Gradle plugin. For iOS, add your app and APNs key under Configuration › Push. See Push notifications for the details. - 7Check it worksRead
Bubbl.diagnostics()and check thatactiveandregisteredare true, and thathasPushTokenbecomes 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 withBubbl.start(key, { baseUrl, logLevel: 'debug' })and read the native logs (Logcat tagBubblSDK, or thetech.bubbl.sdksubsystem 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.
npx expo install @bubblsdk/react-native-sdk@5.1.0{
"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."
}
}]
]
}
}import { Bubbl } from '@bubblsdk/react-native-sdk';
Bubbl.start('pk_live_…', { baseUrl: 'https://api.bubbl.tech' });
export default function RootLayout() {
// …
}npx expo prebuild
npx expo run:android # or: npx expo run:ios- 1Install the packageInstall
@bubblsdk/react-native-sdkat the exact version shown withnpx expo install. - 2Add the config pluginSet
ios.bundleIdentifierandandroid.packageinapp.jsonand 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). - 3Start Bubbl once, earlyWith Expo Router, call
Bubbl.startat the top ofapp/_layout.tsx, outside the component; otherwise at the top of your entry file. Use the SDK API key and API address from Configuration › Developers. - 4Build a development buildRun
npx expo prebuild, thennpx expo run:androidornpx 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#
- Push notifications: the full push setup for each platform, including pictures on iOS pushes.
- Location and geofences: background location and what the Play Console asks for.
- Plans and going live: the paid plan, your
pk_live_…key and a checklist before you release. - Adding Bubbl to an existing app: your own Firebase service, notification delegate or permission flow.
- Troubleshooting: if
registeredstays false or pushes don't arrive. - In the dashboard, Set up the SDK walks the same steps with a live checklist.