- name
- consent-flow
- description
- Generates GDPR/CCPA/DPDP privacy consent flows with granular category preferences, consent state persistence, audit logging, and ATT (App Tracking Transparency) integration. Use when user needs privacy consent UI, cookie/tracking consent, or compliance management.
- allowed-tools
- ["Read","Write","Edit","Glob","Grep","Bash","AskUserQuestion"]
- last_verified
- 2026-07-16T00:00:00.000Z
- review_by
- 2027-06-22T00:00:00.000Z
- os_version
- iOS 27 / macOS 27
# Consent Flow Generator
Generate a production privacy consent system with granular category-based consent, persistent state management, a consent banner and preferences UI, audit logging for compliance, and App Tracking Transparency integration.
## When This Skill Activates
Use this skill when the user:
- Asks about "privacy consent" or "consent management"
- Mentions "GDPR consent" or "GDPR compliance"
- Wants "cookie consent" or "tracking consent"
- Mentions "ATT prompt" or "App Tracking Transparency"
- Asks for "privacy preferences" or "consent preferences"
- Mentions "CCPA compliance" or "DPDP compliance"
- Wants to "manage user consent" or "consent banner"
- Asks about "consent audit log" or "consent records"
## Pre-Generation Checks
### 1. Project Context Detection
- [ ] Check Swift version (requires Swift 5.9+)
- [ ] Check deployment target (iOS 16+ / macOS 13+)
- [ ] Check for @Observable support (iOS 17+ / macOS 14+)
- [ ] Identify source file locations
### 2. Conflict Detection
Search for existing consent or privacy code:
```
Glob: **/*Consent*.swift, **/*Privacy*.swift, **/*Tracking*.swift, **/*GDPR*.swift
Grep: "ATTrackingManager" or "ConsentManager" or "trackingAuthorizationStatus"
```
If third-party library found (OneTrust, Usercentrics, CookieBot):
- Ask if user wants to replace or keep it
- If keeping, don't generate — advise on integration best practices instead
### 3. ATT Framework Availability
Check for App Tracking Transparency framework:
```
Grep: "AppTrackingTransparency" in project files
Grep: "NSUserTrackingUsageDescription" in Info.plist
```
If `NSUserTrackingUsageDescription` is missing from Info.plist, warn the user that ATT requires this key and offer to add it.
### 4. Platform Detection
Determine if generating for iOS (primary ATT target) or macOS (ATT not applicable) or both.
## Configuration Questions
Ask user via AskUserQuestion:
1. **Target regulations?**
- GDPR only (EU — opt-in model)
- CCPA only (California — opt-out model)
- DPDP only (India — consent-based)
- All regulations (recommended for global apps)
2. **Consent categories?** (multi-select)
- Essential (always on, cannot be disabled)
- Analytics (usage tracking, crash reporting)
- Marketing (advertising, attribution)
- Personalization (recommendations, content tailoring)
- Functional (preferences, saved settings beyond essential)
3. **Include ATT integration?**
- Yes — request ATT permission before any tracking (recommended for iOS)
- No — handle consent without ATT (macOS, or no IDFA usage)
4. **Consent UI style?**
- Bottom banner with manage preferences (recommended)
- Full-screen consent view (for first launch)
- Settings-embedded (preferences in app settings, no banner)
- Banner + Settings (banner on first launch, preferences in settings)
## Generation Process
### Step 1: Read Templates
Read `templates.md` for production Swift code.
Read `patterns.md` for compliance rules, regulation differences, and UX guidance.
### Step 2: Create Core Files
Generate these files:
1. `ConsentCategory.swift` — Enum of consent categories with metadata
2. `ConsentDecision.swift` — Per-category consent state with timestamp
3. `ConsentManager.swift` — @Observable manager with persistence and ATT integration
4. `ConsentAuditLog.swift` — Compliance audit trail with JSON export
### Step 3: Create UI Files
5. `ConsentBannerView.swift` — Animated slide-up consent banner
6. `ConsentPreferencesView.swift` — Detailed toggle list for each category
### Step 4: Create Optional Files
Based on configuration:
- `ConsentRegulationConfig.swift` — If multiple regulations selected
- `ConsentATTBridge.swift` — If ATT integration selected (extracted for testability)
### Step 5: Determine File Location
Check project structure:
- If `Sources/` exists → `Sources/Consent/`
- If `App/` exists → `App/Consent/`
- Otherwise → `Consent/`
## Output Format
After generation, provide:
### Files Created
```
Consent/
├── ConsentCategory.swift # Consent category enum with metadata
├── ConsentDecision.swift # Per-category decision with timestamp
├── ConsentManager.swift # @Observable manager with persistence
├── ConsentAuditLog.swift # Compliance audit trail
├── ConsentBannerView.swift # Animated consent banner
├── ConsentPreferencesView.swift # Granular preferences UI
├── ConsentRegulationConfig.swift # Multi-regulation rules (optional)
└── ConsentATTBridge.swift # ATT integration bridge (optional)
```
### Integration Steps
**Show consent on first launch:**
```swift
@main
struct MyApp: App {
@State private var consentManager = ConsentManager()
var body: some Scene {
WindowGroup {
ContentView()
.environment(consentManager)
.overlay(alignment: .bottom) {
if consentManager.needsConsent {
ConsentBannerView()
.environment(consentManager)
.transition(.move(edge: .bottom).combined(with: .opacity))
}
}
.animation(.easeInOut(duration: 0.3), value: consentManager.needsConsent)
}
}
}
```
**Check consent before tracking:**
```swift
func trackEvent(_ event: AnalyticsEvent) {
guard consentManager.hasConsent(for: .analytics) else { return }
analyticsService.track(event)
}
func showPersonalizedAd() {
guard consentManager.hasConsent(for: .marketing) else {
showGenericAd()
return
}
adService.showPersonalized()
}
```
**Open preferences from settings:**
```swift
NavigationLink("Privacy Preferences") {
ConsentPreferencesView()
.environment(consentManager)
}
```
**Export audit log for data requests:**
```swift
func handleDataRequest() async throws -> Data {
let auditLog = ConsentAuditLog.shared
return try auditLog.exportJSON()
}
```
### Testing
```swift
@Test
func consentGrantedPersistsAcrossLaunches() async {
let defaults = UserDefaults(suiteName: "test")!
defaults.removePersistentDomain(forName: "test")
let manager = ConsentManager(defaults: defaults)
manager.updateConsent(for: .analytics, granted: true)
let manager2 = ConsentManager(defaults: defaults)
#expect(manager2.hasConsent(for: .analytics) == true)
}
@Test
func essentialConsentCannotBeRevoked() {
let manager = ConsentManager()
manager.updateConsent(for: .essential, granted: false)
#expect(manager.hasConsent(for: .essential) == true) // Always granted
}
@Test
func auditLogRecordsDecisions() {
let log = ConsentAuditLog(directory: tempDirectory)
log.record(category: .analytics, granted: true, regulation: .gdpr)
let entries = log.allEntries()
#expect(entries.count == 1)
#expect(entries[0].category == .analytics)
#expect(entries[0].granted == true)
}
@Test
func denyAllRevokesNonEssentialCategories() {
let manager = ConsentManager()
manager.grantAll()
manager.denyAllNonEssential()
#expect(manager.hasConsent(for: .essential) == true)
#expect(manager.hasConsent(for: .analytics) == false)
#expect(manager.hasConsent(for: .marketing) == false)
}
```
## Common Patterns
### Show Consent on First Launch
```swift
.onAppear {
if consentManager.needsConsent {
// Banner auto-shows via overlay
}
}
```
### Update Preferences Later
```swift
Button("Privacy Settings") {
showPreferences = true
}
.sheet(isPresented: $showPreferences) {
ConsentPreferencesView()
.environment(consentManager)
}
```
### Check Consent Before Any Tracking Call
```swift
extension ConsentManager {
func executeIfConsented(
category: ConsentCategory,
action: () -> Void
) {
guard hasConsent(for: category) else { return }
action()
}
}
```
### Export Audit Log for Data Subject Requests
```swift
let jsonData = try consentManager.auditLog.exportJSON()
// Attach to email or upload to compliance endpoint
```
## Gotchas
### ATT Must Be Requested Before Any Tracking
Apple rejects apps that access IDFA before calling `ATTrackingManager.requestTrackingAuthorization`. Always request ATT first, then enable tracking SDKs based on the result.
### GDPR Requires Opt-In (Not Opt-Out)
Under GDPR, all non-essential tracking requires explicit opt-in consent. Pre-checked boxes or implied consent are not valid. The default state for all non-essential categories must be `.notDetermined`, not `.granted`.
### Consent Must Be As Easy to Withdraw As to Grant
GDPR Article 7(3): "It shall be as easy to withdraw as to give consent." If consent is granted with one tap on a banner, it must be revocable with equal ease — not buried 5 screens deep in settings.
### Different Regulations Have Different Age Thresholds
- GDPR: 16 years (member states can lower to 13)
- CCPA: 16 years for sale of data, 13 for minors
- DPDP: 18 years (parental consent required below)
If your app serves minors, you need age verification before consent collection.
### Don't Block the UI on ATT
`ATTrackingManager.requestTrackingAuthorization` is async and shows a system dialog. Never call it during app launch or in a way that blocks the main UI. Show your own consent banner first, then request ATT as a secondary step.
### Consent State Must Survive App Reinstall (GDPR)
For GDPR compliance, consider syncing consent state to a server. UserDefaults is deleted on app uninstall. If a user reinstalls, you must re-request consent — never assume prior consent.
## References
- **templates.md** — All production Swift templates for consent flow
- **patterns.md** — Regulation comparison, ATT details, UX best practices, anti-patterns
- Related: `generators/permission-priming` — Pre-permission UI patterns (ATT priming)
- Related: `generators/analytics-setup` — Analytics that respects consent state
- Related: `generators/settings-screen` — Embedding consent preferences in settings
Ver en GitHub