permissionkit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

PermissionKit

PermissionKit

Note: PermissionKit is new in iOS 26. Method signatures should be verified against the latest Xcode 26 beta SDK.
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.2 / iOS 26+.
注意: PermissionKit是iOS 26中的新功能。方法签名请对照最新的Xcode 26 beta SDK进行验证。
向家长或监护人请求权限以修改儿童的通信规则。PermissionKit可创建通信安全体验,让儿童能够请求放宽家长设置的通信限制。支持Swift 6.2 / iOS 26+。

Contents

目录

Setup

设置

Import
PermissionKit
. No special entitlements are required.
swift
import PermissionKit
Platform availability: iOS 26+, iPadOS 26+, macOS 26+.
导入
PermissionKit
,无需特殊权限。
swift
import PermissionKit
平台支持: iOS 26+, iPadOS 26+, macOS 26+。

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
PermissionKit管理以下流程:
  1. 儿童在应用中遇到通信限制
  2. 应用创建一个
    PermissionQuestion
    来描述请求
  3. 系统向儿童展示该问题,由其发送给家长
  4. 家长审核并批准或拒绝请求
  5. 应用收到包含家长决定的
    PermissionResponse

Key Types

关键类型

TypeRole
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 whether communication limits apply
SignificantAppUpdateTopic
Topic for significant app update permission requests
类型作用
AskCenter
管理权限请求与响应的单例类
PermissionQuestion
描述所请求的权限内容
PermissionResponse
家长的决定(批准或拒绝)
PermissionChoice
具体的答复结果(批准/拒绝)
PermissionButton
触发权限请求流程的SwiftUI按钮
CommunicationTopic
与通信相关的权限请求主题
CommunicationHandle
电话号码、邮箱或自定义标识符
CommunicationLimits
检查是否应用了通信限制
SignificantAppUpdateTopic
与重大应用更新相关的权限请求主题

Checking Communication Limits

检查通信限制

Before presenting a permission request, check if communication limits are enabled and whether the handle is known.
swift
import PermissionKit

func checkCommunicationStatus(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> {
    let limits = CommunicationLimits.current
    return await limits.knownHandles(in: handles)
}
在发起权限请求前,先检查是否启用了通信限制,以及联系人标识符是否已被识别。
swift
import PermissionKit

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

// 批量检查多个联系人标识符
func filterKnownHandles(_ handles: Set<CommunicationHandle>) async -> Set<CommunicationHandle> {
    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: "friend@example.com",
    kind: .emailAddress
)

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

let emailHandle = CommunicationHandle(
    value: "friend@example.com",
    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: "friend@example.com", kind: .emailAddress)
]
let multiQuestion = PermissionQuestion<CommunicationTopic>(handles: handles)
结合联系人信息和通信操作类型构建
PermissionQuestion
swift
// 单个联系人的请求问题
let handle = CommunicationHandle(value: "+1234567890", kind: .phoneNumber)
let question = PermissionQuestion<CommunicationTopic>(handle: handle)

// 多个联系人的请求问题
let handles = [
    CommunicationHandle(value: "+1234567890", kind: .phoneNumber),
    CommunicationHandle(value: "friend@example.com", kind: .emailAddress)
]
let multiQuestion = PermissionQuestion<CommunicationTopic>(handles: handles)

Using CommunicationTopic with Person Information

结合个人信息使用CommunicationTopic

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)
提供显示名称和头像,让权限请求提示更清晰。
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

通信操作类型

ActionDescription
.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
操作类型描述
.message
短信通信
.audioCall
语音通话
.videoCall
视频通话
.call
通用通话
.chat
聊天通信
.follow
关注用户
.beFollowed
允许被关注
.friend
好友请求
.connect
连接请求
.communicate
通用通信

Requesting Permission with AskCenter

通过AskCenter请求权限

Use
AskCenter.shared
to present the permission request to the child.
swift
import PermissionKit

