React Native SDK - 数据隐私

文件

遵守数据隐私法

通过通知Singular用户同意选择GDPR、CCPA、COPPA和其他消费者隐私法规,实施符合隐私法规的数据收集。

当用户同意或拒绝与第三方共享其信息时,使用 Singular 的隐私方法来传达他们的选择。这将确保遵守《加州消费者隐私法案》(CCPA)等法规,并使合作伙伴尊重用户的隐私偏好。

了解更多信息:有关Singular如何处理隐私同意的详细信息,请参阅用户隐私和限制数据共享


限制数据共享

控制第三方数据共享

通知Singular用户是否同意使用limitDataSharing()方法与第三方合作伙伴共享个人数据。

方法签名

static limitDataSharing(shouldLimitDataSharing: boolean): void

参数

  • false:用户已选择并同意共享其数据
  • true: true:用户已退出,不同意共享其数据

重要:虽然此方法是可选的,但它会影响属性数据的共享。有些合作伙伴只有在明确通知用户已选择加入时,才会共享完整的归因信息。

有关完整的方法文档,请参阅limitDataSharing 参考资料


使用示例

根据用户隐私偏好实施数据共享控制。

New AchitectureOld Achitecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';

// User has opted in to share their data
export const onUserOptedInToDataSharing = () => {
  NativeSingular.limitDataSharing(false);
  console.log('Data sharing enabled');
};

// User has opted out and declined to share their data
export const onUserOptedOutOfDataSharing = () => {
  NativeSingular.limitDataSharing(true);
  console.log('Data sharing limited');
};

// Set based on user preference
export const handlePrivacyConsent = (userConsented) => {
  // Pass inverse: false = opted in, true = opted out
  NativeSingular.limitDataSharing(!userConsented);

  console.log(`Data sharing: ${userConsented ? 'Enabled' : 'Limited'}`);
};

如何使用

Singular在用户隐私回邮中使用此设置,并将其传递给需要遵守法规的合作伙伴。


GDPR 合规方法

管理用户跟踪同意和控制 SDK 功能,以符合 GDPR(通用数据保护条例)和其他隐私法规。

跟踪同意管理

跟踪同意

通过向 Singular 服务器发送 GDPR 选入事件,记录用户对跟踪的明确同意。

方法签名

static trackingOptIn(): void

何时使用

  • GDPR 合规性:当用户明确同意在受 GDPR 监管的地区进行跟踪时调用
  • 同意记录:在 Singular 系统中将用户标记为已提供 GDPR 同意
  • 默认行为:如果没有此调用,SDK 将继续跟踪,但不会明确记录同意信息

有关完整的方法文档,请参阅trackingOptIn 参考资料


实施示例

在用户通过应用程序的同意对话框接受跟踪同意后调用trackingOptIn()

New AchitectureOld Achitecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useState, useEffect } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import AsyncStorage from '@react-native-async-storage/async-storage';

export default function GDPRManager() {
  const [hasConsent, setHasConsent] = useState(null);

  useEffect(() => {
    checkStoredConsent();
  }, []);

  const checkStoredConsent = async () => {
    try {
      const consent = await AsyncStorage.getItem('gdpr_consent');
      if (consent === null) {
        // Show consent dialog
        showGDPRConsentDialog();
      } else {
        setHasConsent(consent === 'true');
      }
    } catch (error) {
      console.error('Error checking consent:', error);
    }
  };

  const showGDPRConsentDialog = () => {
    Alert.alert(
      'Privacy Consent',
      'We would like your permission to track app usage and improve your experience.',
      [
        {
          text: 'Accept',
          onPress: onUserAcceptedTracking
        },
        {
          text: 'Decline',
          style: 'cancel',
          onPress: () => setHasConsent(false)
        }
      ]
    );
  };

  const onUserAcceptedTracking = async () => {
    // Record user consent
    NativeSingular.trackingOptIn();
    console.log('User opted in to tracking');

    // Save preference
    await AsyncStorage.setItem('gdpr_consent', 'true');
    setHasConsent(true);
  };

  return (
    <View>
      {hasConsent !== null && (
        <Text>Privacy Status: {hasConsent ? 'Opted In' : 'Declined'}</Text>
      )}
    </View>
  );
}

跟踪控制方法

停止所有跟踪

完全禁用当前用户在此设备上的所有 SDK 跟踪活动。

方法签名

static stopAllTracking(): void

严重警告:此方法会永久禁用 SDK,直到调用resumeAllTracking() 。禁用状态会在应用程序重启时持续存在,只能通过编程方式逆转。

