permission-priming

Generates pre-permission priming screens that explain benefits before showing iOS system permission dialogs. Use when user wants to increase permission grant rates, add pre-permission screens, or explain why the app needs access.

149 stars

Best use case

permission-priming is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Generates pre-permission priming screens that explain benefits before showing iOS system permission dialogs. Use when user wants to increase permission grant rates, add pre-permission screens, or explain why the app needs access.

Teams using permission-priming should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/permission-priming/SKILL.md --create-dirs "https://raw.githubusercontent.com/rshankras/claude-code-apple-skills/main/skills/generators/permission-priming/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/permission-priming/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How permission-priming Compares

Feature / Agentpermission-primingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Generates pre-permission priming screens that explain benefits before showing iOS system permission dialogs. Use when user wants to increase permission grant rates, add pre-permission screens, or explain why the app needs access.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Permission Priming Generator

Generate pre-permission priming screens — shown before iOS system permission dialogs to explain WHY the app needs access. Dramatically increases permission grant rates vs. cold-prompting users with the system alert.

## When This Skill Activates

Use this skill when the user:
- Asks to "add permission priming" or "pre-permission screen"
- Mentions "explain permissions" or "permission request screen"
- Wants to "ask for camera permission" or "notification permission"
- Asks about "location permission explanation" or "permission grant rate"
- Wants to "prime before requesting access" or "improve opt-in rates"
- Mentions "permission dialog" or "permission flow"

## Pre-Generation Checks

### 1. Project Context Detection
- [ ] Check deployment target (iOS 16+ / macOS 13+)
- [ ] Check for @Observable support (iOS 17+ / macOS 14+)
- [ ] Identify source file locations

### 2. Existing Permission Code
Search for existing permission handling:
```
Glob: **/*Permission*.swift, **/*Authorization*.swift
Grep: "requestAuthorization" or "AVCaptureDevice" or "UNUserNotificationCenter" or "CLLocationManager" or "PHPhotoLibrary"
```

If existing permission code found:
- Ask if user wants to wrap existing calls with priming screens or replace entirely
- If wrapping, integrate priming views upstream of existing request calls

### 3. Info.plist Usage Description Strings
Search for required usage description keys:
```
Grep: "NSCameraUsageDescription" or "NSMicrophoneUsageDescription" or "NSLocationWhenInUseUsageDescription" or "NSPhotoLibraryUsageDescription" or "NSContactsUsageDescription" or "NSHealthShareUsageDescription"
```

If missing keys found for requested permissions:
- Warn user that the app will crash without the corresponding Info.plist entry
- Provide the required key and a suggested description string

## Configuration Questions

Ask user via AskUserQuestion:

1. **Which permissions to prime?** (multi-select)
   - Push Notifications
   - Camera
   - Photo Library
   - Location (When In Use)
   - Location (Always)
   - Microphone
   - Contacts
   - Health (HealthKit)
   - App Tracking Transparency (ATT)

2. **Priming style?**
   - Full-screen (dedicated view with illustration, title, benefits, CTA) — recommended
   - Alert-style (compact alert-like overlay before system prompt)
   - Inline (embedded card within existing content)

3. **Include "Not Now" option?**
   - Yes (recommended — reduces user frustration, can re-prompt later)
   - No (only "Enable" button — more aggressive)

4. **Show benefits illustration?**
   - SF Symbol (simple, matches system style)
   - Custom illustration area (placeholder for designer asset)
   - None (text only)

## Generation Process

### Step 1: Read Templates
Read `templates.md` for production Swift code.

### Step 2: Create Core Files
Generate these files:
1. `PermissionType.swift` — Enum of all permission types with metadata
2. `PermissionStatus.swift` — Unified status enum wrapping platform-specific statuses
3. `PermissionManager.swift` — @Observable class that checks, requests, and opens Settings

