All skills

Code generator skills that produce production-ready Swift code for common app components. Use when user wants to add logging, analytics, onboarding, review prompts, networking, authentication, paywalls, settings, persistence, error monitoring, CI/CD pipelines, localization, push notifications, deep linking, testing, accessibility, widgets, feature flags, app icons, image caching, pagination, HTTP caching, share cards, social export, subscription lifecycle, referral systems, watermarks, streak tracking, milestone celebrations, what's new screens, lapsed user re-engagement, usage insights, variable rewards, consent flows, account deletion, permission priming, force updates, state restoration, debug menus, offline queues, feedback forms, announcement banners, quick win sessions, Spotlight indexing, App Clips, screenshot automation, background processing, app extensions, data export, or SwiftUI preview sample data and variant matrices.

Use this Skill: https://skilld.dev/gh/rshankras/claude-code-apple-skills/generators

This session only. Nothing lands on disk.

consent-flowtemplates.md

≈6.3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Consent Flow Code Templates

Production-ready Swift templates for privacy consent management. All code targets iOS 16+ / macOS 13+ (iOS 17+ / macOS 14+ for @Observable) and uses modern Swift concurrency.

ConsentCategory.swift

import Foundation

/// Categories of data processing that require user consent.
///
/// Each category represents a distinct purpose for data collection.
/// Essential is always required and cannot be disabled by the user.
enum ConsentCategory: String, CaseIterable, Codable, Sendable, Identifiable {
    case essential
    case analytics
    case marketing
    case personalization
    case functional

    var id: String { rawValue }

    /// Human-readable display name.
    var displayName: String {
        switch self {
        case .essential: return "Essential"
        case .analytics: return "Analytics"
        case .marketing: return "Marketing"
        case .personalization: return "Personalization"
        case .functional: return "Functional"
        }
    }

    /// Explanation shown to users describing what this category covers.
    var explanation: String {
        switch self {
        case .essential:
            return "Required for the app to function. Includes authentication, security, and core features."
        case .analytics:
            return "Helps us understand how you use the app so we can improve it. Includes usage statistics and crash reports."
        case .marketing:
            return "Used for advertising and attribution. Allows us to measure ad effectiveness and show relevant ads."
        case .personalization:
            return "Enables personalized content and recommendations based on your usage patterns."
        case .functional:
            return "Remembers your preferences and settings to enhance your experience beyond essential functionality."
        }
    }

    /// Whether this category is required and cannot be disabled.
    var isRequired: Bool {
        self == .essential
    }

    /// Categories that require user consent (excludes essential).
    static var consentable: [ConsentCategory] {
        allCases.filter { !$0.isRequired }
    }
}

ConsentDecision.swift

import Foundation

/// The user's consent decision for a specific category.
enum ConsentStatus: String, Codable, Sendable {
    case granted
    case denied
    case notDetermined
}

/// A recorded consent decision with metadata.
struct ConsentDecision: Codable, Sendable, Equatable {
    let category: ConsentCategory
    let status: ConsentStatus
    let timestamp: Date
    let regulation: ConsentRegulation?

    init(
        category: ConsentCategory,
        status: ConsentStatus,
        timestamp: Date = Date(),
        regulation: ConsentRegulation? = nil
    ) {
        self.category = category
        self.status = status
        self.timestamp = timestamp
        self.regulation = regulation
    }
}

/// Supported privacy regulations.
enum ConsentRegulation: String, Codable, Sendable, CaseIterable {
    case gdpr    // EU General Data Protection Regulation
    case ccpa    // California Consumer Privacy Act
    case dpdp    // India Digital Personal Data Protection

    var displayName: String {
        switch self {
        case .gdpr: return "GDPR"
        case .ccpa: return "CCPA"
        case .dpdp: return "DPDP"
        }
    }

    /// Whether this regulation requires explicit opt-in consent.
    var requiresOptIn: Bool {
        switch self {
        case .gdpr: return true   // Must opt-in
        case .ccpa: return false  // Opt-out model
        case .dpdp: return true   // Must opt-in
        }
    }

    /// Minimum age for self-consent.
    var minimumConsentAge: Int {
        switch self {
        case .gdpr: return 16    // Member states can lower to 13
        case .ccpa: return 16    // 13 for sale-of-data specific consent
        case .dpdp: return 18
        }
    }
}

ConsentManager.swift

import Foundation
import AppTrackingTransparency