行为

  • 立即生效:立即停止所有跟踪、事件报告和数据收集功能
  • 持续状态:即使在应用程序关闭和重新打开后也会保持禁用状态
  • 无法自动重置:必须明确调用resumeAllTracking() 才能重新启用

有关完整的方法文档,请参阅stopAllTracking 参考资料


实施示例

当用户拒绝同意或通过隐私设置选择退出时停止跟踪。

New AchitectureOld Achitecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
import AsyncStorage from '@react-native-async-storage/async-storage';

// User declined all tracking
export const onUserDeclinedTracking = async () => {
  NativeSingular.stopAllTracking();
  console.log('All tracking stopped');

  // Store preference
  await AsyncStorage.setItem('tracking_enabled', 'false');
};

// Handle user opt-out from settings menu
export const handlePrivacySettingsChange = (trackingEnabled) => {
  if (!trackingEnabled) {
    NativeSingular.stopAllTracking();
    console.log('Privacy settings: Tracking disabled');
  }
};

恢复全部跟踪

在使用stopAllTracking() 停止跟踪后重新启用跟踪。

方法签名

static resumeAllTracking(): void

使用案例

  • 同意变更:用户更改隐私偏好并选择恢复跟踪
  • 隐私设置:用户通过应用程序设置菜单更新同意
  • 地区合规性:当用户转移到非监管区域时重新启用跟踪功能

有关完整的方法文档,请参阅resumeAllTracking 参考资料


实施示例

当用户重新选择或更新隐私偏好时恢复跟踪。

New AchitectureOld Achitecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
import AsyncStorage from '@react-native-async-storage/async-storage';

// User opted back in to tracking
export const onUserResumedTracking = async () => {
  NativeSingular.resumeAllTracking();
  console.log('Tracking resumed');

  // Update stored preference
  await AsyncStorage.setItem('tracking_enabled', 'true');
};

// Handle consent update from settings
export const handlePrivacySettingsChange = (trackingEnabled) => {
  if (trackingEnabled) {
    NativeSingular.resumeAllTracking();
    console.log('Privacy settings: Tracking enabled');
  }
};

IsAllTrackingStopped

检查当前用户的跟踪是否已被禁用。

方法签名

static isAllTrackingStopped(): Promise<boolean>

返回

  • Promise<true>:当前已通过stopAllTracking()停止跟踪
  • Promise<false>:跟踪处于活动状态(从未停止或已恢复

有关该方法的完整文档,请参阅isAllTrackingStopped 参考文献


实现示例

检查跟踪状态,使 UI 状态与 SDK 跟踪状态同步。

New AchitectureOld Achitecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useState, useEffect } from 'react';
import { View, Text, Switch } from 'react-native';
import NativeSingular from 'singular-react-native/jsNativeSingular';

export default function PrivacySettingsUI() {
  const [trackingEnabled, setTrackingEnabled] = useState(false);
  const [statusText, setStatusText] = useState('Checking...');

  useEffect(() => {
    updatePrivacyUI();
  }, []);

  // Check current tracking status
  const isTrackingEnabled = async () => {
    const isStopped = await NativeSingular.isAllTrackingStopped();
    return !isStopped;
  };

  // Display privacy status in settings
  const getPrivacyStatusText = async () => {
    const isStopped = await NativeSingular.isAllTrackingStopped();
    return isStopped ? 'Tracking: Disabled' : 'Tracking: Enabled';
  };

  // Sync UI with tracking state
  const updatePrivacyUI = async () => {
    const isStopped = await NativeSingular.isAllTrackingStopped();
    setTrackingEnabled(!isStopped);

    const status = await getPrivacyStatusText();
    setStatusText(status);

    console.log(`Current tracking state: ${isStopped ? 'Stopped' : 'Active'}`);
  };

  // Handle toggle change from UI
  const onTrackingToggleChanged = async (enabled) => {
    if (enabled) {
      NativeSingular.resumeAllTracking();
    } else {
      NativeSingular.stopAllTracking();
    }

    await updatePrivacyUI();
  };

  return (
    <View>
      <Text>{statusText}</Text>
      <Switch
        value={trackingEnabled}
        onValueChange={onTrackingToggleChanged}
      />
    </View>
  );
}

儿童隐私保护

跟踪13岁以下儿童

通知 Singular 用户未满 13 周岁,以遵守 COPPA(儿童在线隐私保护法案)和其他儿童隐私法规。

方法签名

static trackingUnder13(): void

合规要求

  • COPPA 合规性:收集美国 13 岁以下儿童数据的应用程序必须遵守
  • 有年龄限制的内容:当用户在注册或年龄验证时表明自己未满 13 岁时使用
  • 限制跟踪:限制数据收集,以遵守儿童隐私保护法