### Step 3: Create UI Files
4. `PermissionPrimingView.swift` — Pre-permission screen with benefits and CTA
5. `PermissionStatusTracker.swift` — Monitors status changes from Settings

### Step 4: Create Modifier Files
6. `PermissionGatedModifier.swift` — ViewModifier that gates content behind permission check

### Step 5: Determine File Location
Check project structure:
- If `Sources/` exists -> `Sources/Permissions/`
- If `App/` exists -> `App/Permissions/`
- Otherwise -> `Permissions/`

## Output Format

After generation, provide:

### Files Created
```
Permissions/
├── PermissionType.swift            # Permission enum with metadata
├── PermissionStatus.swift          # Unified status wrapper
├── PermissionManager.swift         # Check, request, open Settings
├── PermissionPrimingView.swift     # Pre-permission priming screen
├── PermissionStatusTracker.swift   # Monitor status changes
└── PermissionGatedModifier.swift   # Gate content behind permission
```

### Integration Steps

**Basic priming before system prompt:**
```swift
// Show priming screen, then request system permission on tap
PermissionPrimingView(permissionType: .notifications) {
    // User granted — proceed with feature
    startSendingNotifications()
} onDenied: {
    // User denied or tapped "Not Now"
    showLaterPrompt()
}
```

**Gate a feature behind permission:**
```swift
// Camera feature gated behind permission
CameraView()
    .permissionGated(.camera) {
        // Priming screen shown automatically if not yet authorized
    }
```

**In onboarding flow:**
```swift
struct OnboardingPermissionsView: View {
    @State private var permissionManager = PermissionManager()

    var body: some View {
        VStack(spacing: 32) {
            PermissionPrimingView(permissionType: .notifications) {
                // Move to next permission
            } onDenied: {
                // Skip, ask later
            }
        }
    }
}
```

**Check status and re-prompt after denial:**
```swift
struct SettingsView: View {
    @State private var permissionManager = PermissionManager()

    var body: some View {
        Section("Permissions") {
            ForEach(PermissionType.allCases, id: \.self) { type in
                PermissionRow(
                    type: type,
                    status: permissionManager.status(for: type),
                    onRequest: { permissionManager.openSettings() }
                )
            }
        }
    }
}
```

### Testing

```swift
@Test
func primingViewShowsBeforeSystemPrompt() async throws {
    let manager = PermissionManager()
    let status = await manager.status(for: .notifications)
    #expect(status == .notDetermined)
    // Priming view should appear before system dialog
}

@Test
func deniedPermissionDirectsToSettings() async throws {
    let manager = PermissionManager()
    // After denial, tapping "Enable" should open Settings
    let settingsURL = manager.settingsURL
    #expect(settingsURL != nil)
}

@Test
func permissionGatedModifierShowsPrimingWhenNotAuthorized() async throws {
    // ViewModifier should show priming screen when permission is .notDetermined
    // and show content when .authorized
}
```

## Common Patterns

### Prime Before First Use
Show priming screen at the natural moment the user first needs the feature, not during onboarding:
```swift
// User taps "Take Photo" -> show camera priming -> then system prompt
Button("Take Photo") {
    showCameraPriming = true
}
.sheet(isPresented: $showCameraPriming) {
    PermissionPrimingView(permissionType: .camera,
                          onGranted: { openCamera() },
                          onDenied: { showCameraPriming = false })
}
```

### Contextual Priming
Explain the benefit in context of what the user is trying to do:
```swift
PermissionPrimingView(
    permissionType: .location(.whenInUse),
    customTitle: "Find Nearby Restaurants",
    customDescription: "We use your location to show restaurants within walking distance."
) { ... }
```

### Re-Request After Denial
After denial, the system prompt cannot be shown again. Direct to Settings:
```swift
if permissionManager.status(for: .camera) == .denied {
    // Show explanation + "Open Settings" button
    PermissionDeniedView(permissionType: .camera) {
        permissionManager.openSettings()
    }
}
```

## Gotchas

