SDK de Flutter - Seguimiento de la desinstalación

Documento

Seguimiento de desinstalaciones

Realiza un seguimiento de las desinstalaciones de apps para medir la retención de usuarios y optimizar las campañas de reenganche integrando servicios de notificaciones push con Singular SDK.

Importante: Google dejó obsoletas las API de GCM en abril de 2018. Utilice Firebase Cloud Messaging (FCM) para todas las implementaciones de seguimiento de desinstalación de Android.


Seguimiento de desinstalación de Android

Requisitos previos

Antes de implementar el seguimiento de desinstalación en tu app Flutter, configura tu app en la plataforma Singular siguiendo la guía Configuración del seguimiento de desinstalación de Android.


Requisitos del sistema

El seguimiento de desinstalación requiere Firebase Cloud Messaging y configuraciones específicas del dispositivo.

Requisitos de FCM(fuente):

  • Versión de Android: Los dispositivos deben ejecutar Android 4.1 (API 16) o superior
  • Servicios de Google Play: Los dispositivos deben tener instalada la aplicación Google Play Store
  • Compatibilidad con emuladores: Se admiten emuladores de Android 4.1+ con API de Google
  • Distribución: Las aplicaciones se pueden distribuir fuera de Google Play Store sin dejar de admitir el seguimiento de desinstalaciones.

Nota: no se realizará el seguimiento de las desinstalaciones de los usuarios que utilicen versiones de Android no compatibles o dispositivos sin Google Play Services.


Pasos de implementación

Paso 1: Instalar paquetes Firebase

Añade las dependencias de Firebase a tu archivo pubspec.yaml para la funcionalidad básica y el soporte de mensajería.

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^2.24.2
  firebase_messaging: ^14.7.10
  singular_flutter_sdk: ^1.8.0

Después de añadir las dependencias, ejecute flutter pub get para instalar los paquetes.


Paso 2: Configurar Firebase

Añade los archivos de configuración de Firebase a tu proyecto Flutter para Android.

  1. Registra tu aplicación Android en tu proyecto Firebase Console
  2. Descarga google-services.json y colócalo en android/app/
  3. Verifica que las dependencias de mensajería de Firebase están añadidas a tu proyecto

Para obtener instrucciones de configuración detalladas, consulta Añadir Firebase a tu app Flutter.


Paso 3: Inicializar Firebase

Inicialice Firebase antes de ejecutar su app Flutter para habilitar los servicios de mensajería.

Dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Firebase
  await Firebase.initializeApp();

  runApp(MyApp());
}

Paso 4: Solicitar permisos de notificación

Solicite permisos de notificación al usuario (necesario para Android 13+) antes de recuperar el token FCM.

Dart
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

Future<bool> requestNotificationPermission() async {
  if (Platform.isAndroid) {
    // Android 13+ requires explicit permission request
    // Note: firebase_messaging handles this automatically via requestPermission
    final FirebaseMessaging messaging = FirebaseMessaging.instance;
    final NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    return settings.authorizationStatus == AuthorizationStatus.authorized ||
           settings.authorizationStatus == AuthorizationStatus.provisional;
  }

  // iOS permission handled separately
  return true;
}

Paso 5: Recuperar y registrar el token FCM

Obtenga el token de dispositivo FCM y regístrelo en Singular utilizando registerDeviceTokenForUninstall() después de solicitar los permisos.

