文档
Singular SDK 的隐私合规性
针对 GDPR、CCPA、COPPA 和其他隐私法规,使用 Singular SDK 的选择进入和选择退出方法,实施符合隐私法规的跟踪解决方案的综合指南。
法律声明:本页面上的指南不构成法律建议。请咨询您的法律团队,了解如何遵守适用于您的司法管辖区和业务运营的隐私法。
Singular 提供全面的隐私合规工具,使企业能够利用平台功能,同时保持符合不断发展的隐私法规。
|
目标受众 |
开发人员、法律/合规团队、产品经理 |
|
适用法规 |
GDPR、CCPA、COPPA 和特定司法管辖区的隐私法 |
隐私法规概述
支持的合规框架
Singular 平台通过可配置的同意管理和跟踪控制机制,支持遵守主要的隐私法规。
| 法规 |
范围 |
关键要求 |
推荐方法 |
|
GDPR |
欧盟数据保护和隐私 |
收集数据前征得用户同意 |
选择加入或选择退出 |
|
CCPA |
加州消费者隐私权 |
选择不出售数据的权利 |
选择退出 |
|
COPPA |
儿童在线隐私保护(13 岁以下) |
在收集儿童数据前征得家长同意或实施年龄门 |
选择加入 + 年龄门 |
|
地区性法律 |
针对特定司法管辖区的隐私法规 |
因辖区而异 |
咨询法律顾问 |
不断演变的法规:隐私法随着新法规的颁布和解释而不断演变。Singular 平台提供灵活的工具,适应不断变化的合规要求,同时在允许的情况下保持完整的跟踪功能。
合规方法
Singular 支持两种主要的合规方法:Opt-In(同意优先)和Opt-Out(跟踪优先,用户控制),每种方法都能满足不同的法规要求和用户同意模式。
方法选择
选择合适的合规方法
根据适用法规、用户特征和法律顾问指导选择合规方法。
| 方法 |
何时需要 |
实施概要 |
|
选择加入 |
- 欧盟的 GDPR 合规性
- 儿童应用程序 COPPA 合规性
- 需要明确同意的司法管辖区
|
延迟 SDK 初始化,直至用户同意。未经明确许可,不传输数据。 |
|
选择退出 |
- 在加利福尼亚州符合 CCPA
- 允许使用退出选项进行跟踪的司法管辖区
- 针对成人受众的应用程序提供同意选项
|
正常初始化 SDK,但提供用户随时停止跟踪的机制。 |
|
混合型 |
- 在多个司法管辖区运行的应用程序
- 混合受众群体
- 不同的监管要求
|
为需要明确同意的用户实施 "选择进入",然后为持续控制提供 "选择退出 "机制。 |
最佳实践:许多企业采用混合方法,在应用程序设置中使用 "选择进入 "获得初始同意,然后使用 "选择退出 "选项,从而提供最大的灵活性和合规性。
选择进入方法
同意优先法,在用户明确授予数据收集和跟踪权限之前,延迟 SDK 初始化。
选择进入实施
实施策略
选择性输入法要求在任何 SDK 初始化或数据传输到 Singular 平台之前显示同意界面。
关键要求:在用户明确表示同意之前,不要初始化 Singular SDK 或收集任何数据。SDK 初始化必须基于同意状态。
实施工作流程
选择同意流程图
分步实施
| 1 |
检查同意状态
启动应用程序时,在进行任何 SDK 操作前检查存储的同意首选项。
实施注意事项:
-
在持久存储(SharedPreferences、UserDefaults 等)中存储同意首选项。
-
在 AppDelegate 或 MainActivity 初始化前检查同意状态
- 默认为无同意(不假定有权限)
// Check consent status from storage
func hasUserConsent() -> Bool {
return UserDefaults.standard.bool(forKey: "singular_tracking_consent")
}
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Only initialize if consent granted
if hasUserConsent() {
initializeSingular()
} else {
// Show consent dialog
presentConsentDialog()
}
return true
}
// Check consent status from storage
fun hasUserConsent(): Boolean {
val prefs = getSharedPreferences("privacy_prefs", Context.MODE_PRIVATE)
return prefs.getBoolean("singular_tracking_consent", false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Only initialize if consent granted
if (hasUserConsent()) {
initializeSingular()
} else {
// Show consent dialog
showConsentDialog()
}
}
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Singular } from 'singular-react-native';
// Check consent status from storage
async function hasUserConsent() {
const consent = await AsyncStorage.getItem('singular_tracking_consent');
return consent === 'true';
}
async function initializeApp() {
const consentGranted = await hasUserConsent();
if (consentGranted) {
// Initialize Singular SDK
initializeSingular();
} else {
// Show consent dialog
showConsentDialog();
}
}
|
| 2 |
呈现同意界面
如果未存储同意首选项或未授予同意,则提供清晰的同意界面,解释数据收集做法。
同意界面要求:
-
清晰的语言:用通俗易懂的语言解释收集哪些数据以及收集的原因
-
明确的操作:用户必须采取肯定行动(而不是预先勾选方框
-
细化选项:如果法规要求,对不同数据类型分别征得同意
-
轻松拒绝:拒绝同意应与给予同意一样容易
-
隐私政策链接:链接到完整的隐私政策,了解详细信息
GDPR 要求:根据 GDPR,同意必须是自由、具体、知情和明确的。预选框或默示同意不符合要求。
|
| 3 |
处理同意授予
当用户同意时,存储偏好并以正常配置初始化 Singular SDK。
func userGrantedConsent() {
// Store consent preference
UserDefaults.standard.set(true, forKey: "singular_tracking_consent")
UserDefaults.standard.synchronize()
// Initialize Singular SDK
initializeSingular()
}
func initializeSingular() {
let config = SingularConfig(apiKey: "SDK_KEY", andSecret: "SDK_SECRET")
// Configure as needed
config.launchOptions = launchOptions
// Initialize SDK
Singular.start(config)
}
fun userGrantedConsent() {
// Store consent preference
val prefs = getSharedPreferences("privacy_prefs", Context.MODE_PRIVATE)
prefs.edit().putBoolean("singular_tracking_consent", true).apply()
// Initialize Singular SDK
initializeSingular()
}
fun initializeSingular() {
val config = SingularConfig("SDK_KEY", "SDK_SECRET")
// Configure as needed
// config.withLoggingEnabled()
// Initialize SDK
Singular.init(this, config)
}
async function userGrantedConsent() {
// Store consent preference
await AsyncStorage.setItem('singular_tracking_consent', 'true');
// Initialize Singular SDK
initializeSingular();
}
function initializeSingular() {
const config = {
apiKey: 'SDK_KEY',
secret: 'SDK_SECRET',
};
Singular.init(config);
}
|
| 4 |
处理拒绝同意
当用户拒绝同意时,存储偏好并不初始化 SDK。应用程序在不进行跟踪的情况下继续运行。
func userDeclinedConsent() {
// Store declined preference
UserDefaults.standard.set(false, forKey: "singular_tracking_consent")
UserDefaults.standard.synchronize()
// Do NOT initialize Singular SDK
// App continues without tracking
// Optional: Log declined consent for internal analytics
print("User declined tracking consent")
}
fun userDeclinedConsent() {
// Store declined preference
val prefs = getSharedPreferences("privacy_prefs", Context.MODE_PRIVATE)
prefs.edit().putBoolean("singular_tracking_consent", false).apply()
// Do NOT initialize Singular SDK
// App continues without tracking
// Optional: Log declined consent for internal analytics
Log.d("Privacy", "User declined tracking consent")
}
async function userDeclinedConsent() {
// Store declined preference
await AsyncStorage.setItem('singular_tracking_consent', 'false');
// Do NOT initialize Singular SDK
// App continues without tracking
// Optional: Log declined consent for internal analytics
console.log('User declined tracking consent');
}
|
| 5 |
提供同意撤销功能
即使通过 "选择进入 "方法授予同意,用户也应有权选择稍后撤销同意。在应用程序设置中实施退出机制。
法规要求:大多数隐私法规要求撤销同意与授予同意一样容易。在设置中实施 "选择退出 "方法,以便对同意进行持续管理。
有关撤销实施的详情,请参阅 "选择退出方法"部分。
|
COPPA 合规性
儿童隐私保护
针对儿童或混合受众的应用程序需要特别考虑 COPPA 和类似的儿童隐私法规。
年龄门实施
年龄验证要求
混合受众(儿童和成人)的应用程序必须在收集儿童数据前实施年龄门。
年龄门流程
-
提示年龄:在进行任何跟踪之前,在应用程序启动时要求输入用户年龄或出生日期
-
确定受众:确定用户是儿童(美国 13 岁以下)还是成人
-
儿童用户:对于儿童,在初始化 SDK 之前获得可验证的家长同意(将其视为有家长同意要求的 "选择进入")。
-
成人用户:对于成人用户,根据司法管辖区进行正常的 "选择进入 "或 "选择退出 "流程
家长同意方法
COPPA 接受几种可验证家长同意的方法:
- 家长提供信用卡信息
- 通过传真、电子邮件或邮寄签署同意书
- 家长拨打免费电话
- 家长与工作人员视频会议
-
家长提供经数据库验证的政府颁发的身份证件
- 回答基于知识的质疑问题
就适合您应用程序的家长同意方法咨询法律顾问。
只针对儿童的应用程序
儿童应用程序专用 SDK
对于专门针对儿童(非混合受众)的应用程序,Singular 提供专门的儿童应用程序 SDK,通过不捕获设备标识符来满足 COPPA 要求。
儿童应用程序 SDK:请查看儿童应用程序SDK常见问题,并联系您的客户团队,以获得关于如何以符合COPPA要求的方式使用Singular儿童应用程序的指导。
主要区别
儿童应用程序 SDK 与标准 SDK 不同:
- 不收集设备标识符(IDFA、GAID 等)
- 有限的归因功能(仅汇总)
- 无跨设备跟踪
-
符合 App Store 和 Play Store 儿童应用政策
退出方法
跟踪优先方法,正常初始化 SDK,但用户可随时通过应用设置或偏好设置控制停止跟踪。
选择退出实施
实施策略
选择退出方法允许在应用程序启动时初始化 SDK,同时提供机制让用户在需要时(通常通过设置界面)禁用跟踪。
实施工作流程
选择退出流程图
实施步骤
| 1 |
在应用程序启动时初始化 SDK
在应用程序启动时正常初始化 Singular SDK,默认启用跟踪功能。
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Check if user previously opted out
let hasOptedOut = UserDefaults.standard.bool(forKey: "singular_opted_out")
// Initialize Singular SDK
let config = SingularConfig(apiKey: "SDK_KEY", andSecret: "SDK_SECRET")
config.launchOptions = launchOptions
Singular.start(config)
// If previously opted out, stop tracking immediately
if hasOptedOut {
Singular.stopAllTracking()
}
return true
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Check if user previously opted out
val prefs = getSharedPreferences("privacy_prefs", Context.MODE_PRIVATE)
val hasOptedOut = prefs.getBoolean("singular_opted_out", false)
// Initialize Singular SDK
val config = SingularConfig("SDK_KEY", "SDK_SECRET")
Singular.init(this, config)
// If previously opted out, stop tracking immediately
if (hasOptedOut) {
Singular.stopAllTracking()
}
}
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Singular } from 'singular-react-native';
async function initializeApp() {
// Check if user previously opted out
const hasOptedOut = await AsyncStorage.getItem('singular_opted_out');
// Initialize Singular SDK
const config = {
apiKey: 'SDK_KEY',
secret: 'SDK_SECRET',
};
Singular.init(config);
// If previously opted out, stop tracking immediately
if (hasOptedOut === 'true') {
Singular.stopAllTracking();
}
}
|
| 2 |
提供退出机制
为用户创建清晰、易于访问的退出跟踪机制,通常是在应用程序设置或隐私偏好中。
退出界面要求:
-
易于访问:放在直观的位置(设置 → 隐私或类似位置
-
清晰描述:解释选择退出的含义以及对应用程序功能的影响
-
简单切换:只需一步切换或按钮即可禁用跟踪功能
-
立即生效:退出立即生效,无需重启应用程序
-
可逆:用户可以同样轻松地选择恢复跟踪
CCPA 要求:加州消费者隐私法》要求在显著位置显示 "请勿出售我的个人信息 "链接。 退出机制应清晰标注并易于访问。
|
| 3 |
处理退出操作
当用户选择退出时,调用Singular.stopAllTracking(),立即停止向 Singular 传输所有数据。
func userOptedOut() {
// Stop all tracking immediately
Singular.stopAllTracking()
// Store opt-out preference
UserDefaults.standard.set(true, forKey: "singular_opted_out")
UserDefaults.standard.synchronize()
// Optional: Show confirmation to user
showAlert(title: "Tracking Disabled",
message: "Your data will no longer be tracked.")
}
fun userOptedOut() {
// Stop all tracking immediately
Singular.stopAllTracking()
// Store opt-out preference
val prefs = getSharedPreferences("privacy_prefs", Context.MODE_PRIVATE)
prefs.edit().putBoolean("singular_opted_out", true).apply()
// Optional: Show confirmation to user
Toast.makeText(this, "Tracking disabled", Toast.LENGTH_SHORT).show()
}
async function userOptedOut() {
// Stop all tracking immediately
Singular.stopAllTracking();
// Store opt-out preference
await AsyncStorage.setItem('singular_opted_out', 'true');
// Optional: Show confirmation to user
Alert.alert('Tracking Disabled', 'Your data will no longer be tracked.');
}
stopAllTracking() 行为
调用stopAllTracking() 时:
- 立即停止所有事件跟踪
- 会话跟踪停止
- 不向 Singular 服务器发送数据
- 禁用归因跟踪
- 设置在应用程序会话中持续存在,直至被撤销
|
| 4 |
处理退出操作
当用户决定重新选择加入时,调用Singular.resumeAllTracking()以恢复正常跟踪操作。
func userOptedIn() {
// Resume all tracking
Singular.resumeAllTracking()
// Update opt-out preference
UserDefaults.standard.set(false, forKey: "singular_opted_out")
UserDefaults.standard.synchronize()
// Optional: Show confirmation to user
showAlert(title: "Tracking Enabled",
message: "Your app activity will now be tracked.")
}
fun userOptedIn() {
// Resume all tracking
Singular.resumeAllTracking()
// Update opt-out preference
val prefs = getSharedPreferences("privacy_prefs", Context.MODE_PRIVATE)
prefs.edit().putBoolean("singular_opted_out", false).apply()
// Optional: Show confirmation to user
Toast.makeText(this, "Tracking enabled", Toast.LENGTH_SHORT).show()
}
async function userOptedIn() {
// Resume all tracking
Singular.resumeAllTracking();
// Update opt-out preference
await AsyncStorage.setItem('singular_opted_out', 'false');
// Optional: Show confirmation to user
Alert.alert('Tracking Enabled', 'Your app activity will now be tracked.');
}
resumeAllTracking() 行为
调用resumeAllTracking() 时:
- 事件跟踪立即恢复
- 会话跟踪重新启动
- 启用向 Singular 服务器传输数据
- 重新激活归因跟踪
- 创建新会话以标记恢复
|
实施最佳实践
选择退出注意事项
设置放置
建议位置
将退出控制放在直观、易于访问的位置:
-
隐私设置:专门的隐私或数据设置部分
-
常规设置:主设置菜单,带有明确的隐私分节
-
账户设置用户帐户首选项区域
-
首次体验:在上机过程中提及可选择退出
清晰的沟通
透明度要求
明确告知退出意味着什么以及对功能的影响:
-
数据收集说明:解释收集哪些数据以及收集的目的
-
退出效果:说明用户选择退出时会发生什么情况
-
功能影响:披露退出是否会影响应用程序功能
-
可逆性:确认用户可以随时重新选择退出
偏好持久性
存储注意事项
跨应用程序会话可靠地存储退出首选项:
-
持久存储:使用 SharedPreferences(共享首选项)(Android)或 UserDefaults(用户默认设置)(iOS
-
启动时检查:每次启动应用时验证退出状态
-
尊重偏好:即使重新初始化 SDK,也尊重退出选择
-
备份存储:考虑备份到服务器以实现跨设备一致性
其他考虑因素
全面的隐私合规要求在实施基本的选择进入/选择退出之外,还要关注多个方面。
数据保留和删除
用户数据管理
隐私法规通常要求建立用户要求删除或导出数据的机制。
数据主体权利:联系Singular支持部门,就用户数据删除请求或GDPR、CCPA或其他隐私法规下的数据导出要求寻求帮助。
隐私政策
必要披露
维护全面、可访问的隐私政策,披露数据收集、使用和共享做法。
隐私政策要求:
-
收集的数据:列出收集的所有数据类型,包括设备标识符、事件和用户信息
-
使用目的:解释如何使用数据(分析、归因、广告等
-
第三方共享:披露与 Singular 及其他第三方共享数据的情况
-
用户权利:解释用户在数据访问、删除和退出方面的权利
-
联系信息:提供隐私问题和请求的联系方法
多司法管辖区合规性
全球隐私要求
服务于多个司法管辖区的应用程序必须同时遵守各种隐私法规。
| 方法 |
说明 |
|
基于地理位置 |
检测用户位置,并根据辖区应用适当的隐私控制(欧盟选择 "进入",加州选择 "退出 "等 |
|
最高标准 |
在全球范围内应用最严格的要求(通常是 GDPR 选择进入),简化合规性,但要付出即时跟踪的代价 |
|
用户选择 |
允许用户在入职过程中指定管辖区,应用适当的隐私控制措施 |
需要法律顾问:多辖区合规复杂,需要法律专业知识。请咨询隐私法律顾问,以确定适合您企业的方法。
测试隐私实施
验证核对表
隐私实施验证:
-
在授予同意之前未初始化 SDK(选择进入方法)
- stopAllTracking() 立即停止数据传输
- resumeAllTracking() 成功重启跟踪
- 退出偏好在应用程序会话中持续存在
- 同意界面清楚地解释了数据操作
- 可在设置中轻松访问退出机制
- 隐私政策全面易懂
- 针对混合受众应用程序实施了年龄门(COPPA)
- 退出时,测试控制台不显示任何事件
- 选择加入时,测试控制台显示事件恢复
其他资源
有关隐私合规性、SDK 实施和法规指导的完整文档。
奇异文档
隐私法规资源
法律免责声明
非法律建议:本指南中提供的信息代表公认的技术实施方法,但不构成法律建议。要确定隐私法律的完整解释及其如何适用于您的具体业务,请咨询您自己的隐私或法律团队。
不同司法管辖区的隐私法规各不相同,并在不断演变。请持续咨询法律顾问,以确保遵守适用法律。