Bubbl
enum · Swiftobject · Kotlinobject · TypeScriptabstract final class · DartThe SDK's entry point. Every call your app makes to Bubbl goes through it, from start to diagnostics.
Bubbl holds every function of the SDK. You can call it from any thread. Calls made before start do nothing and log a warning, except permissions.openSettings(), which needs nothing from Bubbl. On a device below the runtime minimum (Android 8.1, iOS 17), isSupported is false and every call does nothing.
https://api.sandbox.staging.bubbl.tech and the Production base URL is https://api.staging.bubbl.tech.Properties#
start logs one line and returns, every other call does nothing, and no listener is ever called. @JvmStatic.@JvmStatic.start logs one line and returns, every other call does nothing, permission calls return nil and listeners are never called.sdkVersion from diagnostics.await Bubbl.isSupported.addEventListener listens to this stream.Start and stop#
Call start once per launch, early: in Application.onCreate on Android, in application(_:didFinishLaunchingWithOptions:) or the SwiftUI App init on iOS, in main() on Flutter, and at the top of index.js on React Native. Calling it again with the same key and options changes nothing. To start with a credential from pairing a phone with a code, see Starting with a device credential and BubblCredential.
start(context: Context, apiKey: String, options: BubblOptions)Android onlystart(context: Context, credential: BubblCredential, options: BubblOptions)Android onlystop()Android onlystart is called again: no geofences, no background work, nothing shown. Unlike optOut(), the server isn't told and nothing is dropped.start throws IllegalArgumentException for an empty API key (Bubbl.start: the API key is empty), an incomplete credential (Bubbl.start: the credential is incomplete) or a base URL that isn't allowed (Bubbl.start: baseUrl must be https:// (http:// only for localhost or a .test host)). The other platforms log the same text instead.static func start(apiKey: String, options: BubblOptions)iOS onlystatic func start(credential: BubblCredential, options: BubblOptions)iOS onlystatic func stop()iOS onlystart is called again. Nothing is dropped, and the server isn't told (unlike optOut()).static Future<void> start({required String apiKey, required BubblOptions options})Flutter onlystatic Future<void> startWithCredential(BubblCredential credential, {required BubblOptions options})Flutter onlystatic Future<void> stop()Flutter onlystart again. Nothing is dropped, and the server isn't told.start(apiKey: string, options: BubblOptions): voidReact Native onlystartWithCredential(credential: BubblCredential, options: BubblOptions): voidReact Native onlystop(): voidReact Native onlystart again. Nothing is dropped, and the server isn't told.Consent and data#
For apps that ask their users first, start with requireConsent in BubblOptions. More in Consent and user data.
setConsent(granted: Boolean)Android onlyrequireConsent (and to give consent again after an opt-out). true starts everything; false is the same as optOut().optOut()Android onlydeleteMyData()Android onlysetConsent(true) starts afresh as a new device.static func setConsent(_ granted: Bool)iOS onlyrequireConsent (and to give consent again after an opt-out). true starts everything; false is the same as optOut().static func optOut()iOS onlystatic func deleteMyData()iOS onlysetConsent(true) starts afresh as a new device.static Future<void> setConsent(bool granted)Flutter onlyrequireConsent. true starts everything; false is the same as optOut().static Future<void> optOut()Flutter onlystatic Future<void> deleteMyData()Flutter onlysetConsent(granted: boolean): voidReact Native onlyrequireConsent: true starts everything; false is the same as optOut().optOut(): voidReact Native onlydeleteMyData(): voidReact Native onlyPermissions#
Bubbl.permissions reads where the device stands and asks for notifications and location, showing Bubbl's privacy view first when the dashboard says so. Its functions are listed in Permissions.
Location#
Turning location off stops geofences only; the rest of Bubbl carries on. More in Location and geofences.
setLocationEnabled(enabled: Boolean)Android onlystatic func setLocationEnabled(_ enabled: Bool)iOS onlystatic Future<void> setLocationEnabled(bool enabled)Flutter onlysetLocationEnabled(enabled: boolean): voidReact Native onlySegments and events#
A custom event's name is 1 to 100 characters of A–Z a–z 0–9 . _ : -. It takes up to 50 flat properties: text, numbers, true/false or null. An invalid name is logged and dropped; a property of another type is left out. Event listeners receive BubblEvents on the main thread. More in Segments and events.
setSegments(segments: List<String>)Android onlytrack(name: String, properties: Map<String, Any?> = emptyMap())Android only@JvmOverloads, so Java can leave out properties.addEventListener(listener: BubblEventListener)Android onlyBubblEvents.removeEventListener(listener: BubblEventListener)Android onlyaddEventListener.static func setSegments(_ segments: [String])iOS onlystatic func track(_ name: String, properties: [String: Any?] = [:])iOS only@discardableResult static func addEventListener(_ listener: @escaping @MainActor @Sendable (BubblEvent) -> Void) -> BubblEventTokeniOS onlyBubblEvents. Keep the token to remove it with: Swift closures have no identity.static func removeEventListener(_ token: BubblEventToken)iOS onlytoken.static Future<void> setSegments(List<String> segments)Flutter onlystatic Future<void> track(String name, [Map<String, Object?> properties = const <String, Object?>{}])Flutter onlyproperties is an optional positional argument.static StreamSubscription<BubblEvent> addEventListener(void Function(BubblEvent event) listener)Flutter onlyBubbl.events. Keep the subscription to stop listening.static Future<void> removeEventListener(StreamSubscription<BubblEvent> subscription)Flutter onlyaddEventListener returned.setSegments(segments: string[]): voidReact Native onlytrack(name: string, properties?: Record<string, string | number | boolean | null>): voidReact Native onlyproperties defaults to {}.addEventListener(listener: EventListener): SubscriptionReact Native onlyBubblEvents. There's no removeEventListener: call remove() on the returned subscription.Notifications#
For an app that draws notifications itself or keeps its own inbox. The listener returns true to show a notification itself; the app then reports what happens with the report functions. BubblMessage has the message's fields and the survey answer formats. More in Drawing notifications yourself.
setNotificationListener(listener: BubblNotificationListener?)Android onlytrue to show it yourself, false to let Bubbl. null goes back to Bubbl showing everything.present(message: BubblMessage)Android onlymessage in Bubbl's notification screen, for example one the listener held back.reportDisplayed(message: BubblMessage)Android onlyreportOpened(message: BubblMessage)Android onlyreportCtaClicked(message: BubblMessage)Android onlyreportDismissed(message: BubblMessage)Android onlyreportMediaViewed(message: BubblMessage)Android onlyreportMediaCompleted(message: BubblMessage, durationSeconds: Double)Android onlydurationSeconds long.reportSurveyStarted(message: BubblMessage)Android onlysubmitSurvey(message: BubblMessage, answers: Map<String, Any?>): BooleanAndroid onlyfalse, with the reason logged, when they can't be sent.openCta(message: BubblMessage)Android onlystatic func setNotificationListener(_ listener: (@MainActor @Sendable (BubblMessage) -> Bool)?)iOS onlytrue to show it yourself, false to let Bubbl. nil goes back to Bubbl showing everything.static func present(_ message: BubblMessage)iOS onlymessage in Bubbl's notification screen, for example one the listener held back.static func reportDisplayed(_ message: BubblMessage)iOS onlystatic func reportOpened(_ message: BubblMessage)iOS onlystatic func reportCtaClicked(_ message: BubblMessage)iOS onlystatic func reportDismissed(_ message: BubblMessage)iOS onlystatic func reportMediaViewed(_ message: BubblMessage)iOS onlystatic func reportMediaCompleted(_ message: BubblMessage, durationSeconds: Double)iOS onlydurationSeconds long.static func reportSurveyStarted(_ message: BubblMessage)iOS only@discardableResult static func submitSurvey(_ message: BubblMessage, answers: [String: Any?]) -> BooliOS onlyfalse, with the reason logged, when they can't be sent.static func openCta(_ message: BubblMessage)iOS onlystatic Future<void> setNotificationListener(FutureOr<bool> Function(BubblMessage message)? listener)Flutter onlytrue to show it yourself, false to let Bubbl. With the app in the background or closed, the listener isn't asked and Bubbl shows its system notification. If the listener throws, Bubbl shows the notification. null goes back to Bubbl showing everything.static Future<void> present(BubblMessage message)Flutter onlymessage in Bubbl's notification screen.static Future<void> reportDisplayed(BubblMessage message)Flutter onlystatic Future<void> reportOpened(BubblMessage message)Flutter onlystatic Future<void> reportCtaClicked(BubblMessage message)Flutter onlystatic Future<void> reportDismissed(BubblMessage message)Flutter onlystatic Future<void> reportMediaViewed(BubblMessage message)Flutter onlystatic Future<void> reportMediaCompleted(BubblMessage message, double durationSeconds)Flutter onlydurationSeconds long.static Future<void> reportSurveyStarted(BubblMessage message)Flutter onlystatic Future<bool> submitSurvey(BubblMessage message, Map<String, Object?> answers)Flutter onlyfalse, with the reason logged natively, when they can't be sent.static Future<void> openCta(BubblMessage message)Flutter onlysetNotificationListener(listener: NotificationListener | null): voidReact Native onlytrue (or a promise of true) to show it yourself. Anything else, or a throw, lets Bubbl show it. With the app in the background it isn't asked. null goes back to Bubbl showing everything.present(message: BubblMessage): voidReact Native onlymessage in Bubbl's notification screen.reportDisplayed(message: BubblMessage): voidReact Native onlyreportOpened(message: BubblMessage): voidReact Native onlyreportCtaClicked(message: BubblMessage): voidReact Native onlyreportDismissed(message: BubblMessage): voidReact Native onlyreportMediaViewed(message: BubblMessage): voidReact Native onlyreportMediaCompleted(message: BubblMessage, durationSeconds: number): voidReact Native onlydurationSeconds long.reportSurveyStarted(message: BubblMessage): voidReact Native onlysubmitSurvey(message: BubblMessage, answers: SurveyAnswers): Promise<boolean>React Native onlyfalse, with the reason logged natively, when they can't be sent.openCta(message: BubblMessage): voidReact Native onlyPush and iOS push hooks#
isBubblMessage tells your own push handling to skip Bubbl's pushes. More in Push notifications.
isBubblMessage(data: Map<String, String>): BooleanAndroid onlydata is Bubbl's, for example in your own FirebaseMessagingService.onMessageReceived. Android needs no push hooks: Bubbl receives its pushes itself.static func isBubblMessage(_ userInfo: [AnyHashable: Any]) -> BooliOS onlystatic func setPushToken(_ deviceToken: Data)iOS onlyapplication(_:didRegisterForRemoteNotificationsWithDeviceToken:). Works before start too.static func willPresent(_ notification: UNNotification, completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) -> BooliOS onlyuserNotificationCenter(_:willPresent:withCompletionHandler:). true when the push is Bubbl's, and Bubbl calls completionHandler; false when it isn't, and your code decides.static func didReceive(_ response: UNNotificationResponse, completionHandler: @escaping () -> Void) -> BooliOS onlyuserNotificationCenter(_:didReceive:withCompletionHandler:). true when the tapped notification is Bubbl's, and Bubbl calls completionHandler; false when it isn't.Bubbl picks up the device token and hooks into your notification delegate by itself. Call setPushToken, willPresent and didReceive only when you set BubblAutoIntegrationEnabled to NO in Info.plist.
static bool isBubblMessage(Map<String, Object?> data)Flutter onlydata is Bubbl's, for example in a firebase_messaging handler. Synchronous, and checked in Dart: true when data['bubbl'] is the string '1'.The iOS push hooks aren't in Dart. With BubblAutoIntegrationEnabled set to NO in Info.plist, add import bubbl_flutter_sdk to ios/Runner/AppDelegate.swift and call the Swift Bubbl.setPushToken, Bubbl.willPresent and Bubbl.didReceive there, as on iOS.
isBubblMessage(data: Record<string, unknown> | null | undefined): booleanReact Native onlydata is Bubbl's, for example in a React Native Firebase handler. Checked in JavaScript: true when data.bubbl is the string '1'.The iOS push hooks aren't in JavaScript. With BubblAutoIntegrationEnabled set to NO in Info.plist, call them from your AppDelegate: in Swift, import BubblReactNativeSdk and call Bubbl.setPushToken, Bubbl.willPresent and Bubbl.didReceive as on iOS. From Objective-C, use the BubblPush class from <BubblReactNativeSdk/BubblReactNativeSdk-Swift.h>:
+ (void)setPushToken:(NSData *)deviceTokenReact Native onlyBubbl.setPushToken(_:).+ (BOOL)willPresent:(UNNotification *)notification completionHandler:(void (^)(UNNotificationPresentationOptions))completionHandlerReact Native onlyBubbl.willPresent(_:completionHandler:): YES for Bubbl's pushes.+ (BOOL)didReceive:(UNNotificationResponse *)response completionHandler:(void (^)(void))completionHandlerReact Native onlyBubbl.didReceive(_:completionHandler:): YES for Bubbl's pushes.+ (BOOL)isBubblMessage:(NSDictionary *)userInfoReact Native onlyBubbl.isBubblMessage(_:).Sandbox#
A Sandbox install (pk_test_ key) is pending until it's approved as a test device. registerTestDevice approves it with a single-use registration code from the dashboard (Configuration › Test devices). It never throws: the result says whether it worked. More in Sandbox and Production.
suspend fun registerTestDevice(code: String): Bubbl.TestDeviceResultAndroid onlycode.registerTestDevice(code: String, callback: Bubbl.Callback<Bubbl.TestDeviceResult>)Android only@JvmStatic.static func registerTestDevice(_ code: String) async -> BubblTestDeviceResultiOS onlycode.static func registerTestDevice(_ code: String, completion: @escaping @Sendable @MainActor (BubblTestDeviceResult) -> Void)iOS onlycompletion runs on the main thread.start, no network, below iOS 17). nil when approved.static Future<BubblTestDeviceResult> registerTestDevice(String code)Flutter onlycode. Never throws.null when approved.registerTestDevice(code: string): Promise<BubblTestDeviceResult>React Native onlycode. Never throws; without a native module it resolves not approved, with the reason.Diagnostics#
Where Bubbl stands on this device, for support and for your own debug screen. The fields are listed in Diagnostics.
suspend fun diagnostics(): Bubbl.DiagnosticsAndroid onlydiagnostics(callback: Bubbl.Callback<Bubbl.Diagnostics>)Android only@JvmStatic.static func diagnostics() async -> Bubbl.DiagnosticsiOS onlystatic func diagnostics(completion: @escaping @Sendable @MainActor (Bubbl.Diagnostics) -> Void)iOS onlycompletion runs on the main thread.static Future<BubblDiagnostics> diagnostics()Flutter onlydiagnostics(): Promise<BubblDiagnostics>React Native onlylastError.Listener and callback types#
fun interface Bubbl.Callback<T> { fun onResult(value: T) }Android onlyfun interface BubblEventListener { fun onEvent(event: BubblEvent) }Android onlyBubblEvent, on the main thread.fun interface BubblNotificationListener { fun onNotification(message: BubblMessage): Boolean }Android onlytrue when the app shows message itself. Called on the main thread.addEventListener returns, to remove that listener with. It has no public fields.Event listeners are plain Dart functions, void Function(BubblEvent event), and the notification listener is FutureOr<bool> Function(BubblMessage message). addEventListener returns a StreamSubscription<BubblEvent> from dart:async.
true (or a promise of true) to show the message yourself.addEventListener returns: call remove() to stop listening.The package exports Bubbl both as a named export and as the default export, and every type on these pages as a type export.