/// Manages user privacy consent state across the app.
///
/// Persists consent decisions in UserDefaults and integrates
/// with App Tracking Transparency for iOS.
///
/// Usage:
/// ```swift
/// @State private var consentManager = ConsentManager()
///
/// if consentManager.hasConsent(for: .analytics) {
///     trackEvent(.screenView)
/// }
/// ```
@Observable
final class ConsentManager {
    // MARK: - State

    private(set) var decisions: [ConsentCategory: ConsentDecision] = [:]
    private(set) var attStatus: ATTrackingManager.AuthorizationStatus = .notDetermined

    // MARK: - Dependencies

    private let defaults: UserDefaults
    private let auditLog: ConsentAuditLog
    private let regulation: ConsentRegulation?

    private static let storageKey = "com.app.consentDecisions"

    // MARK: - Computed Properties

    /// Whether the app needs to show the consent banner.
    ///
    /// Returns true if any non-essential category has not been decided.
    var needsConsent: Bool {
        ConsentCategory.consentable.contains { category in
            decisions[category]?.status == .notDetermined || decisions[category] == nil
        }
    }

    /// Summary of current consent state for display.
    var consentSummary: String {
        let granted = ConsentCategory.consentable.filter { hasConsent(for: $0) }
        return "\(granted.count) of \(ConsentCategory.consentable.count) optional categories enabled"
    }

    // MARK: - Initialization

    init(
        defaults: UserDefaults = .standard,
        auditLog: ConsentAuditLog = .shared,
        regulation: ConsentRegulation? = nil
    ) {
        self.defaults = defaults
        self.auditLog = auditLog
        self.regulation = regulation
        loadPersistedDecisions()
        ensureEssentialGranted()

        #if os(iOS)
        attStatus = ATTrackingManager.trackingAuthorizationStatus
        #endif
    }

    // MARK: - Consent Operations

    /// Check if the user has granted consent for a specific category.
    func hasConsent(for category: ConsentCategory) -> Bool {
        if category.isRequired { return true }
        return decisions[category]?.status == .granted
    }

    /// Update consent for a specific category.
    func updateConsent(for category: ConsentCategory, granted: Bool) {
        guard !category.isRequired else { return } // Essential cannot be changed

        let decision = ConsentDecision(
            category: category,
            status: granted ? .granted : .denied,
            regulation: regulation
        )

        decisions[category] = decision
        persistDecisions()

        auditLog.record(decision: decision)

        NotificationCenter.default.post(
            name: .consentDidChange,
            object: nil,
            userInfo: ["category": category, "granted": granted]
        )
    }

    /// Grant consent for all categories.
    func grantAll() {
        for category in ConsentCategory.consentable {
            updateConsent(for: category, granted: true)
        }
    }

    /// Deny consent for all non-essential categories.
    func denyAllNonEssential() {
        for category in ConsentCategory.consentable {
            updateConsent(for: category, granted: false)
        }
    }

    /// Reset all consent decisions, requiring re-consent.
    func resetAllConsent() {
        for category in ConsentCategory.consentable {
            let decision = ConsentDecision(
                category: category,
                status: .notDetermined,
                regulation: regulation
            )
            decisions[category] = decision
            auditLog.record(decision: decision)
        }
        persistDecisions()

        NotificationCenter.default.post(name: .consentDidChange, object: nil)
    }

    /// Execute a closure only if consent is granted for the given category.
    func executeIfConsented(
        category: ConsentCategory,
        action: () -> Void
    ) {
        guard hasConsent(for: category) else { return }
        action()
    }

    // MARK: - ATT Integration

    /// Request App Tracking Transparency permission.
    ///
    /// Should be called after showing your own consent explanation,
    /// not during app launch. Returns the authorization status.
    @MainActor
    func requestATTPermission() async -> ATTrackingManager.AuthorizationStatus {
        #if os(iOS)
        let status = await ATTrackingManager.requestTrackingAuthorization()
        attStatus = status

        // Sync ATT result with marketing consent
        switch status {
        case .authorized:
            updateConsent(for: .marketing, granted: true)
        case .denied, .restricted:
            updateConsent(for: .marketing, granted: false)
        case .notDetermined:
            break
        @unknown default:
            break
        }

        return status
        #else
        return .notDetermined
        #endif
    }

    /// Whether ATT has been requested and resolved.
    var isATTDetermined: Bool {
        #if os(iOS)
        return attStatus != .notDetermined
        #else
        return true
        #endif
    }

    // MARK: - Persistence

    private func loadPersistedDecisions() {
        guard let data = defaults.data(forKey: Self.storageKey),
              let decoded = try? JSONDecoder().decode(
                  [String: ConsentDecision].self, from: data
              ) else {
            initializeDefaultDecisions()
            return
        }

        decisions = Dictionary(
            uniqueKeysWithValues: decoded.compactMap { key, value in
                guard let category = ConsentCategory(rawValue: key) else { return nil }
                return (category, value)
            }
        )
    }

