Drawing notifications yourself
Take a notification before Bubbl shows it, draw it in your own UI, and report what the user does so your campaign reports stay complete.
By default Bubbl shows every notification in its own notification screen. A notification listener gets a say first: return true and Bubbl shows nothing, so your app draws the message itself; return false and Bubbl shows it as usual. Set the listener back to null to let Bubbl show everything again.
- 1Set a notification listenerCall
setNotificationListenerearly, for example right afterstart. The listener is called on the main thread with aBubblMessagefor each notification Bubbl is about to show. Return true for the ones you draw yourself. A listener that throws counts as false, so Bubbl shows the notification rather than losing it.KOTLINBubbl.setNotificationListener { message -> if (message.isSurvey) return@setNotificationListener false // let Bubbl show surveys showMyCard(message) true }SWIFTBubbl.setNotificationListener { message in if message.isSurvey { return false } // let Bubbl show surveys showMyCard(message) return true }DARTawait Bubbl.setNotificationListener((message) async { if (message.isSurvey) return false; // let Bubbl show surveys showMyCard(message); return true; });TSBubbl.setNotificationListener((message) => { if (message.isSurvey) return false; // let Bubbl show surveys showMyBanner(message); return true; }); - 2Draw it and report what happensWhen your UI is on screen, call
reportDisplayed. Then report what the user does:reportOpenedwhen they open it from something of yours,reportCtaClickedfor the call to action,reportDismissedwhen they close it without acting,reportMediaViewedandreportMediaCompletedfor media, andreportSurveyStartedwhen they start a survey. Bubbl records these for your reports, as its own screen would.KOTLINBubbl.reportDisplayed(message) // …then, as they happen: Bubbl.reportDismissed(message) Bubbl.reportMediaViewed(message) Bubbl.reportMediaCompleted(message, durationSeconds = 42.0)SWIFTBubbl.reportDisplayed(message) // …then, as they happen: Bubbl.reportDismissed(message) Bubbl.reportMediaViewed(message) Bubbl.reportMediaCompleted(message, durationSeconds: 42)DARTawait Bubbl.reportDisplayed(message); // …then, as they happen: await Bubbl.reportDismissed(message); await Bubbl.reportMediaViewed(message); await Bubbl.reportMediaCompleted(message, 42);TSBubbl.reportDisplayed(message); // …then, as they happen: Bubbl.reportDismissed(message); Bubbl.reportMediaViewed(message); Bubbl.reportMediaCompleted(message, 42); - 3Send survey answersFor a survey, draw
questionsand send the answers withsubmitSurvey, keyed by question ID. The answers are checked as the server checks them. It returns false, and logs why, when they can't be sent: for example a required question left unanswered, or a rating of 6.KOTLINBubbl.reportSurveyStarted(message) val sent = Bubbl.submitSurvey(message, mapOf( "q1" to "choice-2", // SINGLE_CHOICE: a choice id "q2" to listOf("c1", "c3"), // MULTIPLE_CHOICE: choice ids "q3" to 4, // RATING: 1–5 "q4" to "Great service", // OPEN_ENDED: text ))SWIFTBubbl.reportSurveyStarted(message) let sent = Bubbl.submitSurvey(message, answers: [ "q1": "choice-2", // .singleChoice: a choice id "q2": ["c1", "c3"], // .multipleChoice: choice ids "q3": 4, // .rating: 1–5 "q4": "Great service", // .openEnded: text ])DARTawait Bubbl.reportSurveyStarted(message); final sent = await Bubbl.submitSurvey(message, { 'q1': 'choice-2', // singleChoice: a choice id 'q2': ['c1', 'c3'], // multipleChoice: choice ids 'q3': 4, // rating: 1–5 'q4': 'Great service', // openEnded: text });TSBubbl.reportSurveyStarted(message); const sent = await Bubbl.submitSurvey(message, { q1: 'choice-2', // singleChoice: a choice id q2: ['c1', 'c3'], // multipleChoice: choice ids q3: 4, // rating: 1–5 q4: 'Great service', // openEnded: text }); - 4Open the call to action
openCtaopens the message's call to action the way Bubbl's screen would, and reports the click, so you don't callreportCtaClickedas well. To hand a message back to Bubbl's screen after all, callpresent.KOTLINBubbl.openCta(message) // opens ctaUrl and reports the click Bubbl.present(message) // or: show it in Bubbl's screen after allSWIFTBubbl.openCta(message) Bubbl.present(message)DARTawait Bubbl.openCta(message); await Bubbl.present(message);TSBubbl.openCta(message); Bubbl.present(message);
Survey answers by question type#
Each BubblQuestion has an id, text, type, required and choices (each with an id and text). Answer by type:
| Type | Answer |
|---|---|
| Single choice | A choice ID |
| Multiple choice | A list of choice IDs |
| Rating | A whole number from 1 to 5 |
| Boolean | True or false |
| Number | A number |
| Slider | A number from 0 to 10 |
| Open-ended | Text, up to 2000 characters |
The type names follow each language: SINGLE_CHOICE in Kotlin, .singleChoice in Swift (the enum is BubblQuestion.Kind), singleChoice in Dart (BubblQuestionType) and 'singleChoice' in TypeScript.
When the listener is asked#
On Android and iOS, the listener is asked for every notification Bubbl is about to show itself: from a geofence, a pull, or (on Android) a push, including while your app is in the background. If you return true in the background, Bubbl posts no system notification either, so only take what you can show. On iOS, alert pushes are drawn by iOS without running your app, so they reach the listener only when the app first sees them, for example when the user taps one and it opens.
In Flutter, the listener is asked only while Dart can show something: on Android while your Flutter activity is in front, on iOS while the app is active. Otherwise Bubbl shows the notification itself: its own screen if the app is in front (behind a native screen or a permission dialog, say), a system notification if not. A notification Dart can't take, for example after a hot restart until the listener is set again, is shown by Bubbl too, never dropped.
In React Native, JavaScript is asked only while the app is active. In the background, Bubbl posts its own system notification, whose tap opens Bubbl's screen. If JavaScript can't answer, say mid-reload, Bubbl shows the notification, so none is lost.
Sandbox messages#
Messages from a Sandbox workspace have isSandbox set. Bubbl's own screen shows a SANDBOX ribbon on them; mark them in your UI too, so a test message isn't mistaken for a live campaign.
Related#
- Notifications and surveys: what Bubbl's own screen does.
- BubblMessage
- Bubbl: every report function.