func requestPermission(
    for question: PermissionQuestion<CommunicationTopic>,
    in viewController: UIViewController
) async {
    do {
        try await AskCenter.shared.ask(question, in: viewController)
        // Question was presented to the child
    } catch let error as AskError {
        switch error {
        case .communicationLimitsNotEnabled:
            // Communication limits not active -- no permission needed
            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
        }
    }
}
使用
AskCenter.shared
向儿童展示权限请求。
swift
import PermissionKit

func requestPermission(
    for question: PermissionQuestion<CommunicationTopic>,
    in viewController: UIViewController
) async {
    do {
        try await AskCenter.shared.ask(question, in: viewController)
        // 请求问题已展示给儿童
    } catch let error as AskError {
        switch error {
        case .communicationLimitsNotEnabled:
            // 未启用通信限制——无需申请权限
            break
        case .contactSyncNotSetup:
            // 未配置联系人同步
            break
        case .invalidQuestion:
            // 请求问题格式错误
            break
        case .notAvailable:
            // 当前设备不支持PermissionKit
            break
        case .systemError(let underlying):
            print("系统错误: \(underlying)")
        case .unknown:
            break
        @unknown default:
            break
        }
    }
}

SwiftUI Integration with PermissionButton

通过PermissionButton集成SwiftUI

PermissionButton
is a SwiftUI view that triggers the permission flow when tapped.
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)

        PermissionButton(question: question) {
            Label("Ask to Message", systemImage: "message")
        }
    }
}
PermissionButton
是一个SwiftUI视图,点击时会触发权限请求流程。
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)

        PermissionButton(question: question) {
            Label("申请发送消息权限", systemImage: "message")
        }
    }
}

PermissionButton with Custom Topic

自定义主题的PermissionButton

swift
struct CustomPermissionView: View {
    var body: some View {
        let personInfo = CommunicationTopic.PersonInformation(
            handle: CommunicationHandle(value: "user456", kind: .custom),
            nameComponents: nil,
            avatarImage: nil
        )
        let topic = CommunicationTopic(
            personInformation: [personInfo],
            actions: [.follow]
        )
        let question = PermissionQuestion<CommunicationTopic>(
            communicationTopic: topic
        )

        PermissionButton(question: question) {
            Text("Ask to Follow")
        }
    }
}
swift
struct CustomPermissionView: View {
    var body: some View {
        let personInfo = CommunicationTopic.PersonInformation(
            handle: CommunicationHandle(value: "user456", kind: .custom),
            nameComponents: nil,
            avatarImage: nil
        )
        let topic = CommunicationTopic(
            personInformation: [personInfo],
            actions: [.follow]
        )
        let question = PermissionQuestion<CommunicationTopic>(
            communicationTopic: topic
        )

        PermissionButton(question: question) {
            Text("申请关注权限")
        }
    }
}

Handling Responses

处理权限响应

Listen for permission responses asynchronously.
swift
func observeResponses() async {
    let responses = AskCenter.shared.responses(for: CommunicationTopic.self)

    for await response in responses {
        let choice = response.choice
        let question = response.question

        switch choice.answer {
        case .approval:
            // Parent approved -- enable communication
            print("Approved for topic: \(question.topic)")
        case .denial:
            // Parent denied -- keep restriction
            print("Denied")
        @unknown default:
            break
        }
    }
}
异步监听权限响应结果。
swift
func observeResponses() async {
    let responses = AskCenter.shared.responses(for: CommunicationTopic.self)

    for await response in responses {
        let choice = response.choice
        let question = response.question

        switch choice.answer {
        case .approval:
            // 家长已批准——启用通信功能
            print("已批准主题: \(question.topic)")
        case .denial:
            // 家长已拒绝——保持限制
            print("已拒绝")
        @unknown default:
            break
        }
    }
}

PermissionChoice Properties

PermissionChoice属性

swift
let choice: PermissionChoice = response.choice
print("Answer: \(choice.answer)")  // .approval or .denial
print("Choice ID: \(choice.id)")
print("Title: \(choice.title)")

// Convenience statics
let approved = PermissionChoice.approve
let declined = PermissionChoice.decline
swift
let choice: PermissionChoice = response.choice
print("答复结果: \(choice.answer)")  // .approval 或 .denial
print("选择ID: \(choice.id)")
print("标题: \(choice.title)")