    private func persistDecisions() {
        let encoded = Dictionary(
            uniqueKeysWithValues: decisions.map { ($0.key.rawValue, $0.value) }
        )
        if let data = try? JSONEncoder().encode(encoded) {
            defaults.set(data, forKey: Self.storageKey)
        }
    }

    private func initializeDefaultDecisions() {
        for category in ConsentCategory.allCases {
            decisions[category] = ConsentDecision(
                category: category,
                status: category.isRequired ? .granted : .notDetermined
            )
        }
    }

    private func ensureEssentialGranted() {
        decisions[.essential] = ConsentDecision(
            category: .essential,
            status: .granted
        )
    }
}

// MARK: - Notifications

extension Notification.Name {
    /// Posted when any consent decision changes.
    ///
    /// UserInfo contains "category" (ConsentCategory) and "granted" (Bool).
    static let consentDidChange = Notification.Name("consentDidChange")
}

ConsentBannerView.swift

import SwiftUI

/// A bottom banner view requesting user consent for data processing.
///
/// Displays a brief explanation with three action buttons:
/// - Accept All: grants all consent categories
/// - Manage Preferences: opens detailed preferences view
/// - Reject Non-Essential: denies all optional categories
///
/// Usage:
/// ```swift
/// .overlay(alignment: .bottom) {
///     if consentManager.needsConsent {
///         ConsentBannerView()
///             .transition(.move(edge: .bottom).combined(with: .opacity))
///     }
/// }
/// ```
struct ConsentBannerView: View {
    @Environment(ConsentManager.self) private var consentManager
    @State private var showPreferences = false

    var body: some View {
        VStack(spacing: 16) {
            // Header
            HStack {
                Image(systemName: "hand.raised.fill")
                    .font(.title2)
                    .foregroundStyle(.tint)
                Text("Your Privacy Matters")
                    .font(.headline)
                Spacer()
            }

            // Explanation
            Text("We use cookies and similar technologies to improve your experience. You can choose which categories of data processing to allow.")
                .font(.subheadline)
                .foregroundStyle(.secondary)
                .fixedSize(horizontal: false, vertical: true)

            // Action Buttons
            VStack(spacing: 10) {
                Button {
                    consentManager.grantAll()
                } label: {
                    Text("Accept All")
                        .frame(maxWidth: .infinity)
                }
                .buttonStyle(.borderedProminent)
                .controlSize(.large)

                HStack(spacing: 10) {
                    Button {
                        showPreferences = true
                    } label: {
                        Text("Manage Preferences")
                            .frame(maxWidth: .infinity)
                    }
                    .buttonStyle(.bordered)
                    .controlSize(.regular)

                    Button {
                        consentManager.denyAllNonEssential()
                    } label: {
                        Text("Reject Non-Essential")
                            .frame(maxWidth: .infinity)
                    }
                    .buttonStyle(.bordered)
                    .controlSize(.regular)
                }
            }

            // Privacy Policy Link
            Link("Privacy Policy", destination: URL(string: "https://example.com/privacy")!)
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding(20)
        .background {
            RoundedRectangle(cornerRadius: 16)
                #if os(iOS)
                .fill(.ultraThinMaterial)
                #else
                .fill(Color(nsColor: .controlBackgroundColor))
                #endif
                .shadow(color: .black.opacity(0.15), radius: 10, y: -5)
        }
        .padding(.horizontal, 16)
        .padding(.bottom, 8)
        .sheet(isPresented: $showPreferences) {
            ConsentPreferencesView()
        }
    }
}

ConsentPreferencesView.swift

import SwiftUI

/// Detailed consent preferences view with per-category toggles.
///
/// Shows each consent category with its description and a toggle.
/// Essential category is always on with a disabled toggle.
/// Provides Save and Cancel actions.
///
/// Usage:
/// ```swift
/// .sheet(isPresented: $showPreferences) {
///     ConsentPreferencesView()
///         .environment(consentManager)
/// }
/// ```
struct ConsentPreferencesView: View {
    @Environment(ConsentManager.self) private var consentManager
    @Environment(\.dismiss) private var dismiss

    @State private var pendingDecisions: [ConsentCategory: Bool] = [:]

