GDPR Compliance for Mobile Apps
GDPR Compliance for Mobile Apps
The General Data Protection Regulation sets strict requirements for handling EU citizens' personal data, with significant penalties for non-compliance.
GDPR Implementation for iOS:
// iOS - GDPR compliance implementation
import UIKit
import CoreData
class GDPRComplianceManager {
static let shared = GDPRComplianceManager()
private let consentStorage = ConsentStorage()
// GDPR Article 6 - Lawful basis for processing
enum LawfulBasis {
case consent
case contract
case legalObligation
case vitalInterests
case publicTask
case legitimateInterests
}
// GDPR Article 7 - Consent management
class ConsentManager {
struct ConsentRequest {
let purpose: String
let dataCategories: [DataCategory]
let processingActivities: [ProcessingActivity]
let retentionPeriod: TimeInterval
let thirdPartySharing: Bool
let internationalTransfers: Bool
}
func requestConsent(
for request: ConsentRequest,
completion: @escaping (ConsentResult) -> Void
) {
// Create clear, specific consent request
let consentViewController = ConsentViewController()
consentViewController.configure(with: ConsentViewModel(
title: "Data Processing Consent",
purpose: request.purpose,
dataCategories: request.dataCategories.map { $0.description },
processingDetails: generateProcessingDescription(request),
retentionInfo: "Your data will be retained for \(formatRetentionPeriod(request.retentionPeriod))",
rightsInfo: generateRightsInformation()
))
consentViewController.onConsent = { granted in
if granted {
self.recordConsent(for: request)
}
completion(ConsentResult(granted: granted, timestamp: Date()))
}
presentConsentUI(consentViewController)
}
private func recordConsent(for request: ConsentRequest) {
let consent = ConsentRecord(
id: UUID().uuidString,
timestamp: Date(),
purpose: request.purpose,
dataCategories: request.dataCategories,
version: "1.0",
withdrawable: true
)
consentStorage.save(consent)
// Audit log for compliance
AuditLogger.log(event: .consentGranted(consent))
}
// GDPR Article 7(3) - Withdrawal of consent
func withdrawConsent(for consentId: String) {
guard let consent = consentStorage.getConsent(id: consentId) else { return }
// Stop processing immediately
DataProcessingEngine.shared.stopProcessing(for: consent.dataCategories)
// Delete associated data if required
if consent.requiresDeletion {
deleteUserData(categories: consent.dataCategories)
}
// Record withdrawal
consentStorage.markWithdrawn(consentId: consentId)
AuditLogger.log(event: .consentWithdrawn(consentId))
}
}
// GDPR Article 15 - Right of access
func handleDataAccessRequest(userId: String) async throws -> DataAccessResponse {
var collectedData: [String: Any] = [:]
// Gather all user data
collectedData["profile"] = try await ProfileDataStore.getData(for: userId)
collectedData["activity"] = try await ActivityLogger.getLogs(for: userId)
collectedData["preferences"] = try await PreferencesStore.getPreferences(for: userId)
collectedData["consents"] = consentStorage.getConsents(for: userId)
// Include processing information
let processingInfo = ProcessingInfo(
purposes: getProcessingPurposes(),
recipients: getDataRecipients(),
retentionPeriods: getRetentionPeriods(),
sources: getDataSources()
)
// Generate portable format (JSON)
let portableData = try JSONEncoder().encode(
PortableDataFormat(
userData: collectedData,
processingInfo: processingInfo,
generatedAt: Date()
)
)
return DataAccessResponse(
data: portableData,
format: .json,
checksum: calculateChecksum(portableData)
)
}
// GDPR Article 17 - Right to erasure (Right to be forgotten)
func handleDeletionRequest(userId: String) async throws {
// Verify identity
guard try await verifyUserIdentity(userId) else {
throw GDPRError.identityVerificationFailed
}
// Check for legal obligations to retain data
let retentionObligations = checkRetentionObligations(userId)
if retentionObligations.isEmpty {
// Complete deletion
try await performCompleteDeletion(userId)
} else {
// Partial deletion with anonymization
try await performPartialDeletion(userId, retaining: retentionObligations)
}
// Notify third parties
await notifyThirdPartiesOfDeletion(userId)
// Audit log
AuditLogger.log(event: .dataDeleted(userId, Date()))
}
// GDPR Article 25 - Data protection by design and by default
class PrivacyByDesign {
static func configureDataMinimization() {
// Collect only necessary data
UserDataCollector.shared.configure(
collectLocation: false,
collectDeviceId: false,
collectAnalytics: .anonymized,
collectCrashReports: .optIn
)
// Set privacy-friendly defaults
UserDefaults.standard.register(defaults: [
"analytics_enabled": false,
"personalization_enabled": false,
"third_party_sharing": false
])
}
static func implementPseudonymization() {
// Replace identifiers with pseudonyms
DatabaseManager.shared.enablePseudonymization(
fields: ["email", "name", "phone"],
algorithm: .hmacSHA256,
key: getDerivedKey(purpose: "pseudonymization")
)
}
static func enforceDataRetention() {
// Automatic data expiration
DataRetentionPolicy.configure(
userProfiles: .years(2),
activityLogs: .months(6),
crashReports: .days(30),
marketingData: .days(90)
)
}
}
// GDPR Article 33 - Breach notification
class BreachNotificationHandler {
func handleDataBreach(_ breach: DataBreach) {
// Assess the breach
let assessment = assessBreach(breach)
if assessment.requiresNotification {
// Notify supervisory authority within 72 hours
notifySupervisoryAuthority(breach, assessment)
if assessment.highRiskToIndividuals {
// Notify affected individuals
notifyAffectedUsers(breach, assessment)
}
}
// Document the breach
documentBreach(breach, assessment)
}
private func assessBreach(_ breach: DataBreach) -> BreachAssessment {
return BreachAssessment(
severity: calculateSeverity(breach),
affectedUsers: breach.affectedUserCount,
dataTypes: breach.compromisedDataTypes,
requiresNotification: breach.compromisedDataTypes.contains(.sensitive),
highRiskToIndividuals: breach.severity == .high,
mitigationMeasures: determineMitigationMeasures(breach)
)
}
}
}
Android GDPR Implementation:
// Android - GDPR compliance implementation
class GDPRComplianceManager(private val context: Context) {
private val consentManager = ConsentManager(context)
private val dataController = DataController(context)
// Consent collection with granular options
suspend fun collectConsent(): ConsentResult {
val consentOptions = ConsentOptions(
purposes = listOf(
ConsentPurpose(
id = "analytics",
name = "Analytics",
description = "Help us improve the app by collecting usage data",
required = false,
dataTypes = listOf("app_usage", "crash_reports")
),
ConsentPurpose(
id = "marketing",
name = "Marketing",
description = "Receive personalized offers and recommendations",
required = false,
dataTypes = listOf("preferences", "purchase_history")
),
ConsentPurpose(
id = "essential",
name = "Essential Services",
description = "Required for app functionality",
required = true,
dataTypes = listOf("account_data", "app_settings")
)
)
)
return consentManager.requestConsent(consentOptions)
}
// Privacy dashboard implementation
class PrivacyDashboard : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
PrivacyDashboardScreen()
}
}
}
@Composable
fun PrivacyDashboardScreen() {
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
Text(
"Privacy Settings",
style = MaterialTheme.typography.h4
)
Spacer(modifier = Modifier.height(16.dp))
// Consent management
ConsentManagementCard()
// Data access
DataAccessCard()
// Data portability
DataPortabilityCard()
// Account deletion
AccountDeletionCard()
}
}
@Composable
fun ConsentManagementCard() {
Card(
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Manage Consents", style = MaterialTheme.typography.h6)
val consents = remember { consentManager.getActiveConsents() }
consents.forEach { consent ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(consent.purpose)
Switch(
checked = consent.granted,
onCheckedChange = { granted ->
if (granted) {
consentManager.grantConsent(consent.id)
} else {
consentManager.withdrawConsent(consent.id)
}
}
)
}
}
}
}
}
}
// Data portability implementation
class DataPortabilityManager(private val context: Context) {
suspend fun exportUserData(format: ExportFormat): Uri {
val userData = collectAllUserData()
return when (format) {
ExportFormat.JSON -> exportAsJson(userData)
ExportFormat.CSV -> exportAsCsv(userData)
ExportFormat.XML -> exportAsXml(userData)
}
}
private suspend fun collectAllUserData(): UserDataPackage {
return UserDataPackage(
profile = getProfileData(),
activities = getActivityData(),
preferences = getPreferences(),
consents = getConsentHistory(),
generatedAt = System.currentTimeMillis()
)
}
private suspend fun exportAsJson(data: UserDataPackage): Uri {
val gson = GsonBuilder()
.setPrettyPrinting()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
.create()
val json = gson.toJson(data)
// Save to secure location
val file = File(context.getExternalFilesDir(null), "user_data_export.json")
val encryptedFile = EncryptedFile.Builder(
context,
file,
MasterKey.Builder(context).build(),
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
encryptedFile.openFileOutput().use { output ->
output.write(json.toByteArray())
}
return FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
file
)
}
}
}