Complying with Data Privacy Laws
Implement privacy-compliant data collection by notifying Singular of user consent choices for GDPR, CCPA, COPPA, and other consumer privacy regulations.
When users consent or decline to share their information with third parties, use Singular's privacy methods to communicate their choice. This ensures compliance with regulations like California Consumer Privacy Act (CCPA) and enables partners to respect user privacy preferences.
Learn More: See User Privacy and Limit Data Sharing for detailed information on how Singular processes privacy consent.
Limit Data Sharing
Control Third-Party Data Sharing
Notify Singular whether users have consented to share their personal
data with third-party partners using the limitDataSharing()
method.
Method Signature:
static limitDataSharing(shouldLimitDataSharing: boolean): void
Parameters:
- false: User has opted in and consented to share their data
- true: User has opted out and does not consent to share their data
Important: While optional, this method affects attribution data sharing. Some partners only share complete attribution information when explicitly notified that users have opted in.
For complete method documentation, see limitDataSharing reference.
Usage Examples
Implement data sharing controls based on user privacy preferences.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/js/NativeSingular';
// 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'}`);
};
How It Works:
Singular uses this setting in User Privacy Postbacks and passes it to partners who require it for regulatory compliance.
GDPR Compliance Methods
Manage user tracking consent and control SDK functionality to comply with GDPR (General Data Protection Regulation) and other privacy regulations.
Tracking Consent Management
TrackingOptIn
Record explicit user consent for tracking by sending a GDPR opt-in event to Singular servers.
Method Signature:
static trackingOptIn(): void
When to Use:
- GDPR Compliance: Call when users explicitly consent to tracking in GDPR-regulated regions
- Consent Recording: Marks users as having provided GDPR consent in Singular's systems
- Default Behavior: Without this call, SDK continues tracking but doesn't specifically record consent
For complete method documentation, see trackingOptIn reference.
Implementation Example
Call trackingOptIn() after users accept tracking consent
through your app's consent dialog.
// 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/js/NativeSingular';
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>
);
}
Tracking Control Methods
StopAllTracking
Completely disable all SDK tracking activities for the current user on this device.
Method Signature:
static stopAllTracking(): void
Critical Warning: This method permanently disables
the SDK until resumeAllTracking() is called. The disabled
state persists across app restarts and can only be reversed programmatically.
Behavior:
- Immediate Effect: Stops all tracking, event reporting, and data collection instantly
- Persistent State: Remains disabled even after app closes and reopens
-
No Automatic Reset: Must explicitly call
resumeAllTracking()to re-enable
For complete method documentation, see stopAllTracking reference.
Implementation Example
Stop tracking when users decline consent or opt out through privacy settings.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/js/NativeSingular';
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');
}
};
ResumeAllTracking
Re-enable tracking after it was stopped with
stopAllTracking().
Method Signature:
static resumeAllTracking(): void
Use Cases:
- Consent Change: User changes privacy preferences and opts back into tracking
- Privacy Settings: User updates consent through app settings menu
- Regional Compliance: Re-enable tracking when user moves to non-regulated regions
For complete method documentation, see resumeAllTracking reference.
Implementation Example
Resume tracking when users opt back in or update their privacy preferences.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/js/NativeSingular';
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
Check whether tracking has been disabled for the current user.
Method Signature:
static isAllTrackingStopped(): Promise<boolean>
Returns:
-
Promise<true>: Tracking is currently stopped
via
stopAllTracking() - Promise<false>: Tracking is active (either never stopped or resumed)
For complete method documentation, see isAllTrackingStopped reference.
Implementation Example
Check tracking status to sync UI state with SDK tracking state.
// 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/js/NativeSingular';
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>
);
}
Children's Privacy Protection
TrackingUnder13
Notify Singular that the user is under 13 years old to comply with COPPA (Children's Online Privacy Protection Act) and other child privacy regulations.
Method Signature:
static trackingUnder13(): void
Compliance Requirements:
- COPPA Compliance: Required for apps that collect data from children under 13 in the United States
- Age-Gated Content: Use when users identify themselves as under 13 during registration or age verification
- Restricted Tracking: Limits data collection to comply with children's privacy protection laws
For complete method documentation, see trackingUnder13 reference.
Implementation Example
Call trackingUnder13() immediately after determining the
user is under 13 through age verification.
// 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/js/NativeSingular';
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>
);
}
Important: Call this method as early as possible after determining the user is under 13, ideally during app initialization or immediately after age verification. This ensures all subsequent tracking respects children's privacy regulations.
SetLimitAdvertisingIdentifiers
Restrict the collection and use of advertising identifiers (GAID on Android, IDFA on iOS) after SDK initialization for mixed audience apps.
Method Signature:
static setLimitAdvertisingIdentifiers(isEnabled: boolean): void
Parameters:
- true: Enable limited advertising identifiers mode (restrict collection)
- false: Disable limited advertising identifiers mode (normal collection)
Use Cases:
- Mixed Audience Apps: Apps that serve both adults and children where age is determined after app launch
- Dynamic Privacy Controls: Adjust tracking based on user actions or content being accessed
- Runtime Restrictions: Apply advertising identifier limitations after initial SDK setup
For complete method documentation, see setLimitAdvertisingIdentifiers reference.
Implementation Example
Dynamically limit advertising identifiers based on user age, content type, or app mode.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/js/NativeSingular';
// 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');
}
};
Configuration Alternative: You can also limit advertising
identifiers during SDK initialization by setting the
limitAdvertisingIdentifiers configuration option if
you know privacy requirements before the SDK starts.
Configuration Property
Set advertising identifier limitations during SDK initialization for apps that know privacy requirements upfront.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/js/NativeSingular';
// 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);
For complete configuration documentation, see withLimitAdvertisingIdentifiers reference.
Implementation Best Practices
Complete Privacy Management Example
Implement comprehensive privacy controls that respect user preferences and comply with regulations.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/js/NativeSingular';
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;
}
Key Implementation Guidelines
Best Practices:
- Persistent Storage: Save user preferences using AsyncStorage or secure storage solution
- Early Initialization: Apply privacy settings before SDK initialization when possible
-
UI Sync: Keep settings UI synchronized with
actual SDK state using
isAllTrackingStopped() - Clear Communication: Provide clear, accessible privacy controls in app settings
-
Inverse Logic: Remember that
limitDataSharing(false)means data sharing is enabled, whiletruemeans it's limited -
COPPA Priority: Apply children's privacy protections
(
trackingUnder13()) before other privacy settings - Compliance Documentation: Maintain records of when and how users provide or revoke consent