有关完整的方法文档,请参阅trackingUnder13 参考资料


实施示例

通过年龄验证确定用户未满 13 岁后,立即调用trackingUnder13()

New AchitectureOld Achitecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useState } from 'react';
import { View, TextInput, Button, Alert } from 'react-native';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import AsyncStorage from '@react-native-async-storage/async-storage';

export default function COPPAManager() {
  const [age, setAge] = useState('');

  // User identified as under 13
  const onUserUnder13 = async () => {
    NativeSingular.trackingUnder13();
    console.log('COPPA mode enabled for user under 13');

    // Store age category for app restart
    await AsyncStorage.setItem('user_age_category', 'under_13');
  };

  // Call after age verification
  const onAgeVerified = async (userAge) => {
    if (userAge < 13) {
      NativeSingular.trackingUnder13();
      console.log('COPPA restrictions applied');

      // Also limit advertising identifiers
      NativeSingular.setLimitAdvertisingIdentifiers(true);

      await AsyncStorage.setItem('user_age_category', 'under_13');

      Alert.alert('Child Account', 'Special privacy protections have been applied.');
    } else {
      await AsyncStorage.setItem('user_age_category', 'adult');
      console.log('Adult account - standard tracking');
    }
  };

  const handleAgeSubmit = () => {
    const userAge = parseInt(age, 10);

    if (isNaN(userAge) || userAge < 1 || userAge > 120) {
      Alert.alert('Invalid Age', 'Please enter a valid age.');
      return;
    }

    onAgeVerified(userAge);
  };

  return (
    <View>
      <TextInput
        placeholder="Enter your age"
        value={age}
        onChangeText={setAge}
        keyboardType="number-pad"
      />
      <Button title="Submit" onPress={handleAgeSubmit} />
    </View>
  );
}

重要:在确定用户未满 13 岁后,应尽早调用此方法,最好是在应用程序初始化期间或年龄验证后立即调用。这样可确保所有后续跟踪都遵守儿童隐私法规。


设置广告标识符限制

在混合受众应用程序的 SDK 初始化后,限制广告标识符(Android 上为 GAID,iOS 上为 IDFA)的收集和使用。

方法签名

static setLimitAdvertisingIdentifiers(isEnabled: boolean): void

