permissionkit by dpearson2699/swift-ios-skills
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill permissionkit注意: PermissionKit 是 iOS 26 中的新功能。方法签名应针对最新的 Xcode 26 beta SDK 进行验证。
向家长或监护人请求修改儿童通信规则的权限。PermissionKit 创建通信安全体验,让儿童可以请求对其父母设置的通信限制进行例外处理。目标平台:Swift 6.2 / iOS 26+。
导入 PermissionKit。无需特殊授权。
import PermissionKit
平台可用性: iOS 26+, iPadOS 26+, macOS 26+。
PermissionKit 管理以下流程:
PermissionQuestionPermissionResponse广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 类型 | 角色 |
|---|---|
AskCenter | 管理权限请求和响应的单例 |
PermissionQuestion | 描述正在请求的权限 |
PermissionResponse | 家长的决定(批准或拒绝) |
PermissionChoice | 具体答案(批准/拒绝) |
PermissionButton | 触发权限流程的 SwiftUI 按钮 |
CommunicationTopic | 通信相关权限请求的主题 |
CommunicationHandle | 电话号码、电子邮件或自定义标识符 |
CommunicationLimits | 检查通信限制是否适用 |
SignificantAppUpdateTopic | 重大应用更新权限请求的主题 |
在呈现权限请求之前,请检查通信限制是否已启用以及句柄是否已知。
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)
}
let phoneHandle = CommunicationHandle(
value: "+1234567890",
kind: .phoneNumber
)
let emailHandle = CommunicationHandle(
value: "friend@example.com",
kind: .emailAddress
)
let customHandle = CommunicationHandle(
value: "user123",
kind: .custom
)
使用联系信息和通信操作类型构建 PermissionQuestion。
// 针对单个联系人的问题
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)
提供显示名称和头像,以获得更丰富的权限提示。
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)
| 操作 | 描述 |
|---|---|
.message | 短信 |
.audioCall | 语音通话 |
.videoCall | 视频通话 |
.call | 通用通话 |
.chat | 聊天通信 |
.follow | 关注用户 |
.beFollowed | 允许被关注 |
.friend | 好友请求 |
.connect | 连接请求 |
.communicate | 通用通信 |
使用 AskCenter.shared 向儿童呈现权限请求。
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
}
}
}
PermissionButton 是一个 SwiftUI 视图,点击时触发权限流程。
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")
}
}
}
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("请求关注")
}
}
}
异步监听权限响应。
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
}
}
}
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
请求需要家长批准的重大应用更新的权限。
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
}
}
如果通信限制未启用,调用 ask 会抛出 .communicationLimitsNotEnabled。请先检查或处理该错误。
// 错误:假设限制始终处于活动状态
try await AskCenter.shared.ask(question, in: viewController)
// 正确:处理限制未启用的情况
do {
try await AskCenter.shared.ask(question, in: viewController)
} catch AskError.communicationLimitsNotEnabled {
// 通信限制未激活 -- 直接允许通信
allowCommunication()
} catch {
handleError(error)
}
每种错误情况都需要不同的处理方式。
// 错误:笼统捕获,没有用户反馈
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
}
}
没有句柄或人员信息的问题是无效的。
// 错误:空句柄数组
let question = PermissionQuestion<CommunicationTopic>(handles: []) // 无效
// 正确:提供至少一个句柄
let handle = CommunicationHandle(value: "+1234567890", kind: .phoneNumber)
let question = PermissionQuestion<CommunicationTopic>(handle: handle)
呈现问题而不监听响应意味着您永远不会知道家长是否批准。
// 错误:发送后即忘
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)
使用 PermissionButton 而不是已弃用的 CommunicationLimitsButton。
// 错误:已弃用
CommunicationLimitsButton(question: question) {
Text("请求权限")
}
// 正确:使用 PermissionButton
PermissionButton(question: question) {
Text("请求权限")
}
AskError.communicationLimitsNotEnabled 以允许回退AskError 情况已单独处理,并提供适当的用户反馈CommunicationHandle 使用正确的 Kind(电话、电子邮件、自定义)创建PermissionQuestion 包含至少一个句柄或人员信息AskCenter.shared.responses(for:) 以接收家长决定PermissionButton 而不是已弃用的 CommunicationLimitsButtonreferences/permissionkit-patterns.md每周安装量
354
代码仓库
GitHub 星标数
283
首次出现
2026年3月8日
安全审计
安装于
codex350
opencode347
github-copilot347
amp347
cline347
kimi-cli347
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+.
Import PermissionKit. No special entitlements are required.
import PermissionKit
Platform availability: iOS 26+, iPadOS 26+, macOS 26+.
PermissionKit manages a flow where:
PermissionQuestion describing the requestPermissionResponse with the parent's decision| 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 |
Before presenting a permission request, check if communication limits are enabled and whether the handle is known.
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)
}
let phoneHandle = CommunicationHandle(
value: "+1234567890",
kind: .phoneNumber
)
let emailHandle = CommunicationHandle(
value: "friend@example.com",
kind: .emailAddress
)
let customHandle = CommunicationHandle(
value: "user123",
kind: .custom
)
Build a PermissionQuestion with the contact information and communication action type.
// 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)
Provide display names and avatars for a richer permission prompt.
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)
| Action | Description |
|---|---|
.message | Text messaging |
.audioCall | Voice call |
.videoCall | Video call |
.call | Generic call |
.chat | Chat communication |
.follow | Follow a user |
Use AskCenter.shared to present the permission request to the child.
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
}
}
}
PermissionButton is a SwiftUI view that triggers the permission flow when tapped.
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")
}
}
}
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")
}
}
}
Listen for permission responses asynchronously.
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
}
}
}
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
Request permission for significant app updates that require parental approval.
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
}
}
If communication limits are not enabled, calling ask throws .communicationLimitsNotEnabled. Check first or handle the error.
// 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)
}
Each error case requires different handling.
// 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
}
}
A question with no handles or person information is invalid.
// 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)
Presenting a question without listening for the response means you never know if the parent approved.
// 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)
Use PermissionButton instead of the deprecated CommunicationLimitsButton.
// WRONG: Deprecated
CommunicationLimitsButton(question: question) {
Text("Ask Permission")
}
// CORRECT: Use PermissionButton
PermissionButton(question: question) {
Text("Ask Permission")
}
AskError.communicationLimitsNotEnabled handled to allow fallbackAskError cases handled individually with appropriate user feedbackCommunicationHandle created with correct Kind (phone, email, custom)PermissionQuestion includes at least one handle or person informationAskCenter.shared.responses(for:) observed to receive parent decisionsPermissionButton used instead of deprecated CommunicationLimitsButtonreferences/permissionkit-patterns.mdWeekly Installs
354
Repository
GitHub Stars
283
First Seen
Mar 8, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex350
opencode347
github-copilot347
amp347
cline347
kimi-cli347
xdrop 文件传输脚本:Bun 环境下安全上传下载工具,支持加密分享
24,700 周安装
| 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 |
.beFollowed| Allow being followed |
.friend | Friend request |
.connect | Connection request |
.communicate | Generic communication |