Dart
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();

    if (Platform.isAndroid) {
      initializeAndroidUninstallTracking();
    }
  }

  Future<void> initializeAndroidUninstallTracking() async {
    try {
      // Request notification permission
      final hasPermission = await requestNotificationPermission();

      if (!hasPermission) {
        print('Notification permission denied - uninstall tracking unavailable');
        return;
      }

      // Get FCM token
      final token = await FirebaseMessaging.instance.getToken();

      if (token != null) {
        // Register token with Singular for uninstall tracking
        Singular.registerDeviceTokenForUninstall(token);
        print('FCM token registered with Singular: $token');
      } else {
        print('No FCM token available');
      }
    } catch (error) {
      print('Error setting up uninstall tracking: $error');
    }
  }

  Future<bool> requestNotificationPermission() async {
    final messaging = FirebaseMessaging.instance;
    final settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    return settings.authorizationStatus == AuthorizationStatus.authorized ||
           settings.authorizationStatus == AuthorizationStatus.provisional;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

Firma del método:

static void registerDeviceTokenForUninstall(String token)

Para obtener la documentación completa del método, consulte la referencia registerDeviceTokenForUninstall.


Paso 6: Gestionar la actualización del token

Actualice el token FCM con Singular cada vez que se actualice para mantener un seguimiento preciso de la desinstalación.

Dart
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();

    if (Platform.isAndroid) {
      setupTokenRefreshListener();
    }
  }

  void setupTokenRefreshListener() {
    // Listen for token refresh events
    FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
      print('FCM token refreshed: $token');

      // Update Singular with new token
      Singular.registerDeviceTokenForUninstall(token);
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

Práctica recomendada: Los tokens FCM pueden actualizarse en cualquier momento (actualizaciones de aplicaciones, restauración de dispositivos, etc.). Suscríbase siempre al flujo onTokenRefresh para mantener Singular actualizado con el token más reciente.


Seguimiento de la desinstalación de iOS

Requisitos previos

Configure su aplicación iOS en la plataforma Singular siguiendo la guía Configuración del seguimiento de desinstalación de iOS.

El seguimiento de desinstalaciones en iOS se basa en la tecnología del servicio de notificaciones push de Apple (APNs). Si su aplicación no es compatible con las notificaciones push, consulte la guía de Apple para Registrar su aplicación con APNs.


Pasos de implementación

Paso 1: Configurar el proyecto iOS

Añade la configuración de Firebase y habilita las capacidades de notificaciones push en tu proyecto iOS.

  1. Registra tu app iOS en tu proyecto Firebase Console
  2. Descarga GoogleService-Info.plist y añádelo a la carpeta Xcode Runner
  3. En la configuración del proyecto Xcode, habilita la capacidad de Notificaciones Push
  4. Habilita Background Modes y marca Remote notifications

Paso 2: Solicitar autorización de notificaciones iOS

Solicite permisos de notificación al usuario y recupere el token del dispositivo APNS.

Dart
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

Future<bool> requestIOSNotificationPermission() async {
  if (!Platform.isIOS) {
    return false;
  }

  try {
    final FirebaseMessaging messaging = FirebaseMessaging.instance;
    final NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
      provisional: false,
    );

    final authorized =
        settings.authorizationStatus == AuthorizationStatus.authorized ||
        settings.authorizationStatus == AuthorizationStatus.provisional;

    if (authorized) {
      print('iOS notification authorization status: ${settings.authorizationStatus}');
      return true;
    }

    return false;
  } catch (error) {
    print('Error requesting iOS notification permission: $error');
    return false;
  }
}

Paso 3: Recuperar y registrar el token APNS

Obtenga el token de dispositivo APNS y regístrelo en Singular utilizando registerDeviceTokenForUninstall() una vez concedida la autorización.

