GDRP и CCPA
The General Data Protection Regulation, более известный как GDPR, вступил в силу 25 Мая 2018 года. Это набор правил разработанный для жителей Европейского союза о получении контроля над сбором их персональных данных. Любые бизнесы, основанные в Европейском союзе или имеющие пользователей из Европейского союза, обязаны соответствовать закону о защите персональных данных (GDPR) во избежание связанных рисков. Закон California Consumer Privacy Act (CCPA) вступил в силу 1 Января 2020 года. Мы собрали вместе полезную информацию, для того, чтобы помочь вам лучше понять необходимые шаги для соответсвия вашего приложения требованиям GDRP.
Пожалуйста, обратите внимание, что лучше всего обратиться к квалифицированным юристам, чтобы получить дополнительную информацию и быть хорошо подготовленным к соблюдению требований. Вы можете ознакомиться детальнее в разделе GDPR и CCPA.
Шаг 1. Обновление Политики Конфиденциальности
1.1 Убедитесь, Что Ваша Политика Конфиденциальности Включает Информацию О Сборе Пользовательского Рекламного ID.
Не забудьте добавить информацию о сборе IP адреса и рекламного ID, так же как ссылку на политику конфиденциальности Appodeal в вашу политику конфиденциальности в App Store.
Для ускорения процесса вы можете воспользоваться генераторами политики конфиденциальности, где вам необходимо будет указать ID, IP адрес и локацию (если вы ее собираете) в поле "Personally Identifiable Information you collect" (в поле с дополнительной информацией о вашем приложении) а так же как ссылку на политику конфиденциальности Appodeal в поле "Link to the privacy policy of third party service providers used by the app".
1.2 Добавьте Политику Конфиденциальности В Ваше Мобильное Приложение.
Вам необходимо явно добавить ссылку на вашу политику конфиденциальности в два места: на страницу приложения в app's Store и в ваше приложение.
Вы можете найти подробные инструкции о добавлении политики конфиденциальности в ваше приложения на официальных ресурсах, например: Iubenda - решение, адаптированное к требованиям законодательства, которое содержит подробное руководство по включению политики конфиденциальности в ваше приложение.
Убедитесь что ваш сайт с политикой конфиденциальности содержит SSL-сертификат — это пункт может казаться очевидным, но это важно.
Ниже вы можете найти две ссылки на ресурсы, которые вы можете использовать во время работы над соответствием вашего приложения:
- App privacy details on the App Store
- Recommendations on Developing a Meaningful Privacy Policy (by Attorney General California Department of Justice)
Пожалуйста, обратите внимание, что, хотя мы всегда готовы предоставить вам ценную информацию, мы не уполномочены предоставлять какие-либо юридические консультации.
Шаг 2. Stack Consent Manager
В соответствии с Appodeal и рекламными сетями в предоставлении наиболее подходящей мобильной рекламы конечному пользователю, вам необходимо запрашивать соглашение в регионах GDPR и CCPA.
Чтобы получить согласие на сбор персональных данных ваших пользователей, мы предлагаем воспользоваться готовым решением - Stack Consent Manager.
Библиотека Stack Consent Manager включает разработанное окно согласия, показ которого вы можете предоставлять конечному пользователю. Это означает что вам больше не требуется создавать данное окно согласия самостоятельно.
Начиная с Appodeal SDK 3.0, Stack Consent Manager включен в состав Appodeal SDK по умолчанию.
Согласие будет запрошено автоматически при инициализации SDK, и форма согласия будет показана, если это необходимо, без дополнительных вызовов.
Имейте в виду, что Согласие будет отображаться только в регионах ЕС и Калифорнии, вы можете использовать VPN для тестирования.
- Swift
- Objective-C
@UIApplicationMain
final class MyAppDelegate: UIResponder, UIApplicationDelegate, AppodealInitializationDelegate {
func application(
_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool {
Appodeal.setAutocache(false, types: .interstitial)
Appodeal.setLogLevel(.verbose)
// New optional delegate for initialization completion
Appodeal.setInitializationDelegate(self)
/// Any other pre-initialization
/// app specific logic
Appodeal.initialize(
withApiKey: "APP_KEY",
types: .interstitial
)
return true
}
func appodealSDKDidInitialize() {
// Appodeal SDK did complete initialization
}
}
@interface MyAppDelegate ()
<AppodealInitializationDelegate>
@end
@implementation MyAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Appodeal setAutocache:NO types:AppodealAdTypeInterstitial];
[Appodeal setLogLevel:APDLogLevelVerbose];
// New optional delegate for initialization completion
[Appodeal setInitializationDelegate:self];
/// Any other pre-initialization
/// app specific logic
[Appodeal initializeWithApiKey:@"APP KEY" types:AppodealAdTypeInterstitial];
return YES;
}
- (void)appodealSDKDidInitialize {
// Appodeal SDK did complete initialization
}
@end
Дополнительно
Stack Consent Manager включен в состав Appodeal SDK по умолчанию. Согласие будет запрошено автоматически при инициализации SDK, и форма согласия будет показана, если это необходимо без дополнительных вызовов.
Вы по-прежнему можете использовать свой собственный Consent Manager или настроить Stack Consent Manager, выполнив описанные ниже действия.
Обновление Решения Пользователя
Если вы хотите дать возможность пользователям изменить согласие на сбор персональных данных, то вы можете обновить состояние Consent используя данные методы.
Эти методы должны быть вызваны перед инициализацией Appodeal SDK. Значение согласия не может быть изменено во время выполнения. В качестве опции вы можете сохранить решение пользователя в UserDefaults и обновить решение о согласии при следующем запуске приложения.
- Swift
- Objective-C
Appodeal.updateUserConsentGDPR(.personalized)
Appodeal.updateUserConsentCCPA(.optIn)
[Appodeal updateUserConsentGDPR:APDGDPRUserConsentPersonalized];
[Appodeal updateUserConsentCCPA:APDCCPAUserConsentOptIn];
Используйте следующие значения, чтобы задать согласие пользователя для GDPR и CCPA:
- APDGDPRUserConsent.unknown - статус неизвестен. Окно согласия не
- отображалось.
- APDGDPRUserConsent.personalized - пользователь дал согласие на персонализированную рекламу.
- APDGDPRUserConsent.nonPersonalized - пользователь НЕ давал согласия на персонализированную рекламу.
- APDCCPAUserConsent.optIn - пользователь дал согласие на персонализированную рекламу.
- APDCCPAUserConsent.optOut - пользователь НЕ давал согласия на персонализированную рекламу.
Вы можете использовать эти методы, чтобы предоставить согласие пользователя GDPR/CCPA для рекламных сетей в Appodeal SDK. Дополнительную информацию о GDPR/CCPA можно узнать здесь.
Управление Stack Consent
Stack Consent Manager включен в Appodeal SDK по умолчанию. Согласие будет запрошено автоматически при инициализации SDK, и форма согласия будет показана, если это необходимо, без каких-либо дополнительных вызовов.
При желании вы можете управлять согласием и обновлять его вручную, используя методы Stack Consent Manager.
Обновление Статуса Согласия
Stack Consent Manager можно синхронизировать в любой момент жизненного цикла приложения. Рекомендуем синхронизировать его при запуске приложения. Допускается несколько вызовов синхронизации.
Импортируйте StackConsentManager/StackConsentManager.h
в AppDelegate.m
- Swift
- Objective-C
import StackConsentManager
#import <StackConsentManager/StackConsentManager.h>
Запустите синхронизацию Consent в -application:didFinishLaunchingWithOptions:
- Swift
- Objective-C
/// Initialisation
class YourAppDelegate: AppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?
) -> Bool {
STKConsentManager.shared().synchronize(withAppKey: "Your app key") { [unowned self] error in
error.map { print("Error while synchronising consent manager: \($0)") }
guard STKConsentManager.shared().shouldShowConsentDialog == .true else {
// Initialise SDK here
return
}
// Load and present consent dialog
}
return true
}
}
/// Initialisation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
__weak typeof(self) weakSelf = self;
[STKConsentManager.sharedManager synchronizeWithAppKey:APP_KEY completion:^(NSError *error) {
__strong typeof(self) strongSelf = weakSelf;
if (error) {
NSLog(@"Error while synchronising consent manager: %@", error);
}
if (STKConsentManager.sharedManager.shouldShowConsentDialog != STKConsentBoolTrue) {
// Initialise SDK here
return ;
}
// Load and present consent dialog
}];
return YES;
}
APP_KEY
- обязательный параметр (Appodeal APP Key)
completion
- это
блок, который вызывается после синхронизации
После завершения синхронизации вы можете получить информацию о предыдущем согласии пользователя и зоне регулирования. До синхронизации эти параметры не определены
- Swift
- Objective-C
// Check regulation
let regulation = STKConsentManager.shared().regulation
// Check consent status
let status = STKConsentManager.shared().consentStatus
// Available after first show (synchronisation is required)
let consentString = STKConsentManager.shared().iabConsentString
// Check regulation
STKConsentRegulation *regulation = [STKConsentManager.sharedManager regulation];
// Check consent status
STKConsentStatus *status = [STKConsentManager.sharedManager consentStatus];
// Available after first show (synchronisation is required)
NSString *consentString = [STKConsentManager.sharedManager iabConsentString];
SDK позволяет вызывать api Сonsent окна только после синхронизации
После синхронизации SDK вы можете загрузить Сonsent окно. Загрузка разрешена в любой зоне регулирования и независимо от предварительного согласия.
- Swift
- Objective-C
// Load and present consent dialog
STKConsentManager.shared().loadConsentDialog { [unowned self] error in
error.map { print("Error while loading consent dialog: \($0)") }
guard
let controller = UIApplication.shared.keyWindow?.rootViewController,
STKConsentManager.shared().isConsentDialogReady
else {
// Initialise SDK here
return
}
STKConsentManager.shared().showConsentDialog(fromRootViewController: controller,
delegate: self)
}
// Load and present consent dialog
__weak typeof(self) weakSelf = self;
[STKConsentManager.sharedManager loadConsentDialog:^(NSError *error) {
__strong typeof(self) strongSelf = weakSelf;
if (error) {
NSLog(@"Error while loading consent dialog: %@", error);
}
if (!STKConsentManager.sharedManager.isConsentDialogReady) {
// Initialise SDK here
return ;
}
UIViewController *rootViewController = [UIApplication.sharedApplication keyWindow].rootViewController;
[STKConsentManager.sharedManager showConsentDialogFromRootViewController:rootViewController
delegate:strongSelf];
}];
Показ окна согласия
Вы можете проверить, готово ли окно согласия.
- Swift
- Objective-C
// Indicates that consent window ready to present
let isReady = STKConsentManager.shared().isConsentDialogReady
// Indicates that consent window ready to present
BOOL isReady = [STKConsentManager.sharedManager isConsentDialogReady];
После проверки готовности окна согласия вы можете его показать.
- Swift
- Objective-C
// Show consent dialog
STKConsentManager.shared().showConsentDialog(fromRootViewController: controller, delegate: self)
// Show consent dialog
[STKConsentManager.sharedManager showConsentDialogFromRootViewController:rootViewController delegate:strongSelf];
Обработка функций обратных вызовов показа.
- Swift
- Objective-C
/// Get consent manager presentation callbacks
class YourViewController: UIViewController, STKConsentManagerDisplayDelegate {
// MARK: STKConsentManagerDisplayDelegate
func consentManagerWillShowDialog(_ consentManager: STKConsentManager) {}
func consentManager(_ consentManager: STKConsentManager, didFailToPresent error: Error) {
// Something went wrong
initializeAppodealSDK()
}
func consentManagerDidDismissDialog(_ consentManager: STKConsentManager) {
// Resume app
initializeAppodealSDK()
}
}
/// Get consent manager presentation callbacks
@interface YourViewController() <STKConsentManagerDisplayDelegate>
@end
@implementation YourViewController
// MARK: STKConsentManagerDisplayDelegate
- (void)consentManagerWillShowDialog:(STKConsentManager *)consentManager {}
- (void)consentManagerDidDismissDialog:(STKConsentManager *)consentManager {
// Something went wrong
[self initializeAppodealSDK];
}
- (void)consentManager:(STKConsentManager *)consentManager didFailToPresent:(NSError *)error {
// Resume app
[self initializeAppodealSDK];
}
@end
Обновление статуса согласия
Издателям необходимо передать объект Consent result из SDK Stack Consent
Manager в метод Appodeal.updateConsentReport(report)
перед
инициализацией SDK.
- Swift
- Objective-C
let report = STKConsentManager.shared().consent!
Appodeal.updateConsentReport(report)
Appodeal.initialize(withApiKey: appKey, types: adTypes)
id<STKConsent> report = STKConsentManager.sharedManager.consent;
[Appodeal updateConsentReport:report];
[Appodeal initializeWithApiKey:APP_KEY types:types];
Дополнительно
Вы можете заставить менеджер согласия записывать ключи iAB в
NSUserDefaults
, настроив свойство хранилища перед синхронизацией с
STKConsentDialogStorageUserDefaults
.
SDK не удаляет ключи iAB из NSUserDefaults, а только переопределяет их.
- Swift
- Objective-C
// Store IAB strings in user defaults
// should be called before synchronize
STKConsentManager.shared().storage = .userDefaults
// Store IAB strings in user defaults
// should be called before synchronize
STKConsentManager.sharedManager.storage = STKConsentDialogStorageUserDefaults;
Перед синхронизацией вы можете зарегистрировать себя как вендора.
Parameter | Type | Description |
---|---|---|
id | Integer | iAB id. If you are not registered as iAB vendor you can use custom id |
name | String | Native ad Display name. Will be displayed in the consent window |
purposesIds | Array of integersring | iAB purposes ids array |
featuresIds | Array of integers | iAB features ids array |
legIntPurposeIds | Array of integers | iAB leg int purposes ids array |
- Swift
- Objective-C
// Register custom vendor (will be displayed on consent window)
// should be called before synchronize
STKConsentManager.shared().registerCustomVendor { builder in
let _ = builder
.appendPolicyURL(URL(string: "https://cmg.com/privacy")!)
.appendName("My app")
.appendBundle("com.app.bundle")
.appendPurposesIds([1, 2, 3])
.appendFeaturesIds([5, 6])
.appendLegIntPurposeIds([1])
}
// Register custom vendor (will be displayed on consent window)
// should be called before synchronize
[STKConsentManager.sharedManager registerCustomVendor:^(STKVendorBuilder * _Nonnull builder) {
builder
.appendPolicyURL([NSURL URLWithString:@"https://cmg.com/privacy"])
.appendName(@"My app")
.appendBundle(@"com.app.bundle")
.appendPurposesIds(@[@1, @2, @3])
.appendFeaturesIds(@[@5, @6])
.appendLegIntPurposeIds(@[@1]);
}];
Пример
- Swift
- Objective-C
import UIKit
import Appodeal
import StackConsentManager
@main
class AppDelegate: UIResponder, UIApplicationDelegate, AppodealInitializationDelegate, STKConsentManagerDisplayDelegate {
let appKey = "Your app key"
let adTypes:AppodealAdType = .banner
func appodealSDKDidInitialize() {}
func consentManagerWillShowDialog(_ consentManager: STKConsentManager) {
Appodeal.initialize(withApiKey: appKey, types: adTypes)
}
func consentManager(_ consentManager: STKConsentManager, didFailToPresent error: Error) {
Appodeal.initialize(withApiKey: appKey, types: adTypes)
}
func consentManagerDidDismissDialog(_ consentManager: STKConsentManager) {
let report = STKConsentManager.shared().consent!
Appodeal.updateConsentReport(report)
Appodeal.initialize(withApiKey: appKey, types: adTypes)
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Appodeal.setInitializationDelegate(self)
// Synchronize Consent manager
STKConsentManager.shared().synchronize(withAppKey: appKey) { [unowned self] error in
error.map { print("Error while synchronising consent manager: \($0)") }
guard STKConsentManager.shared().shouldShowConsentDialog == .true else {
Appodeal.initialize(withApiKey: appKey, types: adTypes)
return
}
// Load and present consent dialog
STKConsentManager.shared().loadConsentDialog { [unowned self] error in
error.map { print("Error while loading consent dialog: \($0)") }
guard
let controller = UIApplication.shared.keyWindow?.rootViewController,
STKConsentManager.shared().isConsentDialogReady
else {
Appodeal.initialize(withApiKey: appKey, types: adTypes)
return
}
// Show consent dialog
STKConsentManager.shared().showConsentDialog(fromRootViewController: controller,
delegate: self)
}
}
return true
}
// ...
}
#import "AppDelegate.h"
#import <Appodeal/Appodeal.h>
#import <StackConsentManager/StackConsentManager.h>
@interface AppDelegate ()
<AppodealInitializationDelegate, STKConsentManagerDisplayDelegate>
@property (strong, nonatomic) NSString *appKey;
@property (assign, nonatomic) AppodealAdType adTypes;
@end
@implementation AppDelegate
- (void)appodealSDKDidInitialize {}
- (void)consentManagerWillShowDialog:(STKConsentManager *)consentManager {}
- (void)consentManagerDidDismissDialog:(STKConsentManager *)consentManager {
// Resume app
[Appodeal updateConsentReport:[STKConsentManager sharedManager].consent];
[Appodeal initializeWithApiKey:_appKey types:_adTypes];
}
- (void)consentManager:(STKConsentManager *)consentManager didFailToPresent:(NSError *)error {
// Something went wrong
[Appodeal initializeWithApiKey:_appKey types:_adTypes];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
__weak typeof(self) weakSelf = self;
[Appodeal setInitializationDelegate:self];
self.appKey = @"Your app key";
self.adTypes = AppodealAdTypeBanner;
[STKConsentManager.sharedManager synchronizeWithAppKey:_appKey completion:^(NSError *error) {
if (error) {
NSLog(@"Error while synchronising consent manager: %@", error);
}
if (STKConsentManager.sharedManager.shouldShowConsentDialog != STKConsentBoolTrue) {
// Initialise SDK here
[Appodeal initializeWithApiKey:_appKey types:_adTypes];
return;
}
// Load and present consent dialog
[STKConsentManager.sharedManager loadConsentDialog:^(NSError *error) {
__strong typeof(self) strongSelf = weakSelf;
if (error) {
NSLog(@"Error while loading consent dialog: %@", error);
}
if (!STKConsentManager.sharedManager.isConsentDialogReady) {
[Appodeal initializeWithApiKey:_appKey types:_adTypes];
return ;
}
// Show consent dialog
UIViewController *rootViewController = [UIApplication.sharedApplication keyWindow].rootViewController;
[STKConsentManager.sharedManager showConsentDialogFromRootViewController:rootViewController
delegate:strongSelf];
}];
}];
return YES;
}
Свое Окно Согласия
Если вы не хотите использовать Stack Content Manager SDK, вы можете использовать другое решение, чтобы получить согласие пользователя и предоставить его перед инициализацией Appodeal SDK:
Издателям необходимо передать согласие перед вызовом метода инициализации Appodeal SDK, используя следующие методы.
- Swift
- Objective-C
Appodeal.updateUserConsentGDPR(.personalized)
Appodeal.updateUserConsentCCPA(.optIn)
[Appodeal updateUserConsentGDPR:APDGDPRUserConsentPersonalized];
[Appodeal updateUserConsentCCPA:APDCCPAUserConsentOptIn];
Используйте следующие значения, чтобы задать согласие пользователя для GDPR и CCPA:
- APDGDPRUserConsent.unknown - статус неизвестен. Окно согласия не отображалось.
- APDGDPRUserConsent.personalized - пользователь дал согласие на персонализированную рекламу.
- APDGDPRUserConsent.nonPersonalized - пользователь НЕ давал согласия на персонализированную рекламу.
- APDCCPAUserConsent.optIn - пользователь дал согласие на персонализированную рекламу.
- APDCCPAUserConsent.optOut - пользователь НЕ давал согласия на персонализированную рекламу.
Предоставление значений согласия перед инициализацией предотвращает отображение встроенного Stack Consent Window. Необходимо использовать этот подход, чтобы отключить окно согласия, отображаемое в зонах GDPR/CCPA, или если вы хотите, чтобы пользователь мог изменить свое мнение о значении Согласия.