使用 PermissionKit 建立兒童通訊安全體驗,向家長請求兒童的通訊權限。適用於開發涉及兒童與聯絡人通訊、需要檢查通訊限制、請求家長或監護人核准,或處理未成年人權限回應的 App。
PermissionKit
向家長或監護人請求修改兒童通訊規則的權限。PermissionKit 建立通訊安全體驗,讓兒童可以請求家長設定的通訊限制例外。
PermissionKit 通訊體驗僅限於 iMessage 使用。請將其用於家長/監護人核准流程,而非一般應用程式內聯絡人權限、審核或聊天安全框架。
目錄
- 可用性與設定
- 核心概念
- 檢查通訊限制
- 建立權限問題
- 使用 AskCenter 請求權限
- SwiftUI 整合 PermissionButton
- 處理回應
- 重大應用程式更新主題
- 常見錯誤
- 審查清單
- 參考資料
可用性與設定
匯入 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 管理以下流程:
- 兒童在您的 App 中遇到通訊限制
- 您的 App 建立一個
PermissionQuestion來描述請求 - 系統向兒童呈現問題,讓他們傳送給家長
- 家長審查並核准或拒絕請求
- 您的 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
- [ ] 錯誤狀態為使用者提供明確指引