Dart
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();

    if (Platform.isIOS) {
      initializeIOSUninstallTracking();
    }
  }

  Future<void> initializeIOSUninstallTracking() async {
    try {
      // Request notification authorization
      final hasPermission = await requestIOSNotificationPermission();

      if (!hasPermission) {
        print('Notification permission denied - uninstall tracking unavailable');
        return;
      }

      // Get APNS token
      final apnsToken = await FirebaseMessaging.instance.getAPNSToken();

      if (apnsToken != null) {
        // Register token with Singular for uninstall tracking
        Singular.registerDeviceTokenForUninstall(apnsToken);
        print('APNS token registered with Singular: $apnsToken');
      } else {
        print('No APNS token available');
      }
    } catch (error) {
      print('Error setting up iOS uninstall tracking: $error');
    }
  }

  Future<bool> requestIOSNotificationPermission() async {
    final messaging = FirebaseMessaging.instance;
    final settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    return settings.authorizationStatus == AuthorizationStatus.authorized ||
           settings.authorizationStatus == AuthorizationStatus.provisional;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

Formato del token: El token APNS recuperado de getAPNSToken() ya está formateado como cadena hexadecimal, que es el formato correcto para Singular.


Paso 4: Actualización del token (iOS)

Actualice el token APNS con Singular si cambia durante el ciclo de vida de la aplicación.

Dart
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();

    if (Platform.isIOS) {
      setupIOSTokenRefreshListener();
    }
  }

  void setupIOSTokenRefreshListener() {
    // Listen for token refresh events
    FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
      print('APNS token refreshed: $token');

      // Update Singular with new token
      Singular.registerDeviceTokenForUninstall(token);
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

Implementación completa multiplataforma

Configuración unificada de seguimiento de desinstalación

Implemente el seguimiento de desinstalación para las plataformas Android e iOS con la lógica adecuada de gestión de errores y actualización de tokens.

Dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
import 'dart:io';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Firebase
  await Firebase.initializeApp();

  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();

    // Initialize Singular SDK
    initializeSingularSDK();

    // Setup uninstall tracking
    initializeUninstallTracking();

    // Setup token refresh listener
    setupTokenRefreshListener();
  }

  void initializeSingularSDK() {
    final config = SingularConfig(
      'YOUR_SDK_KEY',
      'YOUR_SDK_SECRET'
    );

    Singular.start(config);
  }

  Future<void> initializeUninstallTracking() async {
    try {
      if (Platform.isAndroid) {
        await setupAndroidUninstallTracking();
      } else if (Platform.isIOS) {
        await setupIOSUninstallTracking();
      }
    } catch (error) {
      print('Error initializing uninstall tracking: $error');
    }
  }

  Future<void> setupAndroidUninstallTracking() async {
    print('Setting up Android uninstall tracking');

    // Request notification permission
    final messaging = FirebaseMessaging.instance;
    final settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    if (settings.authorizationStatus != AuthorizationStatus.authorized &&
        settings.authorizationStatus != AuthorizationStatus.provisional) {
      print('Android notification permission denied');
      return;
    }

    // Get and register FCM token
    final token = await messaging.getToken();

    if (token != null) {
      Singular.registerDeviceTokenForUninstall(token);
      print('Android FCM token registered: $token');
    } else {
      print('Failed to retrieve Android FCM token');
    }
  }

  Future<void> setupIOSUninstallTracking() async {
    print('Setting up iOS uninstall tracking');

    // Request iOS notification authorization
    final messaging = FirebaseMessaging.instance;
    final settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    final authorized =
        settings.authorizationStatus == AuthorizationStatus.authorized ||
        settings.authorizationStatus == AuthorizationStatus.provisional;

    if (!authorized) {
      print('iOS notification permission denied');
      return;
    }

    // Get and register APNS token
    final apnsToken = await messaging.getAPNSToken();

    if (apnsToken != null) {
      Singular.registerDeviceTokenForUninstall(apnsToken);
      print('iOS APNS token registered: $apnsToken');
    } else {
      print('Failed to retrieve iOS APNS token');
    }
  }

  void setupTokenRefreshListener() {
    // Listen for token refresh events (works for both platforms)
    FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
      print('${Platform.operatingSystem.toUpperCase()} token refreshed: $token');
      Singular.registerDeviceTokenForUninstall(token);
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Uninstall Tracking Demo'),
        ),
        body: Center(
          child: Text('Uninstall tracking initialized'),
        ),
      ),
    );
  }
}