// 便捷静态属性
let approved = PermissionChoice.approve
let declined = PermissionChoice.decline

Significant App Update Topic

重大应用更新主题

Request permission for significant app updates that require parental approval.
swift
let updateTopic = SignificantAppUpdateTopic(
    description: "This update adds multiplayer chat features"
)

let question = PermissionQuestion<SignificantAppUpdateTopic>(
    significantAppUpdateTopic: updateTopic
)

// Present the question
try await AskCenter.shared.ask(question, in: viewController)

// Listen for responses
for await response in AskCenter.shared.responses(for: SignificantAppUpdateTopic.self) {
    switch response.choice.answer {
    case .approval:
        // Proceed with update
        break
    case .denial:
        // Skip update
        break
    @unknown default:
        break
    }
}
当应用有重大更新需要家长批准时,可发起权限请求。
swift
let updateTopic = SignificantAppUpdateTopic(
    description: "本次更新新增多人聊天功能"
)

let question = PermissionQuestion<SignificantAppUpdateTopic>(
    significantAppUpdateTopic: updateTopic
)

// 展示请求问题
try await AskCenter.shared.ask(question, in: viewController)

// 监听响应结果
for await response in AskCenter.shared.responses(for: SignificantAppUpdateTopic.self) {
    switch response.choice.answer {
    case .approval:
        // 继续执行更新
        break
    case .denial:
        // 跳过更新
        break
    @unknown default:
        break
    }
}

Common Mistakes

常见错误

DON'T: Skip checking if communication limits are enabled

错误做法:跳过检查通信限制是否启用

If communication limits are not enabled, calling
ask
throws
.communicationLimitsNotEnabled
. Check first or handle the error.
swift
// WRONG: Assuming limits are always active
try await AskCenter.shared.ask(question, in: viewController)

// CORRECT: Handle the case where limits are not enabled
do {
    try await AskCenter.shared.ask(question, in: viewController)
} catch AskError.communicationLimitsNotEnabled {
    // Communication limits not active -- allow communication directly
    allowCommunication()
} catch {
    handleError(error)
}
如果未启用通信限制,调用
ask
方法会抛出
.communicationLimitsNotEnabled
错误。请提前检查或处理该错误。
swift
// 错误:假设通信限制始终处于启用状态
try await AskCenter.shared.ask(question, in: viewController)

// 正确:处理未启用通信限制的情况
do {
    try await AskCenter.shared.ask(question, in: viewController)
} catch AskError.communicationLimitsNotEnabled {
    // 未启用通信限制——直接允许通信
    allowCommunication()
} catch {
    handleError(error)
}

DON'T: Ignore AskError cases

错误做法:忽略AskError的具体类型

Each error case requires different handling.
swift
// WRONG: Catch-all with no user feedback
do {
    try await AskCenter.shared.ask(question, in: viewController)
} catch {
    print(error)
}

// CORRECT: Handle each case
do {
    try await AskCenter.shared.ask(question, in: viewController)
} catch let error as AskError {
    switch error {
    case .communicationLimitsNotEnabled:
        allowCommunication()
    case .contactSyncNotSetup:
        showContactSyncPrompt()
    case .invalidQuestion:
        showInvalidQuestionAlert()
    case .notAvailable:
        showUnavailableMessage()
    case .systemError(let underlying):
        showSystemError(underlying)
    case .unknown:
        showGenericError()
    @unknown default:
        break
    }
}
每种错误类型需要不同的处理方式。
swift
// 错误:统一捕获错误但不提供用户反馈
do {
    try await AskCenter.shared.ask(question, in: viewController)
} catch {
    print(error)
}

// 正确:分别处理每种错误类型
do {
    try await AskCenter.shared.ask(question, in: viewController)
} catch let error as AskError {
    switch error {
    case .communicationLimitsNotEnabled:
        allowCommunication()
    case .contactSyncNotSetup:
        showContactSyncPrompt()
    case .invalidQuestion:
        showInvalidQuestionAlert()
    case .notAvailable:
        showUnavailableMessage()
    case .systemError(let underlying):
        showSystemError(underlying)
    case .unknown:
        showGenericError()
    @unknown default:
        break
    }
}

