mcpField
P
permissionkit

permissionkit

github.com/dpearson2699开发1.2k3.6k 次安装13 周前更新
代码仓库

关于 permissionkit

permissionkit 是指导开发者使用 PermissionKit 构建家长或监护人审批流程的技能,适用于儿童通信限制的例外申请。内容包括提问、请求发送及异步处理审批结果,并明确该流程仅通过 iMessage 提供。

主要功能

  1. 01检查系统已知的通信对象
  2. 02创建通信权限问题
  3. 03通过 AskCenter 或 PermissionButton 发起请求
  4. 04异步接收并处理审批结果
  5. 05处理待定、取消与错误状态

适用场景

  1. 01儿童请求联系受限制的对象
  2. 02为通信例外设计家长审批流程
  3. 03请求家长批准重大应用更新
  4. 04跟踪请求等待及过期状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dpearson2699/swift-ios-skills --skill permissionkit

安装后在 Claude Code 中输入/permissionkit确认已加载。

附带文件evals/evals.jsonreferences/permissionkit-patterns.md

详细介绍

PermissionKit

向家长或监护人请求修改孩子通信规则的许可。PermissionKit 创建通信安全体验,让孩子可以请求例外,以突破家长设定的通信限制。

PermissionKit 通信体验仅可通过 iMessage 使用。 请将其用于家长/监护人审批流程,而不要将其用作通用的应用内联系人权限、内容审核或聊天安全框架。

目录

可用性和设置

导入 PermissionKit。不要自行编造 PermissionKit 权限密钥;添加签名要求前,请核对 Apple 当前文档和 Xcode 功能。

import PermissionKit

请使用此集中版本矩阵,并对照当前 SDK 进行核实:

层级APIiOS/iPadOS/Mac Catalyst/macOS/visionOS
核心主题、句柄、问题、响应、选项、CommunicationLimits26.0+
错误AskError26.1+
展示AskCenter、询问/响应序列、PermissionButton、重要更新主题26.2+

核心概念

PermissionKit 管理以下流程:

  1. 孩子在你的应用中遇到通信限制
  2. 你的应用创建描述请求内容的 PermissionQuestion
  3. 系统向孩子展示问题,由孩子发送给家长
  4. 家长查看请求并批准或拒绝
  5. 你的应用收到包含家长决定的 PermissionResponse
关键类型
类型作用
AskCenter管理许可请求和响应的单例
PermissionQuestion描述所请求的许可
PermissionResponse家长的决定(批准或拒绝)
PermissionChoice具体答复(批准/拒绝)
PermissionButton触发许可流程的 SwiftUI 按钮
CommunicationTopic与通信相关的许可请求主题
CommunicationHandle电话号码、电子邮件或自定义标识符
CommunicationLimits检查系统已知的通信句柄
SignificantAppUpdateTopic重要应用更新许可请求主题

检查通信限制

使用 CommunicationLimits.current 检查系统是否已知与你的应用相关的通信句柄。这并不是用于探测“是否启用了通信限制”。如果未启用限制,AskCenter.shared.ask(_:in:) 会抛出 AskError.communicationLimitsNotEnabled;发起请求时请处理此情况。

knownHandles(in:) 还要求调用应用具有非空的 bundle 标识符。修正后的代码应在调用该方法前检查 Bundle.main.bundleIdentifier。

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)
}
创建通信句柄
let phoneHandle = CommunicationHandle(
    value: "+1234567890",
    kind: .phoneNumber
)

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

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

创建许可问题

使用联系人信息和通信操作类型构建 PermissionQuestion。

// 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)
将 CommunicationTopic 与人物信息搭配使用

提供显示名称和头像,让许可提示更丰富。

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 请求权限

使用 AskCenter.shared 请求孩子将权限问题发送给其父母或监护人。异步 ask 调用会启动发送流程;之后通过 responses(for:) 接收父母的决定。如果孩子取消发送流程,系统不会为该问题发送 PermissionResponse。

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
        }
    }
}

使用 PermissionButton 集成 SwiftUI

PermissionButton 是一个 SwiftUI 视图,点击后会触发权限流程。它使用与 AskCenter 相同的响应模型:观察响应,并对待处理或已取消状态进行建模,而不要假定每次点击都会产生父母的决定。

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")
        }
    }
}

如需了解更丰富的 SwiftUI 流程、自定义主题和长期运行的管理器,请阅读 references/permissionkit-patterns.md。

处理响应

异步监听权限响应。按 question.id 跟踪待处理问题,并为 UI 提供重试或过期处理方式,因为孩子可以取消 iMessage 发送流程而不产生响应。 将已知句柄检查与响应处理结合时,沿用 knownHandles(in:) 中的 bundle 标识符保护逻辑。

enum PermissionRequestState {
    case pending, approved, denied, expired
}

var requestStates: [UUID: PermissionRequestState] = [:]

func expireIfStillPending(_ id: UUID) {
    guard requestStates[id] == .pending else { return }
    requestStates[id] = .expired
    // Re-enable asking or show retry/canceled UI.
}

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
            requestStates[question.id] = .approved
            print("Approved for topic: \(question.topic)")
        case .denial:
            // Parent denied -- keep restriction
            requestStates[question.id] = .denied
            print("Denied")
        @unknown default:
            break
        }
    }
}
PermissionChoice 属性
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

重大 App 更新主题

请求对需要家长批准的重大 App 更新进行授权。你的 App 应根据适用法规确定哪些情况属于重大更新,并应咨询具备相应资质的法律顾问,以解读合规要求。 使用简洁易懂的描述,明确说明家长要批准的具体变更。

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)
requestStates[question.id] = .pending
scheduleExpiration(for: question.id)

// Listen for responses
for await response in AskCenter.shared.responses(for: SignificantAppUpdateTopic.self) {
    switch response.choice.answer {
    case .approval:
        // Proceed with update
        requestStates[response.question.id] = .approved
    case .denial:
        // Skip update
        requestStates[response.question.id] = .denied
    @unknown default:
        break
    }
}

// If no response arrives before your pending window expires, keep the update
// blocked or offer a retry. Child cancellation produces no denial response.

常见错误

错误修正方法
将已知句柄查询视为已启用限制的证明将 ask 操作返回的 .communicationLimitsNotEnabled 作为常见的未配置情况处理。
将 AskError 合并为一条消息区分限制未启用、联系人同步、问题无效、不可用、系统和未知情况。
问题未包含句柄或人员信息在展示前,至少验证一个有意义的通信目标。
Ask 调用即发即忘观察响应和待处理状态,同时允许孩子取消或放弃。
使用已弃用的 CommunicationLimitsButton使用 PermissionButton。

检查清单

  • 选择 PermissionKit 前,已了解仅通过 iMessage 路由的限制
  • 对正在使用的每个 API,均应用了集中管理的可用性矩阵
  • 使用正确的 Kind(phone、email、custom)创建 CommunicationHandle
  • 已知句柄示例在调用 knownHandles(in:) 前,会检查 bundle 标识符非 nil 且非空
  • 人员信息包含姓名组成部分,以便清晰呈现权限提示
  • 通信操作与 App 的实际通信能力相符
  • 响应处理会在主线程执行器上更新 UI
  • 错误状态为用户提供清晰的指导

参考资料

评论

支持 Markdown