Notas específicas para cada plataforma

  • iOS: Asegúrese de que su aplicación tiene los derechos de notificación push necesarios y de que los APN están correctamente configurados en su cuenta de desarrollador de Apple.
  • Android: Verifica que FCM está configurado en tu consola Firebase y que el archivo google-services.json está incluido en tu proyecto en android/app/

Verificación y resolución de problemas

Verificar la implementación

Confirme que el seguimiento de desinstalación funciona correctamente antes de desplegar a producción.

  1. Compruebe los registros: Compruebe que el registro de tokens aparece en los registros de la consola con el formato correcto.
  2. Pruebe la generación de tokens: Asegúrese de que los tokens se generan en el primer lanzamiento de la aplicación después de conceder los permisos.
  3. Supervise el panel de control: Compruebe los datos de seguimiento de desinstalación del panel de Singular transcurridas 24-48 horas.
  4. Prueba de actualización de tokens: Borre los datos de la aplicación y compruebe que los tokens se actualizan correctamente al reiniciar la aplicación.

Problemas comunes

  • Token no generado: Comprueba que las dependencias de Firebase están correctamente instaladas y que Firebase está configurado en tu proyecto Flutter. Ejecute flutter pub get después de añadir las dependencias
  • Permiso denegado: Comprueba que los usuarios han concedido permisos de notificación. Para Android 13+, se requiere una solicitud de permiso explícita. Para iOS, los usuarios deben autorizar las notificaciones
  • Token no actualizado: Asegúrate de que te has suscrito al flujo onTokenRefresh para ambas plataformas. La escucha debe configurarse en la inicialización de la aplicación.
  • Faltan datos: Confirma que los dispositivos cumplen los requisitos de la plataforma (Android 4.1+ con Google Play Services, iOS con soporte APNs). Los dispositivos sin estos servicios no pueden ser rastreados
  • Error de configuración: Compruebe que el seguimiento de desinstalación está activado en la configuración de la plataforma Singular para su aplicación. Siga las guías de configuración específicas de la plataforma enlazadas en Requisitos previos
  • Configuración de Firebase: Para Android, asegúrese de que google-services.json está en android/app/. Para iOS, asegúrese de que GoogleService-Info.plist se añade a la carpeta Runner del proyecto Xcode.
  • Inicialización del SDK: Confirme que Singular SDK está inicializado antes de llamar a registerDeviceTokenForUninstall(). El registro del token debe realizarse después de iniciar el SDK.
  • Detección de plataforma: Utilice Platform.isAndroid y Platform.isIOS desde dart:io para garantizar que el código específico de la plataforma se ejecuta en la plataforma correcta.

Mejores prácticas

Gestión de tokens

  • Registro temprano: Solicite permisos y registre tokens lo antes posible en el ciclo de vida de la aplicación, idealmente durante el primer lanzamiento de la aplicación.
  • Gestión de errores: Implemente una sólida gestión de errores en torno a la recuperación y el registro de tokens para gestionar los fallos con elegancia.
  • Actualización de tokens: Implementa siempre escuchas de actualización de tokens para mantener Singular actualizado cuando cambien los tokens.
  • Experiencia de usuario: Solicitar permisos de notificación en contexto, explicando por qué la aplicación los necesita para mejorar las tasas de concesión de permisos.

Estrategia de pruebas

  • Pruebas de desarrollo: Probar tanto en dispositivos físicos Android como iOS, ya que los emuladores pueden tener un soporte limitado de notificaciones push
  • Flujos de permisos: Probar escenarios en los que los usuarios denieguen permisos y verificar que la aplicación los gestiona correctamente.
  • Persistencia de tokens: Verificar que los tokens persisten durante los reinicios de la aplicación y se actualizan correctamente al actualizarla.
  • Registro: Habilite un registro detallado durante el desarrollo para realizar un seguimiento de la generación y el registro de tokens.