DON'T: Create questions with empty handles

错误做法:创建包含空联系人列表的请求问题

A question with no handles or person information is invalid.
swift
// WRONG: Empty handles array
let question = PermissionQuestion<CommunicationTopic>(handles: [])  // Invalid

// CORRECT: Provide at least one handle
let handle = CommunicationHandle(value: "+1234567890", kind: .phoneNumber)
let question = PermissionQuestion<CommunicationTopic>(handle: handle)
没有联系人标识符或个人信息的请求问题是无效的。
swift
// 错误:空联系人数组
let question = PermissionQuestion<CommunicationTopic>(handles: [])  // 无效

// 正确:至少提供一个联系人标识符
let handle = CommunicationHandle(value: "+1234567890", kind: .phoneNumber)
let question = PermissionQuestion<CommunicationTopic>(handle: handle)

DON'T: Forget to observe responses

错误做法:忘记监听响应结果

Presenting a question without listening for the response means you never know if the parent approved.
swift
// WRONG: Fire and forget
try await AskCenter.shared.ask(question, in: viewController)

// CORRECT: Observe responses
Task {
    for await response in AskCenter.shared.responses(for: CommunicationTopic.self) {
        handleResponse(response)
    }
}
try await AskCenter.shared.ask(question, in: viewController)
发起请求但不监听响应,将无法得知家长是否批准。
swift
// 错误:发起请求后不再处理
try await AskCenter.shared.ask(question, in: viewController)

// 正确:监听响应结果
Task {
    for await response in AskCenter.shared.responses(for: CommunicationTopic.self) {
        handleResponse(response)
    }
}
try await AskCenter.shared.ask(question, in: viewController)

DON'T: Use deprecated CommunicationLimitsButton

错误做法:使用已废弃的CommunicationLimitsButton

Use
PermissionButton
instead of the deprecated
CommunicationLimitsButton
.
swift
// WRONG: Deprecated
CommunicationLimitsButton(question: question) {
    Text("Ask Permission")
}

// CORRECT: Use PermissionButton
PermissionButton(question: question) {
    Text("Ask Permission")
}
请使用
PermissionButton
替代已废弃的
CommunicationLimitsButton
swift
// 错误:使用已废弃的API
CommunicationLimitsButton(question: question) {
    Text("申请权限")
}

// 正确:使用PermissionButton
PermissionButton(question: question) {
    Text("申请权限")
}

Review Checklist

审核清单

  • AskError.communicationLimitsNotEnabled
    handled to allow fallback
  • AskError
    cases handled individually with appropriate user feedback
  • CommunicationHandle
    created with correct
    Kind
    (phone, email, custom)
  • PermissionQuestion
    includes at least one handle or person information
  • AskCenter.shared.responses(for:)
    observed to receive parent decisions
  • PermissionButton
    used instead of deprecated
    CommunicationLimitsButton
  • Person information includes name components for a clear permission prompt
  • Communication actions match the app's actual communication capabilities
  • Response handling updates UI on the main actor
  • Error states provide clear guidance to the user
  • 已处理
    AskError.communicationLimitsNotEnabled
    错误,提供回退方案
  • 已分别处理
    AskError
    的每种错误类型,并提供合适的用户反馈
  • 创建
    CommunicationHandle
    时使用了正确的类型(电话、邮箱、自定义)
  • PermissionQuestion
    包含至少一个联系人标识符或个人信息
  • 已通过
    AskCenter.shared.responses(for:)
    监听家长的决定
  • 使用
    PermissionButton
    替代已废弃的
    CommunicationLimitsButton
  • 个人信息中包含姓名组件,让权限请求提示更清晰
  • 通信操作类型与应用实际的通信能力匹配
  • 响应处理逻辑在主线程更新UI
  • 错误状态向用户提供清晰的指导

References

参考资料