Implementing Right to Data Portability (Export) in Mobile Apps
GDPR Article 20 and App Store Review Guideline 5.1.1 require providing user copy of their data in machine-readable format. Apple since 2022 actively checks this during app review with personal data. Missing export function — real reason for rejection.
What to Include in Export
Minimum per GDPR: all data user provided directly (profile, settings, content) and data generated by service use (action history, transactions, preferences).
Not mandatory: derivative data (analytics aggregates, ML models), technical logs (server access logs), other users' data.
Formats: JSON preferred for machine-readability, CSV for users wanting Excel. ZIP archive with multiple files — standard practice (like Google Takeout).
Server-Side Export Implementation
Export potentially heavy operation. Don't do synchronous HTTP response:
POST /api/user/export-request
→ 202 Accepted { "job_id": "exp_xxxx", "estimated_minutes": 5 }
GET /api/user/export-request/exp_xxxx
→ 200 { "status": "processing" | "ready", "download_url": "...", "expires_at": "..." }
Background task (Celery, Sidekiq, Laravel Queue) collects from all tables, builds archive, uploads to S3/storage with presigned URL for 24–72 hours. After completion — push notification or email.
Presigned URL with TTL critical: don't give direct S3 links without auth — data leak.
Client Flow
// iOS — request export and poll status
class DataExportViewModel: ObservableObject {
@Published var exportState: ExportState = .idle
func requestExport() async {
exportState = .requesting
let job = try await api.requestDataExport()
exportState = .processing(jobID: job.id)
await pollStatus(jobID: job.id)
}
private func pollStatus(jobID: String) async {
while true {
try? await Task.sleep(nanoseconds: 30_000_000_000) // 30 seconds
let status = try await api.getExportStatus(jobID: jobID)
if status.isReady {
exportState = .ready(downloadURL: status.downloadURL!)
return
}
}
}
}
When ready — offer user save file via UIDocumentPickerViewController (iOS) or ActivityResultContracts.CreateDocument (Android). Don't auto-save to Documents without consent.
Request Frequency Limit
User shouldn't request export every 5 minutes — DB load. Reasonable limit: one request per 24–48 hours. Show last export date and time until next one possible.
Timeline — 1–3 days: server export queue, data collection from all sources, archive building, client UI with polling and download.







