Install
Terminal · npx$
npx skills add https://github.com/microsoft/github-copilot-for-azure --skill azure-rbacWorks with Paperclip
How Permissionkit fits into a Paperclip company.
Permissionkit drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
S
SaaS FactoryPaired
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
$27$59
Explore packSource file
SKILL.md446 linesExpandCollapse
---name: permissionkitdescription: "Create child communication safety experiences using PermissionKit to request parental permission for children. Use when building apps that involve child-to-contact communication, need to check communication limits, request parent/guardian approval, or handle permission responses for minors."--- # 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 communicationrules. PermissionKit creates communication safety experiences that let childrenask for exceptions to communication limits set by their parents. TargetsSwift 6.3 / iOS 26+. ## Contents - [Setup](#setup)- [Core Concepts](#core-concepts)- [Checking Communication Limits](#checking-communication-limits)- [Creating Permission Questions](#creating-permission-questions)- [Requesting Permission with AskCenter](#requesting-permission-with-askcenter)- [SwiftUI Integration with PermissionButton](#swiftui-integration-with-permissionbutton)- [Handling Responses](#handling-responses)- [Significant App Update Topic](#significant-app-update-topic)- [Common Mistakes](#common-mistakes)- [Review Checklist](#review-checklist)- [References](#references) ## Setup Import `PermissionKit`. No special entitlements are required. ```swiftimport PermissionKit``` **Platform availability:** iOS 26+, iPadOS 26+, macOS 26+. ## Core Concepts PermissionKit manages a flow where: 1. A child encounters a communication limit in your app2. Your app creates a `PermissionQuestion` describing the request3. The system presents the question to the child for them to send to their parent4. The parent reviews and approves or denies the request5. Your app receives a `PermissionResponse` with the parent's decision ### Key Types | 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` | 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 | ## Checking Communication Limits Before presenting a permission request, check if communication limits areenabled and whether the handle is known. ```swiftimport PermissionKit func checkCommunicationStatus(for handle: CommunicationHandle) async -> Bool { let limits = CommunicationLimits.current let isKnown = await limits.isKnownHandle(handle) return isKnown} // Check multiple handles at oncefunc filterKnownHandles(_ handles: Set<CommunicationHandle>) async -> Set<CommunicationHandle> { let limits = CommunicationLimits.current return await limits.knownHandles(in: handles)}``` ### Creating Communication Handles ```swiftlet 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 communicationaction type. ```swift// Question for a single contactlet handle = CommunicationHandle(value: "+1234567890", kind: .phoneNumber)let question = PermissionQuestion<CommunicationTopic>(handle: handle) // Question for multiple contactslet handles = [ CommunicationHandle(value: "+1234567890", kind: .phoneNumber), CommunicationHandle(value: "friend@example.com", kind: .emailAddress)]let multiQuestion = PermissionQuestion<CommunicationTopic>(handles: handles)``` ### Using CommunicationTopic with Person Information Provide display names and avatars for a richer permission prompt. ```swiftlet 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 | Action | Description ||---|---|| `.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 | ## Requesting Permission with AskCenter Use `AskCenter.shared` to present the permission request to the child. ```swiftimport 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 } }}``` ## SwiftUI Integration with PermissionButton `PermissionButton` is a SwiftUI view that triggers the permission flowwhen tapped. ```swiftimport SwiftUIimport 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 with Custom Topic ```swiftstruct 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") } }}``` ## Handling Responses Listen for permission responses asynchronously. ```swiftfunc 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 } }}``` ### PermissionChoice Properties ```swiftlet choice: PermissionChoice = response.choiceprint("Answer: \(choice.answer)") // .approval or .denialprint("Choice ID: \(choice.id)")print("Title: \(choice.title)") // Convenience staticslet approved = PermissionChoice.approvelet declined = PermissionChoice.decline``` ## Significant App Update Topic Request permission for significant app updates that require parental approval. ```swiftlet updateTopic = SignificantAppUpdateTopic( description: "This update adds multiplayer chat features") let question = PermissionQuestion<SignificantAppUpdateTopic>( significantAppUpdateTopic: updateTopic) // Present the questiontry await AskCenter.shared.ask(question, in: viewController) // Listen for responsesfor 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 }}``` ## 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 activetry await AskCenter.shared.ask(question, in: viewController) // CORRECT: Handle the case where limits are not enableddo { try await AskCenter.shared.ask(question, in: viewController)} catch AskError.communicationLimitsNotEnabled { // Communication limits not active -- allow communication directly allowCommunication()} catch { handleError(error)}``` ### DON'T: Ignore AskError cases Each error case requires different handling. ```swift// WRONG: Catch-all with no user feedbackdo { try await AskCenter.shared.ask(question, in: viewController)} catch { print(error)} // CORRECT: Handle each casedo { 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 arraylet question = PermissionQuestion<CommunicationTopic>(handles: []) // Invalid // CORRECT: Provide at least one handlelet 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 neverknow if the parent approved. ```swift// WRONG: Fire and forgettry await AskCenter.shared.ask(question, in: viewController) // CORRECT: Observe responsesTask { 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 Use `PermissionButton` instead of the deprecated `CommunicationLimitsButton`. ```swift// WRONG: DeprecatedCommunicationLimitsButton(question: question) { Text("Ask Permission")} // CORRECT: Use PermissionButtonPermissionButton(question: question) { Text("Ask Permission")}``` ## 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 ## References - Extended patterns (response handling, multi-topic, UIKit): [references/permissionkit-patterns.md](references/permissionkit-patterns.md)- [PermissionKit framework](https://sosumi.ai/documentation/permissionkit)- [AskCenter](https://sosumi.ai/documentation/permissionkit/askcenter)- [PermissionQuestion](https://sosumi.ai/documentation/permissionkit/permissionquestion)- [PermissionButton](https://sosumi.ai/documentation/permissionkit/permissionbutton)- [PermissionResponse](https://sosumi.ai/documentation/permissionkit/permissionresponse)- [CommunicationTopic](https://sosumi.ai/documentation/permissionkit/communicationtopic)- [CommunicationHandle](https://sosumi.ai/documentation/permissionkit/communicationhandle)- [CommunicationLimits](https://sosumi.ai/documentation/permissionkit/communicationlimits)- [SignificantAppUpdateTopic](https://sosumi.ai/documentation/permissionkit/significantappupdatetopic)- [AskError](https://sosumi.ai/documentation/permissionkit/askerror)- [Creating a communication experience](https://sosumi.ai/documentation/permissionkit/creating-a-communication-experience)