Claude
Skills
Sign in
Back

permissionkit

Included with Lifetime
$97 forever

Create child communication safety experiences using PermissionKit to request parental permission for children. Use when building apps that involve child-to-contact communication, need to check communication limits, request parent/guardian approval, or handle permission responses for minors.

General

What this skill does


# PermissionKit

> **Note:** PermissionKit APIs span multiple 26.x releases. Verify signatures
> and availability against the current Xcode 26 SDK before shipping.

Request permission from a parent or guardian to modify a child's communication
rules. PermissionKit creates communication safety experiences that let children
ask for exceptions to communication limits set by their parents. Targets
Swift 6.3 / iOS 26+.

PermissionKit communication experiences are available only through iMessage.
Use it for parent/guardian approval flows, not as a general in-app contact
permission, moderation, or chat-safety framework.

## Contents

- [Setup](#setup)
- [Core Concepts](#core-concepts)
- [Checking Communication Limits](#checking-communication-limits)
- [Creating Permission Questions](#creating-permission-questions)
- [Requesting Permission with AskCenter](#requesting-permission-with-askcenter)
- [SwiftUI Integration with PermissionButton](#swiftui-integration-with-permissionbutton)
- [Handling Responses](#handling-responses)
- [Significant App Update Topic](#significant-app-update-topic)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)

## Setup

Import `PermissionKit`. Do not invent PermissionKit entitlement keys; verify
current Apple documentation and Xcode capabilities before adding signing
requirements.

```swift
import PermissionKit
```

**Platform availability:**

When reviewing or correcting code, state these exact tiers instead of collapsing
PermissionKit to "iOS 26+":

- Core topic, handle, question, response, choice, and `CommunicationLimits`
  APIs: iOS 26.0+, iPadOS 26.0+, Mac Catalyst 26.0+, macOS 26.0+,
  visionOS 26.0+.
- `AskError`: iOS 26.1+, iPadOS 26.1+, Mac Catalyst 26.1+, macOS 26.1+,
  visionOS 26.1+.
- `AskCenter`, `AskCenter.ask(_:in:)`, `AskCenter.responses(for:)`,
  `PermissionButton`, and `SignificantAppUpdateTopic`: iOS/iPadOS/
  Mac Catalyst/macOS/visionOS 26.2+.

## Core Concepts

PermissionKit manages a flow where:

1. A child encounters a communication limit in your app
2. Your app creates a `PermissionQuestion` describing the request
3. The system presents the question to the child for them to send to their parent
4. The parent reviews and approves or denies the request
5. Your app receives a `PermissionResponse` with the parent's decision

### Key Types

| Type | Role |
|---|---|
| `AskCenter` | Singleton that manages permission requests and responses |
| `PermissionQuestion` | Describes the permission being requested |
| `PermissionResponse` | The parent's decision (approval or denial) |
| `PermissionChoice` | The specific answer (approve/decline) |
| `PermissionButton` | SwiftUI button that triggers the permission flow |
| `CommunicationTopic` | Topic for communication-related permission requests |
| `CommunicationHandle` | A phone number, email, or custom identifier |
| `CommunicationLimits` | Checks which communication handles are known to the system |
| `SignificantAppUpdateTopic` | Topic for significant app update permission requests |

## Checking Communication Limits

Use `CommunicationLimits.current` to check whether the system already knows a
communication handle for your app. This is not an "are communication limits
enabled?" probe. If limits are not enabled, `AskCenter.shared.ask(_:in:)`
throws `AskError.communicationLimitsNotEnabled`; handle that path when asking.

`knownHandles(in:)` also requires the calling app to have a non-nil, nonempty
bundle identifier. Corrected code should guard `Bundle.main.bundleIdentifier`
before calling it.

```swift
import PermissionKit

func needsPermissionPrompt(for handle: CommunicationHandle) async -> Bool {
    let limits = CommunicationLimits.current
    let isKnown = await limits.isKnownHandle(handle)
    return !isKnown
}

// Check multiple handles at once.
func filterKnownHandles(_ handles: Set<CommunicationHandle>) async -> Set<CommunicationHandle> {
    guard Bundle.main.bundleIdentifier?.isEmpty == false else { return [] }

    let limits = CommunicationLimits.current
    return await limits.knownHandles(in: handles)
}
```

### Creating Communication Handles

```swift
let phoneHandle = CommunicationHandle(
    value: "+1234567890",
    kind: .phoneNumber
)

let emailHandle = CommunicationHandle(
    value: "[email protected]",
    kind: .emailAddress
)

let customHandle = CommunicationHandle(
    value: "user123",
    kind: .custom
)
```

## Creating Permission Questions

Build a `PermissionQuestion` with the contact information and communication
action type.

```swift
// Question for a single contact
let handle = CommunicationHandle(value: "+1234567890", kind: .phoneNumber)
let question = PermissionQuestion<CommunicationTopic>(handle: handle)

// Question for multiple contacts
let handles = [
    CommunicationHandle(value: "+1234567890", kind: .phoneNumber),
    CommunicationHandle(value: "[email protected]", kind: .emailAddress)
]
let multiQuestion = PermissionQuestion<CommunicationTopic>(handles: handles)
```

### Using CommunicationTopic with Person Information

Provide display names and avatars for a richer permission prompt.

```swift
let personInfo = CommunicationTopic.PersonInformation(
    handle: CommunicationHandle(value: "+1234567890", kind: .phoneNumber),
    nameComponents: {
        var name = PersonNameComponents()
        name.givenName = "Alex"
        name.familyName = "Smith"
        return name
    }(),
    avatarImage: nil
)

let topic = CommunicationTopic(
    personInformation: [personInfo],
    actions: [.message, .audioCall]
)

let question = PermissionQuestion<CommunicationTopic>(communicationTopic: topic)
```

### Communication Actions

| Action | Description |
|---|---|
| `.message` | Text messaging |
| `.audioCall` | Voice call |
| `.videoCall` | Video call |
| `.call` | Generic call |
| `.chat` | Chat communication |
| `.follow` | Follow a user |
| `.beFollowed` | Allow being followed |
| `.friend` | Friend request |
| `.connect` | Connection request |
| `.communicate` | Generic communication |

## Requesting Permission with AskCenter

Use `AskCenter.shared` to request that the child send the permission question
to their parent or guardian. The async `ask` call starts the send flow; parent
decisions arrive later through `responses(for:)`. If the child cancels the send
flow, the system does not deliver a `PermissionResponse` for that question.

```swift
import PermissionKit

func requestPermission(
    for question: PermissionQuestion<CommunicationTopic>,
    in viewController: UIViewController
) async {
    do {
        try await AskCenter.shared.ask(question, in: viewController)
        // Question send flow was started; wait for responses(for:) separately.
    } catch let error as AskError {
        switch error {
        case .communicationLimitsNotEnabled:
            // Communication limits not active -- continue with normal app flow.
            break
        case .contactSyncNotSetup:
            // Contact sync not configured
            break
        case .invalidQuestion:
            // Question is malformed
            break
        case .notAvailable:
            // PermissionKit not available on this device
            break
        case .systemError(let underlying):
            print("System error: \(underlying)")
        case .unknown:
            break
        @unknown default:
            break
        }
    }
}
```

## SwiftUI Integration with PermissionButton

`PermissionButton` is a SwiftUI view that triggers the permission flow when
tapped. It uses the same response model as `AskCenter`: observe responses and
model a pending/canceled state instead of assuming every tap produces a parent
decision.

```swift
import SwiftUI
import PermissionKit

struct ContactPermissionView: View {
    let handle = CommunicationHandle(value: "+1234567890", kind: .phoneNumber)

    var body: some View {
        let question = PermissionQuestion<CommunicationTopic>(handle: handle

Related in General