{"id":"5afce087-1b99-4647-8ae3-f95cb30039f3","shortId":"jLMtDQ","kind":"skill","title":"Permissionkit","tagline":"Swift Ios Skills skill by Dpearson2699","description":"# PermissionKit\n\n> **Note:** PermissionKit is new in iOS 26. Method signatures should be verified against the latest Xcode 26 beta SDK.\n\nRequest permission from a parent or guardian to modify a child's communication\nrules. PermissionKit creates communication safety experiences that let children\nask for exceptions to communication limits set by their parents. Targets\nSwift 6.3 / iOS 26+.\n\n## Contents\n\n- [Setup](#setup)\n- [Core Concepts](#core-concepts)\n- [Checking Communication Limits](#checking-communication-limits)\n- [Creating Permission Questions](#creating-permission-questions)\n- [Requesting Permission with AskCenter](#requesting-permission-with-askcenter)\n- [SwiftUI Integration with PermissionButton](#swiftui-integration-with-permissionbutton)\n- [Handling Responses](#handling-responses)\n- [Significant App Update Topic](#significant-app-update-topic)\n- [Common Mistakes](#common-mistakes)\n- [Review Checklist](#review-checklist)\n- [References](#references)\n\n## Setup\n\nImport `PermissionKit`. No special entitlements are required.\n\n```swift\nimport PermissionKit\n```\n\n**Platform availability:** iOS 26+, iPadOS 26+, macOS 26+.\n\n## Core Concepts\n\nPermissionKit manages a flow where:\n\n1. A child encounters a communication limit in your app\n2. Your app creates a `PermissionQuestion` describing the request\n3. The system presents the question to the child for them to send to their parent\n4. The parent reviews and approves or denies the request\n5. Your app receives a `PermissionResponse` with the parent's decision\n\n### Key Types\n\n| Type | Role |\n|---|---|\n| `AskCenter` | Singleton that manages permission requests and responses |\n| `PermissionQuestion` | Describes the permission being requested |\n| `PermissionResponse` | The parent's decision (approval or denial) |\n| `PermissionChoice` | The specific answer (approve/decline) |\n| `PermissionButton` | SwiftUI button that triggers the permission flow |\n| `CommunicationTopic` | Topic for communication-related permission requests |\n| `CommunicationHandle` | A phone number, email, or custom identifier |\n| `CommunicationLimits` | Checks whether communication limits apply |\n| `SignificantAppUpdateTopic` | Topic for significant app update permission requests |\n\n## Checking Communication Limits\n\nBefore presenting a permission request, check if communication limits are\nenabled and whether the handle is known.\n\n```swift\nimport PermissionKit\n\nfunc checkCommunicationStatus(for handle: CommunicationHandle) async -> Bool {\n    let limits = CommunicationLimits.current\n    let isKnown = await limits.isKnownHandle(handle)\n    return isKnown\n}\n\n// Check multiple handles at once\nfunc filterKnownHandles(_ handles: Set<CommunicationHandle>) async -> Set<CommunicationHandle> {\n    let limits = CommunicationLimits.current\n    return await limits.knownHandles(in: handles)\n}\n```\n\n### Creating Communication Handles\n\n```swift\nlet phoneHandle = CommunicationHandle(\n    value: \"+1234567890\",\n    kind: .phoneNumber\n)\n\nlet emailHandle = CommunicationHandle(\n    value: \"friend@example.com\",\n    kind: .emailAddress\n)\n\nlet customHandle = CommunicationHandle(\n    value: \"user123\",\n    kind: .custom\n)\n```\n\n## Creating Permission Questions\n\nBuild a `PermissionQuestion` with the contact information and communication\naction type.\n\n```swift\n// Question for a single contact\nlet handle = CommunicationHandle(value: \"+1234567890\", kind: .phoneNumber)\nlet question = PermissionQuestion<CommunicationTopic>(handle: handle)\n\n// Question for multiple contacts\nlet handles = [\n    CommunicationHandle(value: \"+1234567890\", kind: .phoneNumber),\n    CommunicationHandle(value: \"friend@example.com\", kind: .emailAddress)\n]\nlet multiQuestion = PermissionQuestion<CommunicationTopic>(handles: handles)\n```\n\n### Using CommunicationTopic with Person Information\n\nProvide display names and avatars for a richer permission prompt.\n\n```swift\nlet personInfo = CommunicationTopic.PersonInformation(\n    handle: CommunicationHandle(value: \"+1234567890\", kind: .phoneNumber),\n    nameComponents: {\n        var name = PersonNameComponents()\n        name.givenName = \"Alex\"\n        name.familyName = \"Smith\"\n        return name\n    }(),\n    avatarImage: nil\n)\n\nlet topic = CommunicationTopic(\n    personInformation: [personInfo],\n    actions: [.message, .audioCall]\n)\n\nlet question = PermissionQuestion<CommunicationTopic>(communicationTopic: topic)\n```\n\n### Communication Actions\n\n| Action | Description |\n|---|---|\n| `.message` | Text messaging |\n| `.audioCall` | Voice call |\n| `.videoCall` | Video call |\n| `.call` | Generic call |\n| `.chat` | Chat communication |\n| `.follow` | Follow a user |\n| `.beFollowed` | Allow being followed |\n| `.friend` | Friend request |\n| `.connect` | Connection request |\n| `.communicate` | Generic communication |\n\n## Requesting Permission with AskCenter\n\nUse `AskCenter.shared` to present the permission request to the child.\n\n```swift\nimport PermissionKit\n\nfunc requestPermission(\n    for question: PermissionQuestion<CommunicationTopic>,\n    in viewController: UIViewController\n) async {\n    do {\n        try await AskCenter.shared.ask(question, in: viewController)\n        // Question was presented to the child\n    } catch let error as AskError {\n        switch error {\n        case .communicationLimitsNotEnabled:\n            // Communication limits not active -- no permission needed\n            break\n        case .contactSyncNotSetup:\n            // Contact sync not configured\n            break\n        case .invalidQuestion:\n            // Question is malformed\n            break\n        case .notAvailable:\n            // PermissionKit not available on this device\n            break\n        case .systemError(let underlying):\n            print(\"System error: \\(underlying)\")\n        case .unknown:\n            break\n        @unknown default:\n            break\n        }\n    }\n}\n```\n\n## SwiftUI Integration with PermissionButton\n\n`PermissionButton` is a SwiftUI view that triggers the permission flow\nwhen tapped.\n\n```swift\nimport SwiftUI\nimport PermissionKit\n\nstruct ContactPermissionView: View {\n    let handle = CommunicationHandle(value: \"+1234567890\", kind: .phoneNumber)\n\n    var body: some View {\n        let question = PermissionQuestion<CommunicationTopic>(handle: handle)\n\n        PermissionButton(question: question) {\n            Label(\"Ask to Message\", systemImage: \"message\")\n        }\n    }\n}\n```\n\n### PermissionButton with Custom Topic\n\n```swift\nstruct CustomPermissionView: View {\n    var body: some View {\n        let personInfo = CommunicationTopic.PersonInformation(\n            handle: CommunicationHandle(value: \"user456\", kind: .custom),\n            nameComponents: nil,\n            avatarImage: nil\n        )\n        let topic = CommunicationTopic(\n            personInformation: [personInfo],\n            actions: [.follow]\n        )\n        let question = PermissionQuestion<CommunicationTopic>(\n            communicationTopic: topic\n        )\n\n        PermissionButton(question: question) {\n            Text(\"Ask to Follow\")\n        }\n    }\n}\n```\n\n## Handling Responses\n\nListen for permission responses asynchronously.\n\n```swift\nfunc observeResponses() async {\n    let responses = AskCenter.shared.responses(for: CommunicationTopic.self)\n\n    for await response in responses {\n        let choice = response.choice\n        let question = response.question\n\n        switch choice.answer {\n        case .approval:\n            // Parent approved -- enable communication\n            print(\"Approved for topic: \\(question.topic)\")\n        case .denial:\n            // Parent denied -- keep restriction\n            print(\"Denied\")\n        @unknown default:\n            break\n        }\n    }\n}\n```\n\n### PermissionChoice Properties\n\n```swift\nlet choice: PermissionChoice = response.choice\nprint(\"Answer: \\(choice.answer)\")  // .approval or .denial\nprint(\"Choice ID: \\(choice.id)\")\nprint(\"Title: \\(choice.title)\")\n\n// Convenience statics\nlet approved = PermissionChoice.approve\nlet declined = PermissionChoice.decline\n```\n\n## Significant App Update Topic\n\nRequest permission for significant app updates that require parental approval.\n\n```swift\nlet updateTopic = SignificantAppUpdateTopic(\n    description: \"This update adds multiplayer chat features\"\n)\n\nlet question = PermissionQuestion<SignificantAppUpdateTopic>(\n    significantAppUpdateTopic: updateTopic\n)\n\n// Present the question\ntry await AskCenter.shared.ask(question, in: viewController)\n\n// Listen for responses\nfor await response in AskCenter.shared.responses(for: SignificantAppUpdateTopic.self) {\n    switch response.choice.answer {\n    case .approval:\n        // Proceed with update\n        break\n    case .denial:\n        // Skip update\n        break\n    @unknown default:\n        break\n    }\n}\n```\n\n## Common Mistakes\n\n### DON'T: Skip checking if communication limits are enabled\n\nIf communication limits are not enabled, calling `ask` throws\n`.communicationLimitsNotEnabled`. Check first or handle the error.\n\n```swift\n// WRONG: Assuming limits are always active\ntry await AskCenter.shared.ask(question, in: viewController)\n\n// CORRECT: Handle the case where limits are not enabled\ndo {\n    try await AskCenter.shared.ask(question, in: viewController)\n} catch AskError.communicationLimitsNotEnabled {\n    // Communication limits not active -- allow communication directly\n    allowCommunication()\n} catch {\n    handleError(error)\n}\n```\n\n### DON'T: Ignore AskError cases\n\nEach error case requires different handling.\n\n```swift\n// WRONG: Catch-all with no user feedback\ndo {\n    try await AskCenter.shared.ask(question, in: viewController)\n} catch {\n    print(error)\n}\n\n// CORRECT: Handle each case\ndo {\n    try await AskCenter.shared.ask(question, in: viewController)\n} catch let error as AskError {\n    switch error {\n    case .communicationLimitsNotEnabled:\n        allowCommunication()\n    case .contactSyncNotSetup:\n        showContactSyncPrompt()\n    case .invalidQuestion:\n        showInvalidQuestionAlert()\n    case .notAvailable:\n        showUnavailableMessage()\n    case .systemError(let underlying):\n        showSystemError(underlying)\n    case .unknown:\n        showGenericError()\n    @unknown default:\n        break\n    }\n}\n```\n\n### DON'T: Create questions with empty handles\n\nA question with no handles or person information is invalid.\n\n```swift\n// WRONG: Empty handles array\nlet question = PermissionQuestion<CommunicationTopic>(handles: [])  // Invalid\n\n// CORRECT: Provide at least one handle\nlet handle = CommunicationHandle(value: \"+1234567890\", kind: .phoneNumber)\nlet question = PermissionQuestion<CommunicationTopic>(handle: handle)\n```\n\n### DON'T: Forget to observe responses\n\nPresenting a question without listening for the response means you never\nknow if the parent approved.\n\n```swift\n// WRONG: Fire and forget\ntry await AskCenter.shared.ask(question, in: viewController)\n\n// CORRECT: Observe responses\nTask {\n    for await response in AskCenter.shared.responses(for: CommunicationTopic.self) {\n        handleResponse(response)\n    }\n}\ntry await AskCenter.shared.ask(question, in: viewController)\n```\n\n### DON'T: Use deprecated CommunicationLimitsButton\n\nUse `PermissionButton` instead of the deprecated `CommunicationLimitsButton`.\n\n```swift\n// WRONG: Deprecated\nCommunicationLimitsButton(question: question) {\n    Text(\"Ask Permission\")\n}\n\n// CORRECT: Use PermissionButton\nPermissionButton(question: question) {\n    Text(\"Ask Permission\")\n}\n```\n\n## Review Checklist\n\n- [ ] `AskError.communicationLimitsNotEnabled` handled to allow fallback\n- [ ] `AskError` cases handled individually with appropriate user feedback\n- [ ] `CommunicationHandle` created with correct `Kind` (phone, email, custom)\n- [ ] `PermissionQuestion` includes at least one handle or person information\n- [ ] `AskCenter.shared.responses(for:)` observed to receive parent decisions\n- [ ] `PermissionButton` used instead of deprecated `CommunicationLimitsButton`\n- [ ] Person information includes name components for a clear permission prompt\n- [ ] Communication actions match the app's actual communication capabilities\n- [ ] Response handling updates UI on the main actor\n- [ ] Error states provide clear guidance to the user\n\n## References\n\n- Extended patterns (response handling, multi-topic, UIKit): [references/permissionkit-patterns.md](references/permissionkit-patterns.md)\n- [PermissionKit framework](https://sosumi.ai/documentation/permissionkit)\n- [AskCenter](https://sosumi.ai/documentation/permissionkit/askcenter)\n- [PermissionQuestion](https://sosumi.ai/documentation/permissionkit/permissionquestion)\n- [PermissionButton](https://sosumi.ai/documentation/permissionkit/permissionbutton)\n- [PermissionResponse](https://sosumi.ai/documentation/permissionkit/permissionresponse)\n- [CommunicationTopic](https://sosumi.ai/documentation/permissionkit/communicationtopic)\n- [CommunicationHandle](https://sosumi.ai/documentation/permissionkit/communicationhandle)\n- [CommunicationLimits](https://sosumi.ai/documentation/permissionkit/communicationlimits)\n- [SignificantAppUpdateTopic](https://sosumi.ai/documentation/permissionkit/significantappupdatetopic)\n- [AskError](https://sosumi.ai/documentation/permissionkit/askerror)\n- [Creating a communication experience](https://sosumi.ai/documentation/permissionkit/creating-a-communication-experience)","tags":["permissionkit","swift","ios","skills","dpearson2699"],"capabilities":["skill","source-dpearson2699","category-swift-ios-skills"],"categories":["swift-ios-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dpearson2699/swift-ios-skills/permissionkit","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"install_from":"skills.sh"}},"qualityScore":"0.300","qualityRationale":"deterministic score 0.30 from registry signals: · indexed on skills.sh · published under dpearson2699/swift-ios-skills","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T03:40:33.816Z","embedding":null,"createdAt":"2026-04-18T20:34:37.039Z","updatedAt":"2026-04-22T03:40:33.816Z","lastSeenAt":"2026-04-22T03:40:33.816Z","tsv":"'+1234567890':349,390,406,441,625,1012 '/documentation/permissionkit)':1197 '/documentation/permissionkit/askcenter)':1201 '/documentation/permissionkit/askerror)':1233 '/documentation/permissionkit/communicationhandle)':1221 '/documentation/permissionkit/communicationlimits)':1225 '/documentation/permissionkit/communicationtopic)':1217 '/documentation/permissionkit/creating-a-communication-experience)':1240 '/documentation/permissionkit/permissionbutton)':1209 '/documentation/permissionkit/permissionquestion)':1205 '/documentation/permissionkit/permissionresponse)':1213 '/documentation/permissionkit/significantappupdatetopic)':1229 '1':157 '2':167 '26':15,25,64,145,147,149 '3':176 '4':192 '5':202 '6.3':62 'action':378,461,470,471,676,1158 'activ':556,867,895 'actor':1173 'actual':1163 'add':790 'alex':449 'allow':493,896,1107 'allowcommun':899,953 'alway':866 'answer':242,749 'app':111,116,166,169,204,278,770,777,1161 'appli':273 'appropri':1114 'approv':197,236,720,722,726,751,764,782,821,1041 'approve/decline':243 'array':996 'ask':50,641,687,852,1091,1100 'askcent':90,95,217,508,1198 'askcenter.shared':510 'askcenter.shared.ask':534,804,870,886,926,940,1049,1068 'askcenter.shared.responses':703,815,1061,1134 'askerror':548,906,948,1109,1230 'askerror.communicationlimitsnotenabled':891,1104 'assum':863 'async':310,331,530,700 'asynchron':696 'audiocal':463,476 'avail':143,578 'avatar':428 'avatarimag':454,669 'await':317,337,533,707,803,812,869,885,925,939,1048,1058,1067 'befollow':492 'beta':26 'bodi':629,655 'bool':311 'break':560,567,573,582,593,596,740,825,830,833,974 'build':369 'button':246 'call':478,481,482,484,851 'capabl':1165 'case':551,561,568,574,583,591,719,730,820,826,877,907,910,936,951,954,957,960,963,969,1110 'catch':544,890,900,917,930,944 'catch-al':916 'category-swift-ios-skills' 'chat':485,486,792 'check':73,77,269,282,290,322,839,855 'checkcommunicationstatus':306 'checking-communication-limit':76 'checklist':125,128,1103 'child':38,159,184,518,543 'children':49 'choic':712,745,755 'choice.answer':718,750 'choice.id':757 'choice.title':760 'clear':1154,1177 'common':119,122,834 'common-mistak':121 'communic':40,44,54,74,78,162,256,271,283,292,342,377,469,487,502,504,553,724,841,846,892,897,1157,1164,1236 'communication-rel':255 'communicationhandl':260,309,347,354,361,388,404,409,439,623,662,1010,1117,1218 'communicationlimit':268,1222 'communicationlimits.current':314,335 'communicationlimitsbutton':1076,1083,1087,1146 'communicationlimitsnoten':552,854,952 'communicationtop':252,420,458,467,673,681,1214 'communicationtopic.personinformation':437,660 'communicationtopic.self':705,1063 'compon':1151 'concept':69,72,151 'configur':566 'connect':499,500 'contact':374,385,401,563 'contactpermissionview':619 'contactsyncnotsetup':562,955 'content':65 'conveni':761 'core':68,71,150 'core-concept':70 'correct':874,933,1002,1053,1093,1120 'creat':43,80,84,170,341,366,977,1118,1234 'creating-permission-quest':83 'custom':266,365,648,666,1124 'customhandl':360 'custompermissionview':652 'decis':212,235,1140 'declin':767 'default':595,739,832,973 'deni':199,733,737 'denial':238,731,753,827 'deprec':1075,1082,1086,1145 'describ':173,226 'descript':472,787 'devic':581 'differ':912 'direct':898 'display':425 'dpearson2699':7 'email':264,1123 'emailaddress':358,413 'emailhandl':353 'empti':980,994 'enabl':295,723,844,850,882 'encount':160 'entitl':136 'error':546,550,589,860,902,909,932,946,950,1174 'except':52 'experi':46,1237 'extend':1183 'fallback':1108 'featur':793 'feedback':922,1116 'filterknownhandl':328 'fire':1044 'first':856 'flow':155,251,610 'follow':488,489,495,677,689 'forget':1022,1046 'framework':1194 'friend':496,497 'friend@example.com':356,411 'func':305,327,522,698 'generic':483,503 'guardian':34 'guidanc':1178 'handl':105,108,299,308,319,324,329,340,343,387,396,397,403,417,418,438,622,635,636,661,690,858,875,913,934,981,986,995,1000,1007,1009,1018,1019,1105,1111,1130,1167,1186 'handleerror':901 'handlerespons':1064 'handling-respons':107 'id':756 'identifi':267 'ignor':905 'import':132,140,303,520,614,616 'includ':1126,1149 'individu':1112 'inform':375,423,989,1133,1148 'instead':1079,1143 'integr':97,102,598 'invalid':991,1001 'invalidquest':569,958 'io':3,14,63,144 'ipado':146 'isknown':316,321 'keep':734 'key':213 'kind':350,357,364,391,407,412,442,626,665,1013,1121 'know':1037 'known':301 'label':640 'latest':23 'least':1005,1128 'let':48,312,315,333,345,352,359,386,393,402,414,435,456,464,545,585,621,632,658,671,678,701,711,714,744,763,766,784,794,945,965,997,1008,1015 'limit':55,75,79,163,272,284,293,313,334,554,842,847,864,879,893 'limits.isknownhandle':318 'limits.knownhandles':338 'listen':692,808,1030 'maco':148 'main':1172 'malform':572 'manag':153,220 'match':1159 'mean':1034 'messag':462,473,475,643,645 'method':16 'mistak':120,123,835 'modifi':36 'multi':1188 'multi-top':1187 'multipl':323,400 'multiplay':791 'multiquest':415 'name':426,446,453,1150 'name.familyname':450 'name.givenname':448 'namecompon':444,667 'need':559 'never':1036 'new':12 'nil':455,668,670 'notavail':575,961 'note':9 'number':263 'observ':1024,1054,1136 'observerespons':699 'one':1006,1129 'parent':32,59,191,194,210,233,721,732,781,1040,1139 'pattern':1184 'permiss':29,81,85,88,93,221,228,250,258,280,288,367,432,506,514,558,609,694,774,1092,1101,1155 'permissionbutton':99,104,244,600,601,637,646,683,1078,1095,1096,1141,1206 'permissionchoic':239,741,746 'permissionchoice.approve':765 'permissionchoice.decline':768 'permissionkit':1,8,10,42,133,141,152,304,521,576,617,1193 'permissionquest':172,225,371,395,416,466,526,634,680,796,999,1017,1125,1202 'permissionrespons':207,231,1210 'person':422,988,1132,1147 'personinfo':436,460,659,675 'personinform':459,674 'personnamecompon':447 'phone':262,1122 'phonehandl':346 'phonenumb':351,392,408,443,627,1014 'platform':142 'present':179,286,512,540,799,1026 'print':587,725,736,748,754,758,931 'proceed':822 'prompt':433,1156 'properti':742 'provid':424,1003,1176 'question':82,86,181,368,381,394,398,465,525,535,538,570,633,638,639,679,684,685,715,795,801,805,871,887,927,941,978,983,998,1016,1028,1050,1069,1088,1089,1097,1098 'question.topic':729 'receiv':205,1138 'refer':129,130,1182 'references/permissionkit-patterns.md':1191,1192 'relat':257 'request':28,87,92,175,201,222,230,259,281,289,498,501,505,515,773 'requesting-permission-with-askcent':91 'requestpermiss':523 'requir':138,780,911 'respons':106,109,224,691,695,702,708,710,810,813,1025,1033,1055,1059,1065,1166,1185 'response.choice':713,747 'response.choice.answer':819 'response.question':716 'restrict':735 'return':320,336,452 'review':124,127,195,1102 'review-checklist':126 'richer':431 'role':216 'rule':41 'safeti':45 'sdk':27 'send':188 'set':56,330,332 'setup':66,67,131 'showcontactsyncprompt':956 'showgenericerror':971 'showinvalidquestionalert':959 'showsystemerror':967 'showunavailablemessag':962 'signatur':17 'signific':110,115,277,769,776 'significant-app-update-top':114 'significantappupdatetop':274,786,797,1226 'significantappupdatetopic.self':817 'singl':384 'singleton':218 'skill':4,5 'skip':828,838 'smith':451 'sosumi.ai':1196,1200,1204,1208,1212,1216,1220,1224,1228,1232,1239 'sosumi.ai/documentation/permissionkit)':1195 'sosumi.ai/documentation/permissionkit/askcenter)':1199 'sosumi.ai/documentation/permissionkit/askerror)':1231 'sosumi.ai/documentation/permissionkit/communicationhandle)':1219 'sosumi.ai/documentation/permissionkit/communicationlimits)':1223 'sosumi.ai/documentation/permissionkit/communicationtopic)':1215 'sosumi.ai/documentation/permissionkit/creating-a-communication-experience)':1238 'sosumi.ai/documentation/permissionkit/permissionbutton)':1207 'sosumi.ai/documentation/permissionkit/permissionquestion)':1203 'sosumi.ai/documentation/permissionkit/permissionresponse)':1211 'sosumi.ai/documentation/permissionkit/significantappupdatetopic)':1227 'source-dpearson2699' 'special':135 'specif':241 'state':1175 'static':762 'struct':618,651 'swift':2,61,139,302,344,380,434,519,613,650,697,743,783,861,914,992,1042,1084 'swiftui':96,101,245,597,604,615 'swiftui-integration-with-permissionbutton':100 'switch':549,717,818,949 'sync':564 'system':178,588 'systemerror':584,964 'systemimag':644 'tap':612 'target':60 'task':1056 'text':474,686,1090,1099 'throw':853 'titl':759 'topic':113,118,253,275,457,468,649,672,682,728,772,1189 'tri':532,802,868,884,924,938,1047,1066 'trigger':248,607 'type':214,215,379 'ui':1169 'uikit':1190 'uiviewcontrol':529 'under':586,590,966,968 'unknown':592,594,738,831,970,972 'updat':112,117,279,771,778,789,824,829,1168 'updatetop':785,798 'use':419,509,1074,1077,1094,1142 'user':491,921,1115,1181 'user123':363 'user456':664 'valu':348,355,362,389,405,410,440,624,663,1011 'var':445,628,654 'verifi':20 'video':480 'videocal':479 'view':605,620,631,653,657 'viewcontrol':528,537,807,873,889,929,943,1052,1071 'voic':477 'whether':270,297 'without':1029 'wrong':862,915,993,1043,1085 'xcode':24","prices":[{"id":"794cc306-ac93-4c89-a308-0ff12c09366c","listingId":"5afce087-1b99-4647-8ae3-f95cb30039f3","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dpearson2699","category":"swift-ios-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T20:34:37.039Z"}],"sources":[{"listingId":"5afce087-1b99-4647-8ae3-f95cb30039f3","source":"github","sourceId":"dpearson2699/swift-ios-skills/permissionkit","sourceUrl":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/permissionkit","isPrimary":false,"firstSeenAt":"2026-04-18T22:01:11.139Z","lastSeenAt":"2026-04-22T00:53:44.141Z"},{"listingId":"5afce087-1b99-4647-8ae3-f95cb30039f3","source":"skills_sh","sourceId":"dpearson2699/swift-ios-skills/permissionkit","sourceUrl":"https://skills.sh/dpearson2699/swift-ios-skills/permissionkit","isPrimary":true,"firstSeenAt":"2026-04-18T20:34:37.039Z","lastSeenAt":"2026-04-22T03:40:33.816Z"}],"details":{"listingId":"5afce087-1b99-4647-8ae3-f95cb30039f3","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dpearson2699","slug":"permissionkit","source":"skills_sh","category":"swift-ios-skills","skills_sh_url":"https://skills.sh/dpearson2699/swift-ios-skills/permissionkit"},"updatedAt":"2026-04-22T03:40:33.816Z"}}