アンインストール追跡
プッシュ通知サービスをSingular SDKと統合することで、アプリのアンインストールを追跡し、ユーザーのリテンションを測定し、リエンゲージメントキャンペーンを最適化します。
重要:Google は 2018 年 4 月に GCM API を廃止しました。 Android のアンインストールトラッキングの実装にはすべて Firebase Cloud Messaging (FCM) を使用してください。
Android アンインストールトラッキング
前提条件
React Native アプリにアンインストールトラッキングを実装する前に、「Setting Up Android Uninstall Tracking」のガイドに従って、Singular プラットフォームでアプリを設定してください。
システム要件
アンインストールトラッキングには、Firebase Cloud Messaging と特定のデバイス設定が必要です。
FCM 要件(ソース):
- Android バージョン:Android 4.1 (API 16) 以降を実行しているデバイスが必要です。
- Google Play サービス:Google Playストアアプリがインストールされている必要があります。
- エミュレータのサポート:Google APIを搭載したAndroid 4.1以上のエミュレータに対応しています。
- 配布:アンインストール追跡をサポートしながら、Google Play ストア以外でもアプリを配布できます。
注意:サポートされていない Android バージョンまたは Google Play サービスを利用していないデバイスのユーザーは、アンインストールを追跡されません。
実装ステップ
ステップ 1: Firebase パッケージのインストール
コア機能とメッセージングサポートのために、React Native Firebaseの依存関係を追加します。
npm install @react-native-firebase/app
npm install @react-native-firebase/messaging
ステップ 2: Firebase の設定
Android用React NativeプロジェクトにFirebase設定ファイルを追加します。
- Firebase ConsoleプロジェクトにAndroidアプリを登録します。
-
google-services.jsonをダウンロードし、android/app/に配置します。 - Firebase メッセージングの依存関係がプロジェクトに追加されていることを確認します。
詳細なセットアップ手順については、React Native Firebase Android Setupを参照してください。
ステップ 3: 通知許可のリクエスト
FCMトークンを取得する前に、ユーザーに通知許可をリクエストします(Android 13以上で必要)。
import messaging from '@react-native-firebase/messaging';
import { Platform, PermissionsAndroid } from 'react-native';
async function requestNotificationPermission() {
if (Platform.OS === 'android') {
if (Platform.Version >= 33) {
// Android 13+ requires explicit permission request
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
// Android 12 and below automatically have permission
return true;
}
// iOS permission handled separately
return true;
}
ステップ4: FCMトークンの取得と登録
FCMデバイストークンを取得し、パーミッションを要求した後、setUninstallToken() を使用してSingularに登録します。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
initializeAndroidUninstallTracking();
}
}, []);
async function initializeAndroidUninstallTracking() {
try {
// Request notification permission (Android 13+)
const hasPermission = await requestNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Get FCM token
const token = await messaging().getToken();
if (token) {
// Register token with Singular for uninstall tracking
NativeSingular.setUninstallToken(token);
console.log('FCM token registered with Singular:', token);
} else {
console.warn('No FCM token available');
}
} catch (error) {
console.error('Error setting up uninstall tracking:', error);
}
}
async function requestNotificationPermission() {
// Implementation from Step 3 above
if (Platform.Version >= 33) {
const { PermissionsAndroid } = require('react-native');
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
return true;
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
initializeAndroidUninstallTracking();
}
}, []);
async function initializeAndroidUninstallTracking() {
try {
// Request notification permission (Android 13+)
const hasPermission = await requestNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Get FCM token
const token = await messaging().getToken();
if (token) {
// Register token with Singular for uninstall tracking
Singular.setUninstallToken(token);
console.log('FCM token registered with Singular:', token);
} else {
console.warn('No FCM token available');
}
} catch (error) {
console.error('Error setting up uninstall tracking:', error);
}
}
async function requestNotificationPermission() {
// Implementation from Step 3 above
if (Platform.Version >= 33) {
const { PermissionsAndroid } = require('react-native');
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
return true;
}
return (
// Your app components
);
}
メソッドの詳細については、setUninstallTokenリファレンスを参照してください。
ステップ 5: トークンの更新
FCMトークンが更新されるたびにSingularで更新し、正確なアンインストールトラッキングを維持します。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('FCM token refreshed:', token);
// Update Singular with new token
NativeSingular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('FCM token refreshed:', token);
// Update Singular with new token
Singular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
);
}
ベストプラクティスです:FCM トークンはいつでも更新できます(アプリの更新、デバイスの復元など)。常にonTokenRefresh イベントを購読し、Singular を最新のトークンで更新してください。
iOSのアンインストールトラッキング
前提条件
iOSアンインストールトラッキングの設定」のガイドに従って、SingularプラットフォームでiOSアプリを設定します。
iOS のアンインストールトラッキングは、Apple Push Notification サービス(APNs)技術に基づいています。アプリがプッシュ通知に対応していない場合は、Apple の「APNs にアプリを登録する」ガイドを参照してください。
実装ステップ
ステップ1:iOSの通知許可をリクエストする
ユーザーに通知許可をリクエストし、APNSデバイストークンを取得します。
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
async function requestIOSNotificationPermission() {
if (Platform.OS !== 'ios') {
return false;
}
try {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('iOS notification authorization status:', authStatus);
return true;
}
return false;
} catch (error) {
console.error('Error requesting iOS notification permission:', error);
return false;
}
}
ステップ2:APNSトークンの取得と登録
APNSデバイストークンを取得し、認可後にsetUninstallToken() 。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
initializeIOSUninstallTracking();
}
}, []);
async function initializeIOSUninstallTracking() {
try {
// Request notification authorization
const hasPermission = await requestIOSNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Register for remote notifications (required for APNS)
await messaging().registerDeviceForRemoteMessages();
// Get APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
// Register token with Singular for uninstall tracking
NativeSingular.setUninstallToken(apnsToken);
console.log('APNS token registered with Singular:', apnsToken);
} else {
console.warn('No APNS token available');
}
} catch (error) {
console.error('Error setting up iOS uninstall tracking:', error);
}
}
async function requestIOSNotificationPermission() {
// Implementation from Step 1 above
const authStatus = await messaging().requestPermission();
return authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
initializeIOSUninstallTracking();
}
}, []);
async function initializeIOSUninstallTracking() {
try {
// Request notification authorization
const hasPermission = await requestIOSNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Register for remote notifications (required for APNS)
await messaging().registerDeviceForRemoteMessages();
// Get APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
// Register token with Singular for uninstall tracking
Singular.setUninstallToken(apnsToken);
console.log('APNS token registered with Singular:', apnsToken);
} else {
console.warn('No APNS token available');
}
} catch (error) {
console.error('Error setting up iOS uninstall tracking:', error);
}
}
async function requestIOSNotificationPermission() {
// Implementation from Step 1 above
const authStatus = await messaging().requestPermission();
return authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
}
return (
// Your app components
);
}
トークンのフォーマット getAPNSToken() から取得したAPNSトークンは、すでに16進文字列としてフォーマットされており、これはSingularの正しいフォーマットです。
ステップ3:トークン更新の処理(iOS)
アプリのライフサイクル中にAPNSトークンが変更された場合、Singularで更新します。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('APNS token refreshed:', token);
// Update Singular with new token
NativeSingular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('APNS token refreshed:', token);
// Update Singular with new token
Singular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
);
}
完全なクロスプラットフォーム実装
統一されたアンインストールトラッキングセットアップ
適切なエラー処理とトークン更新ロジックを使用して、Android と iOS の両方のプラットフォームにアンインストールトラッキングを実装します。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform, PermissionsAndroid } from 'react-native';
export default function App() {
useEffect(() => {
initializeUninstallTracking();
const unsubscribe = setupTokenRefreshListener();
// Clean up listener on unmount
return () => {
if (typeof unsubscribe === 'function') unsubscribe();
};
}, []);
async function initializeUninstallTracking() {
try {
if (Platform.OS === 'android') {
await setupAndroidUninstallTracking();
} else if (Platform.OS === 'ios') {
await setupIOSUninstallTracking();
}
} catch (error) {
console.error('Error initializing uninstall tracking:', error);
}
}
async function setupAndroidUninstallTracking() {
// Request permission for Android 13+
if (Platform.Version >= 33) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
console.warn('Android notification permission denied');
return;
}
}
// Get and register FCM token
const token = await messaging().getToken();
if (token) {
NativeSingular.setUninstallToken(token);
console.log('Android FCM token registered:', token);
} else {
console.warn('Failed to retrieve Android FCM token');
}
}
async function setupIOSUninstallTracking() {
// Request iOS notification authorization
const authStatus = await messaging().requestPermission();
const authorized =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (!authorized) {
console.warn('iOS notification permission denied');
return;
}
// Register for remote notifications
await messaging().registerDeviceForRemoteMessages();
// Get and register APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
NativeSingular.setUninstallToken(apnsToken);
console.log('iOS APNS token registered:', apnsToken);
} else {
console.warn('Failed to retrieve iOS APNS token');
}
}
function setupTokenRefreshListener() {
// Listen for token refresh events
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log(`${Platform.OS.toUpperCase()} token refreshed:`, token);
NativeSingular.setUninstallToken(token);
});
return unsubscribe;
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform, PermissionsAndroid } from 'react-native';
export default function App() {
useEffect(() => {
initializeUninstallTracking();
setupTokenRefreshListener();
}, []);
async function initializeUninstallTracking() {
try {
if (Platform.OS === 'android') {
await setupAndroidUninstallTracking();
} else if (Platform.OS === 'ios') {
await setupIOSUninstallTracking();
}
} catch (error) {
console.error('Error initializing uninstall tracking:', error);
}
}
async function setupAndroidUninstallTracking() {
// Request permission for Android 13+
if (Platform.Version >= 33) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
console.warn('Android notification permission denied');
return;
}
}
// Get and register FCM token
const token = await messaging().getToken();
if (token) {
Singular.setUninstallToken(token);
console.log('Android FCM token registered:', token);
} else {
console.warn('Failed to retrieve Android FCM token');
}
}
async function setupIOSUninstallTracking() {
// Request iOS notification authorization
const authStatus = await messaging().requestPermission();
const authorized =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (!authorized) {
console.warn('iOS notification permission denied');
return;
}
// Register for remote notifications
await messaging().registerDeviceForRemoteMessages();
// Get and register APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
Singular.setUninstallToken(apnsToken);
console.log('iOS APNS token registered:', apnsToken);
} else {
console.warn('Failed to retrieve iOS APNS token');
}
}
function setupTokenRefreshListener() {
// Listen for token refresh events
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log(`${Platform.OS.toUpperCase()} token refreshed:`, token);
Singular.setUninstallToken(token);
});
return unsubscribe;
}
return (
// Your app components
);
}
検証とトラブルシューティング
実装の検証
本番環境にデプロイする前に、アンインストールトラッキングが正しく動作していることを確認します。
- ログの確認トークン登録が正しい形式でコンソールログに表示されることを確認します。
- トークン生成のテスト:権限を付与した後、最初のアプリ起動時にトークンが生成されることを確認します。
- ダッシュボードを監視する:24~48時間後のアンインストール追跡データをSingularダッシュボードで確認する
- トークン更新のテスト:アプリのデータを消去し、アプリの再起動時にトークンが正しく更新されることを確認する
よくある問題
- トークンが生成されない:Firebaseの依存関係が正しくインストールされ、React NativeプロジェクトでFirebaseが設定されていることを確認する。
- パーミッションが拒否されている:ユーザーに通知権限が付与されているか確認する(Android 13以上とすべてのiOSバージョンで必要です
-
トークンが更新されない:両方のプラットフォームで
onTokenRefreshイベントを購読していることを確認してください。 - データがありません:デバイスがプラットフォーム要件を満たしていることを確認する(Android 4.1+、Google Play Services、iOS、APNs対応
- 設定エラー:アプリのSingularプラットフォーム設定でアンインストールトラッキングが有効になっていることを確認する。
-
Firebaseのセットアップ:Android の場合、
google-services.jsonがandroid/app/にあることを確認してください。 iOS の場合、GoogleService-Info.plistが Xcode プロジェクトに追加されていることを確認してください。
追加リソース:詳細なトラブルシューティングについては、Android Uninstall Tracking Setup Guide、iOS Uninstall Tracking Setup Guide、およびReact Native Firebase Messaging Documentation を参照してください。