遵守数据隐私法
通过通知Singular用户同意选择GDPR、CCPA、COPPA和其他消费者隐私法规,实施符合隐私法规的数据收集。
当用户同意或拒绝与第三方共享其信息时,使用 Singular 的隐私方法来传达他们的选择。这将确保遵守《加州消费者隐私法案》(CCPA)等法规,并使合作伙伴尊重用户的隐私偏好。
了解更多信息:有关Singular如何处理隐私同意的详细信息,请参阅用户隐私和限制数据共享。
限制数据共享
控制第三方数据共享
通知Singular用户是否同意使用limitDataSharing()方法与第三方合作伙伴共享个人数据。
方法签名:
static limitDataSharing(shouldLimitDataSharing: boolean): void
参数:
- false:用户已选择并同意共享其数据
- true: true:用户已退出,不同意共享其数据
重要:虽然此方法是可选的,但它会影响属性数据的共享。有些合作伙伴只有在明确通知用户已选择加入时,才会共享完整的归因信息。
有关完整的方法文档,请参阅limitDataSharing 参考资料。
使用示例
根据用户隐私偏好实施数据共享控制。
// 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'}`);
};
import { Singular } from 'singular-react-native';
// User has opted in to share their data
export const onUserOptedInToDataSharing = () => {
Singular.limitDataSharing(false);
console.log('Data sharing enabled');
};
// User has opted out and declined to share their data
export const onUserOptedOutOfDataSharing = () => {
Singular.limitDataSharing(true);
console.log('Data sharing limited');
};
// Set based on user preference
export const handlePrivacyConsent = (userConsented) => {
// Pass inverse: false = opted in, true = opted out
Singular.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() 。
// 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>
);
}
import React, { useState, useEffect } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import { Singular } from 'singular-react-native';
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
Singular.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 参考资料。
实施示例
当用户拒绝同意或通过隐私设置选择退出时停止跟踪。
// 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');
}
};
import { Singular } from 'singular-react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
// User declined all tracking
export const onUserDeclinedTracking = async () => {
Singular.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) {
Singular.stopAllTracking();
console.log('Privacy settings: Tracking disabled');
}
};
恢复全部跟踪
在使用stopAllTracking() 停止跟踪后重新启用跟踪。
方法签名:
static resumeAllTracking(): void
使用案例:
- 同意变更:用户更改隐私偏好并选择恢复跟踪
- 隐私设置:用户通过应用程序设置菜单更新同意
- 地区合规性:当用户转移到非监管区域时重新启用跟踪功能
有关完整的方法文档,请参阅resumeAllTracking 参考资料。
实施示例
当用户重新选择或更新隐私偏好时恢复跟踪。
// 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');
}
};
import { Singular } from 'singular-react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
// User opted back in to tracking
export const onUserResumedTracking = async () => {
Singular.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) {
Singular.resumeAllTracking();
console.log('Privacy settings: Tracking enabled');
}
};
IsAllTrackingStopped
检查当前用户的跟踪是否已被禁用。
方法签名:
static isAllTrackingStopped(): Promise<boolean>
返回
-
Promise<true>:当前已通过
stopAllTracking()停止跟踪 - Promise<false>:跟踪处于活动状态(从未停止或已恢复
有关该方法的完整文档,请参阅isAllTrackingStopped 参考文献。
实现示例
检查跟踪状态,使 UI 状态与 SDK 跟踪状态同步。
// 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>
);
}
import React, { useState, useEffect } from 'react';
import { View, Text, Switch } from 'react-native';
import { Singular } from 'singular-react-native';
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 Singular.isAllTrackingStopped();
return !isStopped;
};
// Display privacy status in settings
const getPrivacyStatusText = async () => {
const isStopped = await Singular.isAllTrackingStopped();
return isStopped ? 'Tracking: Disabled' : 'Tracking: Enabled';
};
// Sync UI with tracking state
const updatePrivacyUI = async () => {
const isStopped = await Singular.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) {
Singular.resumeAllTracking();
} else {
Singular.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() 。
// 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>
);
}
import React, { useState } from 'react';
import { View, TextInput, Button, Alert } from 'react-native';
import { Singular } from 'singular-react-native';
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 () => {
Singular.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) {
Singular.trackingUnder13();
console.log('COPPA restrictions applied');
// Also limit advertising identifiers
Singular.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 参考资料。
实施示例
根据用户年龄、内容类型或应用程序模式动态限制广告标识符。
// 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');
}
};
import { Singular } from 'singular-react-native';
// Limit advertising identifiers after initialization
export const enableLimitedMode = () => {
Singular.setLimitAdvertisingIdentifiers(true);
console.log('Advertising identifiers limited');
};
// Mixed audience app with age gate
export const onKidsModeSwitched = (isKidsMode) => {
if (isKidsMode) {
Singular.setLimitAdvertisingIdentifiers(true);
console.log('Advertising identifiers limited for kids mode');
} else {
Singular.setLimitAdvertisingIdentifiers(false);
console.log('Advertising identifiers unrestricted for adult mode');
}
};
// Content-based restrictions
export const onViewingChildrensContent = () => {
Singular.setLimitAdvertisingIdentifiers(true);
console.log('Ad identifiers restricted for children\'s content');
};
// Combined with TrackingUnder13
export const onAgeVerified = (age) => {
if (age < 13) {
Singular.trackingUnder13();
Singular.setLimitAdvertisingIdentifiers(true);
console.log('Full COPPA restrictions applied');
}
};
配置替代方案:如果您在 SDK 启动前就知道隐私要求,也可以在 SDK 初始化期间通过设置limitAdvertisingIdentifiers 配置选项来限制广告标识符。
配置属性
在 SDK 初始化期间为预先知道隐私要求的应用程序设置广告标识符限制。
// 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);
import { Singular, SingularConfig } from 'singular-react-native';
// Limit advertising identifiers at initialization
const config = new SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
)
.withLimitAdvertisingIdentifiers(true);
Singular.init(config);
有关完整的配置文档,请参阅withLimitAdvertisingIdentifiers 参考资料。
实施最佳实践
完整隐私管理示例
实施尊重用户偏好并符合法规的全面隐私控制。
// 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;
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
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') {
Singular.trackingUnder13();
Singular.setLimitAdvertisingIdentifiers(true);
console.log('COPPA restrictions applied on startup');
}
// Apply stored tracking preference
if (hasUserConsent) {
Singular.trackingOptIn();
Singular.resumeAllTracking();
console.log('Privacy initialized: Tracking enabled with consent');
} else {
Singular.stopAllTracking();
console.log('Privacy initialized: Tracking disabled');
}
// Set data sharing preference (inverse logic)
Singular.limitDataSharing(!allowDataSharing);
console.log(`Privacy initialized: consent=${hasUserConsent}, sharing=${allowDataSharing}`);
};
/**
* User accepts tracking via consent dialog
*/
export const onUserAcceptedTracking = async () => {
await saveUserConsent(true);
Singular.trackingOptIn();
Singular.resumeAllTracking();
console.log('User accepted tracking');
};
/**
* User declines tracking
*/
export const onUserDeclinedTracking = async () => {
await saveUserConsent(false);
Singular.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
Singular.limitDataSharing(!enabled);
console.log(`Data sharing: ${enabled ? 'Enabled' : 'Limited'}`);
};
/**
* Check if tracking is currently enabled
*/
export const isTrackingEnabled = async () => {
const isStopped = await Singular.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()) - 合规文档:保留用户何时以及如何提供或撤销同意的记录