- **Cannot re-show system prompt after denial.** Once the user denies a permission, iOS will never show the system dialog again. You must direct the user to Settings > Privacy to re-enable. Always handle the `.denied` state gracefully.
- **App Tracking Transparency (ATT) has special requirements.** ATT must be requested before tracking begins. The priming screen must NOT use misleading language. Apple reviews ATT implementations closely — avoid incentivizing users to allow tracking.
- **Health permissions have unique two-column UI.** HealthKit authorization uses its own system sheet with read/write toggles per data type. You cannot customize it. Your priming screen should explain which health data you need and why before presenting the HealthKit sheet.
- **Location "Always" requires progressive authorization.** You must first request `.whenInUse`, then separately request `.always`. iOS shows a follow-up prompt only after the user has used the app with When In Use access. Do not request Always upfront.
- **Provisional notifications (iOS 12+) bypass the prompt entirely.** With `.provisional`, notifications are delivered silently to Notification Center without asking. Consider whether quiet delivery is acceptable before building a priming screen.
- **Info.plist keys are mandatory.** If you call a permission API without the corresponding `NS*UsageDescription` key in Info.plist, the app will crash immediately. Always verify these keys exist.

## References

- **templates.md** — All production Swift templates for permission priming
- Related: `generators/push-notifications` — Push notification setup and handling
- Related: `generators/consent-flow` — GDPR/privacy consent flows

Related Skills

watchOS

149
from rshankras/claude-code-apple-skills

watchOS development guidance including SwiftUI for Watch, Watch Connectivity, complications, and watch-specific UI patterns. Use for watchOS code review, best practices, or Watch app development.

visionos-widgets

149
from rshankras/claude-code-apple-skills

visionOS widget patterns including mounting styles, glass/paper textures, proximity-aware layouts, and spatial widget families. Use when creating or adapting widgets for visionOS.

test-data-factory

149
from rshankras/claude-code-apple-skills

Generate test fixture factories for your models. Builder pattern and static factories for zero-boilerplate test data. Use when tests need sample data setup.

test-contract

149
from rshankras/claude-code-apple-skills

Generate protocol/interface test suites that any implementation must pass. Define the contract once, test every implementation. Use when designing protocols or swapping implementations.

tdd-refactor-guard

149
from rshankras/claude-code-apple-skills

Pre-refactor safety checklist. Verifies test coverage exists before AI modifies existing code. Use before asking AI to refactor anything.

tdd-feature

149
from rshankras/claude-code-apple-skills

Red-green-refactor scaffold for building new features with TDD. Write failing tests first, then implement to pass. Use when building new features test-first.

tdd-bug-fix

149
from rshankras/claude-code-apple-skills

Fix bugs using red-green-refactor — reproduce the bug as a failing test first, then fix it. Use when fixing bugs to ensure they never regress.

snapshot-test-setup

149
from rshankras/claude-code-apple-skills

Set up SwiftUI visual regression testing with swift-snapshot-testing. Generates snapshot test boilerplate and CI configuration. Use for UI regression prevention.

integration-test-scaffold

149
from rshankras/claude-code-apple-skills

Generate cross-module test harness with mock servers, in-memory stores, and test configuration. Use when testing networking + persistence + business logic together.

characterization-test-generator

149
from rshankras/claude-code-apple-skills

Generates tests that capture current behavior of existing code before refactoring. Use when you need a safety net before AI-assisted refactoring or modifying legacy code.

testing

149
from rshankras/claude-code-apple-skills

TDD and testing skills for iOS/macOS apps. Covers characterization tests, TDD workflows, test contracts, snapshot tests, and test infrastructure. Use for test-driven development, adding tests to existing code, or building test infrastructure.

webkit-integration

149
from rshankras/claude-code-apple-skills

WebKit integration in SwiftUI using WebView and WebPage for embedding web content, navigation, JavaScript interop, and customization. Use when embedding web content in SwiftUI apps.