    var body: some View {
        NavigationStack {
            List {
                Section {
                    explanationHeader
                }

                Section("Consent Categories") {
                    ForEach(ConsentCategory.allCases) { category in
                        ConsentCategoryRow(
                            category: category,
                            isEnabled: binding(for: category)
                        )
                    }
                }

                Section {
                    quickActions
                }

                Section {
                    privacyLinks
                }
            }
            .navigationTitle("Privacy Preferences")
            #if os(iOS)
            .navigationBarTitleDisplayMode(.inline)
            #endif
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") {
                        dismiss()
                    }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button("Save") {
                        savePreferences()
                        dismiss()
                    }
                    .fontWeight(.semibold)
                }
            }
            .onAppear {
                loadCurrentDecisions()
            }
        }
    }

    // MARK: - Subviews

    private var explanationHeader: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("Choose which types of data processing you consent to. Essential data processing cannot be disabled as it is required for the app to function.")
                .font(.subheadline)
                .foregroundStyle(.secondary)
        }
    }

    private var quickActions: some View {
        VStack(spacing: 8) {
            Button("Enable All") {
                for category in ConsentCategory.consentable {
                    pendingDecisions[category] = true
                }
            }

            Button("Disable All Non-Essential") {
                for category in ConsentCategory.consentable {
                    pendingDecisions[category] = false
                }
            }
            .foregroundStyle(.secondary)
        }
    }

    private var privacyLinks: some View {
        VStack(alignment: .leading, spacing: 8) {
            Link("Privacy Policy", destination: URL(string: "https://example.com/privacy")!)
            Link("Terms of Service", destination: URL(string: "https://example.com/terms")!)
        }
        .font(.footnote)
    }

    // MARK: - Logic

    private func binding(for category: ConsentCategory) -> Binding<Bool> {
        Binding(
            get: { pendingDecisions[category] ?? category.isRequired },
            set: { newValue in
                guard !category.isRequired else { return }
                pendingDecisions[category] = newValue
            }
        )
    }

    private func loadCurrentDecisions() {
        for category in ConsentCategory.allCases {
            pendingDecisions[category] = consentManager.hasConsent(for: category)
        }
    }

    private func savePreferences() {
        for (category, granted) in pendingDecisions {
            guard !category.isRequired else { continue }
            consentManager.updateConsent(for: category, granted: granted)
        }
    }
}

// MARK: - Category Row

/// A single row in the consent preferences list.
struct ConsentCategoryRow: View {
    let category: ConsentCategory
    @Binding var isEnabled: Bool

    var body: some View {
        VStack(alignment: .leading, spacing: 6) {
            HStack {
                VStack(alignment: .leading, spacing: 2) {
                    HStack(spacing: 6) {
                        Text(category.displayName)
                            .font(.body.weight(.medium))
                        if category.isRequired {
                            Text("Required")
                                .font(.caption2)
                                .padding(.horizontal, 6)
                                .padding(.vertical, 2)
                                .background(Color.secondary.opacity(0.2))
                                .clipShape(Capsule())
                        }
                    }
                }
                Spacer()
                Toggle("", isOn: $isEnabled)
                    .labelsHidden()
                    .disabled(category.isRequired)
            }

            Text(category.explanation)
                .font(.caption)
                .foregroundStyle(.secondary)
                .fixedSize(horizontal: false, vertical: true)
        }
        .padding(.vertical, 4)
    }
}

ConsentAuditLog.swift

import Foundation

/// Records all consent decisions for compliance auditing.
///
/// Persists entries as JSON in the app's documents directory.
/// Supports export for data subject access requests (DSAR).
///
/// Usage:
/// ```swift
/// let log = ConsentAuditLog.shared
/// log.record(decision: decision)
/// let jsonData = try log.exportJSON()
/// ```
final class ConsentAuditLog: Sendable {
    static let shared = ConsentAuditLog()

    private let fileURL: URL
    private let queue = DispatchQueue(label: "com.app.consentAuditLog", qos: .utility)

    // MARK: - Audit Entry

    struct Entry: Codable, Sendable, Identifiable {
        let id: UUID
        let category: ConsentCategory
        let status: ConsentStatus
        let regulation: ConsentRegulation?
        let timestamp: Date
        let appVersion: String
        let osVersion: String

        init(
            category: ConsentCategory,
            status: ConsentStatus,
            regulation: ConsentRegulation?,
            timestamp: Date = Date()
        ) {
            self.id = UUID()
            self.category = category
            self.status = status
            self.regulation = regulation
            self.timestamp = timestamp
            self.appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown"
            self.osVersion = ProcessInfo.processInfo.operatingSystemVersionString
        }
    }

    // MARK: - Initialization

