Versions
Bubbl iOS Plugin Version: 2.0.7
Minimum iOS Version: 15.0
Xcode Version: 14.0+
Swift Version: 5.7+
Prerequisites
- A Bubbl Dashboard account with an active API key
- Firebase project with Cloud Messaging enabled
- Firebase Messaging SDK integrated - Required for FCM token generation
- Apple Developer account with push notification capability
- CocoaPods or manual framework integration setup
Integration Options
You can integrate the Bubbl iOS SDK using two methods. Note: These options are only for the Bubbl SDK itself—you can integrate Firebase using any method you prefer (CocoaPods, SPM, or manual).
- CocoaPods Integration - Recommended for automatic Bubbl SDK dependency management
- Manual Integration - Direct Bubbl framework integration
Bubbl API Key
To integrate the plugin with your app, you will need a unique
API KeyYou will find your API_KEY on the Bubbl Dashboard Company page.
Key Features
- Geofence-based notifications: Trigger notifications when users enter/exit defined locations
- Firebase Cloud Messaging: Uses FCM tokens for reliable push notification delivery (Firebase Messaging required)
- Background monitoring: Continuous location tracking even when app is closed
- Segmentation support: Target specific user groups with custom tags
- Battery optimization: Efficient power management for location services
Required Permissions
Your app must request the following permissions:
- Location (Always): For background geofence monitoring
- Push Notifications: For notification delivery
- Background Modes: Location updates capability
SDK Architecture
The Bubbl iOS SDK consists of:
- Main SDK interface and initializationBubblPlugin
- Handles notification presentation and tapsNotificationManager
- Manages location-based triggersGeofenceService
- Backend communication (internal)APIService
Quick Start Guide
- Install the SDK via CocoaPods or manual integration
- Install Firebase Messaging SDK (required dependency)
- Configure Firebase and add
GoogleService-Info.plist - Set up Firebase Messaging delegate to receive FCM tokens
- Use gated initialization pattern to ensure FCM token is available before SDK start
- Implement delegate callbacks for authentication status
- Handle notification taps using NotificationManager
The Bubbl SDK requires a valid FCM token to authenticate and register the device. Initializing before the token is ready can cause:
- "device_token: pending" errors
- Failed authentication on first launch
- SDK not loading campaigns or geofences
Use the gated initialization pattern shown in the integration guides to ensure reliable SDK start on fresh installs and TestFlight builds. This pattern uses
Messaging.messaging().token to force token fetch and includes automatic retry logic.The Bubbl SDK sets itself as the UNUserNotificationCenter delegate and does not support delegate chaining.
If your app uses multiple SDKs that handle notifications:
- Only ONE SDK can be the notification delegate at a time
- The last SDK to set the delegate will override previous ones
- This may cause notification handling conflicts with other SDKs
Note: Bubbl SDK must receive notification delegate callbacks to properly handle notification presentation, tap events, and campaign tracking. See the Multi-SDK Notification Router Pattern section below for the recommended solution.
Multi-SDK Notification Router Pattern
For apps using multiple notification SDKs, implement the notification router pattern where your host app owns the
UNUserNotificationCenter delegate and routes notifications to the appropriate SDK based on payload inspection.Bubbl Notification Identification Fields
Bubbl notifications can be identified by these payload fields:
- Bubbl notification IDn_id
- Bubbl location IDlocation_id
- Nested object containing Bubbl-specific datanotification_data
Notification Router Implementation
Create a notification router class that your app owns:
import UserNotifications
import Bubbl
class AppNotificationRouter: NSObject, UNUserNotificationCenterDelegate {
static let shared = AppNotificationRouter()
func setup() {
UNUserNotificationCenter.current().delegate = self
}
// MARK: - Check if notification is from Bubbl
private func isBubblNotification(_ userInfo: [AnyHashable: Any]) -> Bool {
// Check for Bubbl-specific fields
if userInfo["n_id"] != nil { return true }
if userInfo["location_id"] != nil { return true }
if userInfo["notification_data"] != nil { return true }
return false
}
// MARK: - Foreground presentation
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
let userInfo = notification.request.content.userInfo
if isBubblNotification(userInfo) {
// Route to Bubbl SDK
Bubbl.NotificationManager.shared.userNotificationCenter(
center,
willPresent: notification,
withCompletionHandler: completionHandler
)
} else {
// Handle other SDK notifications or your own
// e.g., forward to Firebase, OneSignal, etc.
completionHandler([.banner, .sound, .badge])
}
}
// MARK: - Notification tap handling
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo
if isBubblNotification(userInfo) {
// Route to Bubbl SDK
Bubbl.NotificationManager.shared.userNotificationCenter(
center,
didReceive: response,
withCompletionHandler: completionHandler
)
} else {
// Handle other SDK notifications or your own
completionHandler()
}
}
}Setup in AppDelegate
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Configure Firebase first
FirebaseApp.configure()
// Set YOUR app as the notification delegate (not Bubbl SDK)
AppNotificationRouter.shared.setup()
// Request permissions
UNUserNotificationCenter.current().requestAuthorization(
options: [.alert, .sound, .badge]
) { granted, _ in
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
return true
}Delegate Verification Pattern
After starting the Bubbl SDK, verify that your delegate wasn't overridden. Some SDKs may set the delegate asynchronously during initialization:
// After calling BubblPlugin.shared.start(...)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
let currentDelegate = UNUserNotificationCenter.current().delegate
if currentDelegate === AppNotificationRouter.shared {
print("✅ Delegate still set correctly after SDK start")
} else {
print("⚠️ Delegate was overridden - resetting...")
AppNotificationRouter.shared.setup()
}
}Support and Documentation
For detailed implementation guides, please refer to:
Respecting your app users
The Bubbl Plugin can be configured to respect app users by restricting notification features when a device’s battery reaches a lower limit; this can be set on the SDK Configurations page.