permissionkit

permissionkit

熱門

使用 PermissionKit 建立兒童通訊安全體驗,向家長請求兒童的通訊權限。適用於開發涉及兒童與聯絡人通訊、需要檢查通訊限制、請求家長或監護人核准,或處理未成年人權限回應的 App。

936星標
47分支
更新於 2026/7/15
SKILL.md
唯讀
名稱
permissionkit
描述

使用 PermissionKit 建立兒童通訊安全體驗,向家長請求兒童的通訊權限。適用於開發涉及兒童與聯絡人通訊、需要檢查通訊限制、請求家長或監護人核准,或處理未成年人權限回應的 App。

PermissionKit

向家長或監護人請求修改兒童通訊規則的權限。PermissionKit 建立通訊安全體驗,讓兒童可以請求家長設定的通訊限制例外。

PermissionKit 通訊體驗僅限於 iMessage 使用。請將其用於家長/監護人核准流程,而非一般應用程式內聯絡人權限、審核或聊天安全框架。

目錄

可用性與設定

匯入 PermissionKit。請勿自行發明 PermissionKit 授權金鑰;在新增簽署需求前,請先查閱最新的 Apple 文件與 Xcode 功能。

import PermissionKit

使用此集中版本對照表,並與目前 SDK 進行比對:

層級 API iOS/iPadOS/Mac Catalyst/macOS/visionOS
核心 主題、處理代碼、問題、回應、選項、CommunicationLimits 26.0+
錯誤 AskError 26.1+
呈現 AskCenter、詢問/回應序列、PermissionButton、重大更新主題 26.2+

核心概念

PermissionKit 管理以下流程:

  1. 兒童在您的 App 中遇到通訊限制
  2. 您的 App 建立一個 PermissionQuestion 來描述請求
  3. 系統向兒童呈現問題,讓他們傳送給家長
  4. 家長審查並核准或拒絕請求
  5. 您的 App 收到包含家長決定的 PermissionResponse

關鍵類型

類型 角色
AskCenter 管理權限請求和回應的單例
PermissionQuestion 描述正在請求的權限
PermissionResponse 家長的決定(核准或拒絕)
PermissionChoice 具體答案(核准/拒絕)
PermissionButton 觸發權限流程的 SwiftUI 按鈕
CommunicationTopic 通訊相關權限請求的主題
CommunicationHandle 電話號碼、電子郵件或自訂識別碼
CommunicationLimits 檢查系統已知哪些通訊處理代碼
SignificantAppUpdateTopic 重大應用程式更新權限請求的主題

檢查通訊限制

使用 CommunicationLimits.current 檢查系統是否已知您的 App 的通訊處理代碼。這不是「通訊限制是否啟用?」的探測。如果限制未啟用,AskCenter.shared.ask(_:in:) 會拋出 AskError.communicationLimitsNotEnabled;在詢問時請處理該路徑。

knownHandles(in:) 也要求呼叫的 App 具有非 nil 且非空的 bundle identifier。修正後的程式碼應在呼叫前先保護 Bundle.main.bundleIdentifier

import PermissionKit

func needsPermissionPrompt(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> {
    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: "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)

使用 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)
        // 問題傳送流程已啟動;請另外等待 responses(for:)。
    } catch let error as AskError {
        switch error {
        case .communicationLimitsNotEnabled:
            // 通訊限制未啟用 -- 繼續正常 App 流程。
            break
        case .contactSyncNotSetup:
            // 聯絡人同步未設定
            break
        case .invalidQuestion:
            // 問題格式錯誤
            break
        case .notAvailable:
            // 此裝置上無法使用 PermissionKit
            break
        case .systemError(let underlying):
            print("系統錯誤:\(underlying)")
        case .unknown:
            break
        @unknown default:
            break
        }
    }
}

SwiftUI 整合 PermissionButton

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("請求傳送訊息", systemImage: "message")
        }
    }
}

如需更豐富的 SwiftUI 流程、自訂主題和長期存在的管理器,請參閱 references/permissionkit-patterns.md

處理回應

非同步監聽權限回應。透過 question.id 追蹤待處理問題,並為 UI 提供重試或過期路徑,因為兒童可以取消 iMessage 傳送流程而不產生回應。
在結合已知處理代碼檢查與回應處理時,請保留 knownHandles(in:) 中的 bundle identifier 保護。

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

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

func expireIfStillPending(_ id: UUID) {
    guard requestStates[id] == .pending else { return }
    requestStates[id] = .expired
    // 重新啟用詢問或顯示重試/已取消 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:
            // 家長核准 -- 啟用通訊
            requestStates[question.id] = .approved
            print("已核准主題:\(question.topic)")
        case .denial:
            // 家長拒絕 -- 維持限制
            requestStates[question.id] = .denied
            print("已拒絕")
        @unknown default:
            break
        }
    }
}

PermissionChoice 屬性

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

重大應用程式更新主題

請求需要家長核准的重大應用程式更新的權限。您的 App 根據適用法規決定何謂重大更新,並應諮詢合格法律顧問以了解合規解釋。使用簡潔易懂的描述,說明家長核准的具體變更。

let updateTopic = SignificantAppUpdateTopic(
    description: "此更新新增了多人聊天功能"
)

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

// 呈現問題
try await AskCenter.shared.ask(question, in: viewController)
requestStates[question.id] = .pending
scheduleExpiration(for: question.id)

// 監聽回應
for await response in AskCenter.shared.responses(for: SignificantAppUpdateTopic.self) {
    switch response.choice.answer {
    case .approval:
        // 繼續更新
        requestStates[response.question.id] = .approved
    case .denial:
        // 跳過更新
        requestStates[response.question.id] = .denied
    @unknown default:
        break
    }
}

// 如果在待處理視窗到期前未收到回應,請保持更新封鎖或提供重試。兒童取消不會產生拒絕回應。

常見錯誤

錯誤 修正
將已知處理代碼查詢視為限制已啟用的證據 將詢問操作中的 .communicationLimitsNotEnabled 視為正常的未設定路徑處理。
AskError 被合併為單一訊息 區分限制未啟用、聯絡人同步、問題無效、不可用、系統和未知情況。
問題沒有處理代碼或個人資訊 在呈現前驗證至少有一個有意義的通訊目標。
詢問後即忘記 觀察回應和待處理狀態,同時允許兒童取消/放棄。
使用已棄用的 CommunicationLimitsButton 改用 PermissionButton

審查清單

  • [ ] 在選擇 PermissionKit 前已了解僅限 iMessage 路由
  • [ ] 對使用的每個 API 套用集中可用性對照表
  • [ ] CommunicationHandle 使用正確的 Kind(電話、電子郵件、自訂)建立
  • [ ] 已知處理代碼範例在 knownHandles(in:) 前保護非 nil、非空的 bundle identifier
  • [ ] 個人資訊包含姓名元件以提供清晰的權限提示
  • [ ] 通訊動作符合 App 實際的通訊能力
  • [ ] 回應處理在主執行緒更新 UI
  • [ ] 錯誤狀態為使用者提供明確指引

參考資料