Supporting Push Notifications

Supporting Re-engagement Tracking using Push Notifications

Singular supports push notification tracking to enhance re-engagement measurement. By integrating Firebase Cloud Messaging (FCM) with the Singular SDK, you can track users who interact with push notifications and attribute them accordingly.

To ensure accurate tracking, follow the implementation guidelines below and make sure that notification data is correctly passed to the Singular SDK.

Why is Push Notification Tracking Important?

Push notifications are a key re-engagement tool, but tracking them requires correct integration. Singular ensures that users who interact with notifications are properly attributed, helping optimize marketing campaigns and engagement strategies.


Implementation Guide

Step 1: Setting Up Firebase Push Notifications in Flutter

First, integrate Firebase into your Flutter app to handle push notifications. Follow these steps:

  1. Add Dependencies Add the required packages to your pubspec.yaml
    yaml
    dependencies:
      firebase_core: ^2.24.2
      firebase_messaging: ^14.7.10
  2. Configure Firebase
    • Android: Place google-services.json in android/app/ and update Gradle files (see ).
    • iOS: Place GoogleService-Info.plist in the Xcode Runner folder and update Podfile with Firebase pods.
  3. Initialize Firebase in Flutter Update your main.dart:
    dart
    import 'package:firebase_core/firebase_core.dart';
    import 'package:firebase_messaging/firebase_messaging.dart';
    
    Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
      await Firebase.initializeApp();
      print("Background message: ${message.messageId}");
    }
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp();
      FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
      runApp(MyApp());
    }

Step 2: Handling Push Notifications in Flutter

When your app receives a push notification from Firebase, you need to capture the notification data and prepare it for Singular. Here's how to handle notifications in different app states:

Android

  • Foreground: Handle notifications directly in Dart.
  • Background/Terminated: Interface with the native Android layer.

Update your Dart code:

dart
class _MyAppState extends State<MyApp> {
  final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;

  @override
  void initState() {
    super.initState();

    // Request permission
    _firebaseMessaging.requestPermission();

    // Foreground notifications
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      print('Foreground message: ${message.messageId}');
      String title = message.notification?.title ?? '';
      String body = message.notification?.body ?? '';
      Map<String, dynamic> data = message.data;

      // Process notification for Singular (foreground)
      _processNotificationForSingular(title, body, data);
    });

    // Background/Opened from notification
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      print('Message opened: ${message.messageId}');
      _processNotificationForSingular(
        message.notification?.title ?? '',
        message.notification?.body ?? '',
        message.data,
      );
    });
  }

  void _processNotificationForSingular(
      String title, String body, Map<String, dynamic> data) {
    print('Notification - Title: $title, Body: $body, Data: $data');
    // Add Singular handling here (Step 3)
  }
}

iOS

  • Foreground: Similar to Android, handled in Dart.
  • Background/Terminated: Requires native iOS integration.

Step 3: Interfacing Push Notifications with the Singular SDK

To ensure Singular tracks push notification engagements for attribution, you need to pass the notification payload to the Singular SDK. This involves Dart code and some native configuration.

Providing Push Payload

  1. Android
    • Background: In your Android MainActivity, override onNewIntent to pass the intent to Singular:
      kotlin
      // MainActivity.kt
      package com.example.yourapp
      
      import io.flutter.embedding.android.FlutterActivity
      import android.content.Intent
      import com.singular.sdk.SingularBridge
      
      class MainActivity: FlutterActivity() {
          override fun onNewIntent(intent: Intent) {
              super.onNewIntent(intent)
              SingularBridge.onNewIntent(intent)
          }
      }
    • Terminated: Handled automatically by the Singular bridge layer, no additional code needed.
  2. iOS
    • Background: Call handlePushNotification in your Dart code when processing background notifications:
      dart
      void _processNotificationForSingular(
          String title, String body, Map<String, dynamic> data) {
        Singular.handlePushNotification(data);
      }
    • Terminated: Update AppDelegate.swift to handle launch options:
      swift
      import UIKit
      import Singular
      
      @UIApplicationMain
      @objc class AppDelegate: FlutterAppDelegate {
        override func application(
          _ application: UIApplication,
          didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
        ) -> Bool {
          SingularAppDelegate.shared().launchOptions = launchOptions
          return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }
      }

Setting Push Link Paths in Singular Config