参数

  • true:启用限制广告标识符模式(限制收集
  • false:禁用有限广告标识符模式:禁用有限广告标识符模式(正常收集

使用案例

  • 混合受众应用程序:同时为成人和儿童服务的应用程序,其年龄在应用程序启动后确定
  • 动态隐私控制:根据用户操作或访问内容调整跟踪
  • 运行时限制:在初始 SDK 设置后应用广告标识符限制

有关完整的方法文档,请参阅setLimitAdvertisingIdentifiers 参考资料


实施示例

根据用户年龄、内容类型或应用程序模式动态限制广告标识符。

New AchitectureOld Achitecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';

// Limit advertising identifiers after initialization
export const enableLimitedMode = () => {
  NativeSingular.setLimitAdvertisingIdentifiers(true);
  console.log('Advertising identifiers limited');
};

// Mixed audience app with age gate
export const onKidsModeSwitched = (isKidsMode) => {
  if (isKidsMode) {
    NativeSingular.setLimitAdvertisingIdentifiers(true);
    console.log('Advertising identifiers limited for kids mode');
  } else {
    NativeSingular.setLimitAdvertisingIdentifiers(false);
    console.log('Advertising identifiers unrestricted for adult mode');
  }
};

// Content-based restrictions
export const onViewingChildrensContent = () => {
  NativeSingular.setLimitAdvertisingIdentifiers(true);
  console.log('Ad identifiers restricted for children\'s content');
};

// Combined with TrackingUnder13
export const onAgeVerified = (age) => {
  if (age < 13) {
    NativeSingular.trackingUnder13();
    NativeSingular.setLimitAdvertisingIdentifiers(true);
    console.log('Full COPPA restrictions applied');
  }
};

配置替代方案:如果您在 SDK 启动前就知道隐私要求,也可以在 SDK 初始化期间通过设置limitAdvertisingIdentifiers 配置选项来限制广告标识符。


配置属性

在 SDK 初始化期间为预先知道隐私要求的应用程序设置广告标识符限制。

New AchitectureOld Achitecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';

// Limit advertising identifiers at initialization
const config: SingularConfig = {
  apikey: 'YOUR_SDK_KEY',
  secret: 'YOUR_SDK_SECRET',
  limitAdvertisingIdentifiers: true
};

NativeSingular.init(config);

有关完整的配置文档,请参阅withLimitAdvertisingIdentifiers 参考资料


实施最佳实践

完整隐私管理示例

实施尊重用户偏好并符合法规的全面隐私控制。

New AchitectureOld Achitecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import AsyncStorage from '@react-native-async-storage/async-storage';

const PREF_USER_CONSENT = 'privacy_user_consent';
const PREF_DATA_SHARING = 'privacy_data_sharing';
const PREF_AGE_CATEGORY = 'user_age_category';

/**
 * Initialize privacy settings on app startup based on stored preferences
 */
export const initializePrivacySettings = async () => {
  const hasUserConsent = await getUserConsent();
  const allowDataSharing = await getDataSharingPreference();
  const ageCategory = await getAgeCategory();

  // Apply age-based restrictions first
  if (ageCategory === 'under_13') {
    NativeSingular.trackingUnder13();
    NativeSingular.setLimitAdvertisingIdentifiers(true);
    console.log('COPPA restrictions applied on startup');
  }

  // Apply stored tracking preference
  if (hasUserConsent) {
    NativeSingular.trackingOptIn();
    NativeSingular.resumeAllTracking();
    console.log('Privacy initialized: Tracking enabled with consent');
  } else {
    NativeSingular.stopAllTracking();
    console.log('Privacy initialized: Tracking disabled');
  }

  // Set data sharing preference (inverse logic)
  NativeSingular.limitDataSharing(!allowDataSharing);

  console.log(`Privacy initialized: consent=${hasUserConsent}, sharing=${allowDataSharing}`);
};

/**
 * User accepts tracking via consent dialog
 */
export const onUserAcceptedTracking = async () => {
  await saveUserConsent(true);

  NativeSingular.trackingOptIn();
  NativeSingular.resumeAllTracking();

  console.log('User accepted tracking');
};

/**
 * User declines tracking
 */
export const onUserDeclinedTracking = async () => {
  await saveUserConsent(false);

  NativeSingular.stopAllTracking();

  console.log('User declined tracking');
};

/**
 * User updates data sharing preference
 */
export const setDataSharingEnabled = async (enabled) => {
  await saveDataSharingPreference(enabled);

  // Note: LimitDataSharing uses inverse logic
  // false = data sharing enabled, true = data sharing limited
  NativeSingular.limitDataSharing(!enabled);

  console.log(`Data sharing: ${enabled ? 'Enabled' : 'Limited'}`);
};

/**
 * Check if tracking is currently enabled
 */
export const isTrackingEnabled = async () => {
  const isStopped = await NativeSingular.isAllTrackingStopped();
  return !isStopped;
};

/**
 * Get current privacy status as readable text
 */
export const getPrivacyStatus = async () => {
  const isEnabled = await isTrackingEnabled();
  const dataSharingEnabled = await getDataSharingPreference();
  const ageCategory = await getAgeCategory();

  let status = `Tracking: ${isEnabled ? 'Enabled' : 'Disabled'}\n`;
  status += `Data Sharing: ${dataSharingEnabled ? 'Enabled' : 'Limited'}`;

  if (ageCategory === 'under_13') {
    status += '\nCOPPA: Restrictions Applied';
  }

  return status;
};

// Private helper methods for AsyncStorage

const getUserConsent = async () => {
  const value = await AsyncStorage.getItem(PREF_USER_CONSENT);
  return value === 'true';
};

const saveUserConsent = async (consent) => {
  await AsyncStorage.setItem(PREF_USER_CONSENT, consent.toString());
};

const getDataSharingPreference = async () => {
  const value = await AsyncStorage.getItem(PREF_DATA_SHARING);
  return value === 'true';
};

const saveDataSharingPreference = async (enabled) => {
  await AsyncStorage.setItem(PREF_DATA_SHARING, enabled.toString());
};

const getAgeCategory = async () => {
  return await AsyncStorage.getItem(PREF_AGE_CATEGORY);
};

// React component example
export default function PrivacyInitializer() {
  useEffect(() => {
    initializePrivacySettings();
  }, []);

  return null;
}

关键实施指南

最佳实践

  • 持久存储:使用 AsyncStorage 或安全存储解决方案保存用户首选项
  • 尽早初始化:尽可能在 SDK 初始化之前应用隐私设置
  • UI 同步:使用isAllTrackingStopped()保持设置 UI 与实际 SDK 状态同步
  • 清晰沟通:在应用设置中提供清晰、易用的隐私控制
  • 反向逻辑:请记住,limitDataSharing(false) 意味着启用数据共享,而true 则意味着限制数据共享
  • COPPA 优先级:在其他隐私设置之前应用儿童隐私保护 (trackingUnder13())
  • 合规文档:保留用户何时以及如何提供或撤销同意的记录