    init(directory: URL? = nil) {
        let dir = directory ?? FileManager.default
            .urls(for: .documentDirectory, in: .userDomainMask)[0]
        self.fileURL = dir.appendingPathComponent("consent_audit_log.json")
    }

    // MARK: - Recording

    /// Record a consent decision in the audit log.
    func record(decision: ConsentDecision) {
        let entry = Entry(
            category: decision.category,
            status: decision.status,
            regulation: decision.regulation
        )
        record(entry: entry)
    }

    /// Record a consent decision by components.
    func record(
        category: ConsentCategory,
        granted: Bool,
        regulation: ConsentRegulation? = nil
    ) {
        let entry = Entry(
            category: category,
            status: granted ? .granted : .denied,
            regulation: regulation
        )
        record(entry: entry)
    }

    private func record(entry: Entry) {
        queue.async { [fileURL] in
            var entries = Self.loadEntries(from: fileURL)
            entries.append(entry)
            Self.saveEntries(entries, to: fileURL)
        }
    }

    // MARK: - Querying

    /// All recorded audit entries, sorted by timestamp (newest first).
    func allEntries() -> [Entry] {
        Self.loadEntries(from: fileURL).sorted { $0.timestamp > $1.timestamp }
    }

    /// Entries filtered by category.
    func entries(for category: ConsentCategory) -> [Entry] {
        allEntries().filter { $0.category == category }
    }

    /// The most recent decision for each category.
    func currentDecisions() -> [ConsentCategory: Entry] {
        var result: [ConsentCategory: Entry] = [:]
        for entry in allEntries() {
            if result[entry.category] == nil {
                result[entry.category] = entry
            }
        }
        return result
    }

    // MARK: - Export

    /// Export the full audit log as JSON data.
    ///
    /// Use for data subject access requests (DSAR) or compliance reporting.
    func exportJSON() throws -> Data {
        let entries = allEntries()
        let encoder = JSONEncoder()
        encoder.dateEncodingStrategy = .iso8601
        encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
        return try encoder.encode(entries)
    }

    /// Export as a human-readable string for display.
    func exportReadable() -> String {
        let entries = allEntries()
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .medium

        return entries.map { entry in
            let action = entry.status == .granted ? "Granted" : "Denied"
            let date = formatter.string(from: entry.timestamp)
            let reg = entry.regulation?.displayName ?? "—"
            return "[\(date)] \(action) \(entry.category.displayName) (Regulation: \(reg), App: \(entry.appVersion))"
        }.joined(separator: "\n")
    }

    // MARK: - Maintenance

    /// Remove entries older than the specified retention period.
    ///
    /// Default retention: 3 years (GDPR recommendation).
    func pruneOldEntries(olderThan retention: TimeInterval = 3 * 365 * 24 * 3600) {
        queue.async { [fileURL] in
            let cutoff = Date().addingTimeInterval(-retention)
            var entries = Self.loadEntries(from: fileURL)
            entries.removeAll { $0.timestamp < cutoff }
            Self.saveEntries(entries, to: fileURL)
        }
    }

    /// Delete the entire audit log.
    func deleteAll() {
        queue.async { [fileURL] in
            try? FileManager.default.removeItem(at: fileURL)
        }
    }

    // MARK: - File Operations

    private static func loadEntries(from fileURL: URL) -> [Entry] {
        guard let data = try? Data(contentsOf: fileURL) else { return [] }
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .iso8601
        return (try? decoder.decode([Entry].self, from: data)) ?? []
    }

    private static func saveEntries(_ entries: [Entry], to fileURL: URL) {
        let encoder = JSONEncoder()
        encoder.dateEncodingStrategy = .iso8601
        guard let data = try? encoder.encode(entries) else { return }
        try? data.write(to: fileURL, options: .atomic)
    }
}

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The 'generators' skill provides a robust set of templates and workflows for generating Swift code for iOS and macOS applications. Security analysis identified a surface for indirect prompt injection because the skill reads local project files to adapt its generation logic. It also utilizes dynamic execution by generating and running local Swift scripts to create application icons and uses shell commands to process images and audit project code. All external references are to well-known, established services.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: MEDIUM · 1 issue

  • Runlayer7mo

    59/179 files flagged

  • ZeroLeaks5mo

    Scan incomplete

Signed by skilld at 30e898f. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 2 months ago
What it can do
Reads files Edits files Runs commands
last_verified
2026-07-16
review_by
2027-06-22
os_version
iOS 27 / macOS 27
All 7 allowed tools
ReadWriteEditGlobGrepBashAskUserQuestion

README badge

README badge for rshankras/claude-code-apple-skills/generators