Configure the Singular SDK to recognize specific paths in your notification payloads for Re-engagement attribution:

dart
import 'package:singular_flutter_sdk/singular.dart'; // Adjust import

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Singular configuration
  SingularConfig config = SingularConfig(
    apiKey: 'your_api_key',
    secret: 'your_secret',
  );

  // Set push notification link paths (same for iOS and Android)
  config.pushNotificationsLinkPaths = [
    ['sng_link'],
    ['rootObj', 'nestedObj', 'anotherNested', 'singularLink'],
  ];

  // Initialize Singular
  Singular.init(config);

  runApp(MyApp());
}

Validation Guide

Step 4: Validating/Verifying Push Notification Receipt in the Start Session Call

The Singular SDK API call ensures that the push notification payload link is correctly passed under the singular_link reserved parameter in the start session call.

Example:

https://sdk-api-v1.singular.net/api/v1/start?a=<SDK-Key>&ab=arm64-v8a&aifa=180f63a1-0955-47b4-896a-d75ae3d35955&apc=Marchv7&apg=1&aps=Intex&asid_scope=1&asid_timeinterval=0.112&av=20.0&br=samsung&c=wifi&current_device_time=1740905574088&de=m15x&device_type=phone&device_user_agent=Dalvik/2.1.0 (Linux; U; Android 14; SM-E156B Build/UP1A.231005.007)&dnt=0&event_index=3&fi=e5bRZuVddO8:APA91bHXI3OmFZv3-r8f03zyji2kvKWbIngwf8KBDHk4Rj5q5MEeEm6EtzC-if1vpJRmuYLyGdAcSz9-nc49eIjD86xwj-n9J4jKucqMVt9mP8ICifP0arA&i=net.singular.singularsampleapp&install_time=1740905507036&is=false&k=SDID&lag=0.067&lc=en_IN&ma=samsung&mo=SM-E156B&n=Singular-TestApp-Debug&p=Android&pr=m15xnndins&pu=1&rt=json&s=1740905574084&sdk=Singular/v12.6.2&singular_install_id=2dc5dfc2-a2a8-484a-aad1-fed6cb7a3023&singular_link=https://singularassist2.sng.link/C4nw9/r1m0?_dl=singular://test&_smtype=3&src=com.android.shell&u=75f7b911-5a77-597d-8892-56f5e0e210ff&update_time=1740905507036&v=14&h=1e723fd90344d5d037059f110611ed1d84fbba88


Alternatively, you can use the Singular SDK Console to verify the push notification tracking link under the Deeplink URL, as demonstrated below.

By verifying this, you can confirm that push notification engagements are correctly tracked within the Singular SDK.

Notes:

  • Please note that, unlike the singularLinksHandler code, the Singular SDK does not provide push payload callbacks for this feature. It is the responsibility of the app developer to read the push notification data and implement the deep linking logic to redirect users to specific product pages within the app. In this solution, Singular retrieves the push notification payload when the user taps on the notification and includes this payload in the SDK start session event triggered by Singular.start(config). This data is then processed on the Singular backend to attribute the push notification touchpoint/click and register it for re-engagement tracking.
  • We have a safety mechanism in place that allows only Singular link domains from the custom key value pair passed in the push notification payload. Specifically, only sng.link domains predefined in the Singular Manage Links page are permitted.

    For example:
    https://prod_test.sng.link/B0s2a/51oi?_dl=singular%3A%2F%2Fmain

    If you intend to wrap Singular links within a different domain (e.g., an ESP domain for email service providers), you must explicitly configure the domain by adding the following option in your setup: This ensures that the external domain is recognized and allowed within the Singular framework. please refer to the below configuration example. config.withESPDomains(Arrays.asList("sl.esp.link"));

  • If you need to trigger different deep links based on user actions from a single push notification, you can use a single Singular tracking link and dynamically modify redirects.

    Example:

    A push notification for breaking news may offer multiple deep link options! Instead of creating multiple tracking links, configure one Singular tracking link and adjust redirects dynamically based on user selection.

    Read Latest News
    newsapp://article?id=12345
    Trending Topics
    newsapp://trending
    Sports
    newsapp://sports


    Learn more about overriding redirects in Singular tracking links.

Success!

By following these steps, your app is now set up to track push notification interactions using Singular. This helps improve campaign performance insights and ensures accurate re-engagement attribution.