@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +266 -267
- package/package.json +2 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +68 -68
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,497 +1,379 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: permissionkit
|
|
3
|
-
description: "
|
|
3
|
+
description: "PermissionKit parental approval for minors: CommunicationLimits handle checks, PermissionQuestion, AskCenter requests and responses, PermissionButton, AskError, pending or expired requests. Use when building child communication safety, a child asking to allow a contact, or handling approvals."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# PermissionKit
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
10
|
+
PermissionKit lets a child ask a parent or guardian to change the child's
|
|
11
|
+
communication rules for your app. The typical case: a parent has set
|
|
12
|
+
communication limits, the child wants to talk to someone new, and your app
|
|
13
|
+
turns that wish into a question the parent can approve or decline. The same
|
|
14
|
+
machinery also covers asking a parent to approve a significant app update.
|
|
15
|
+
|
|
16
|
+
What it is not:
|
|
17
|
+
|
|
18
|
+
- It is not a way to ask for in-app contact permission in general, and it is
|
|
19
|
+
not a moderation or chat-safety framework. Use it only when the answer has
|
|
20
|
+
to come from a parent or guardian.
|
|
21
|
+
- The request travels through iMessage and nothing else. Your own chat, DM or
|
|
22
|
+
notification channel cannot carry it.
|
|
23
|
+
- It does not tell you what counts legally as a significant update in a given
|
|
24
|
+
country. That call belongs to the developer, with qualified legal counsel.
|
|
25
|
+
|
|
26
|
+
Examples use Swift 6.3 against the iOS 26 SDK family. The API surface grew
|
|
27
|
+
across three point releases, so check every signature and `@available` line
|
|
28
|
+
against the Xcode 26 SDK you ship with before relying on it.
|
|
29
|
+
|
|
30
|
+
## Setup and availability
|
|
31
|
+
|
|
32
|
+
Add `@preconcurrency import PermissionKit`. The SwiftUI and UIKit/AppKit
|
|
33
|
+
pieces (`PermissionButton`, `AskCenter.ask(_:in:)`) come in through
|
|
34
|
+
cross-import overlays, so importing SwiftUI or UIKit next to PermissionKit is
|
|
35
|
+
enough. The framework's types carry no `Sendable` annotations, so under Swift 6
|
|
36
|
+
a plain import turns every `ask(_:in:)` call from the main actor into a
|
|
37
|
+
"sending 'question' risks causing data races" error; `@preconcurrency` is what
|
|
38
|
+
lets the examples below compile.
|
|
39
|
+
|
|
40
|
+
Do not add made-up entitlement keys for PermissionKit. If a signing
|
|
41
|
+
requirement exists, it will be in Apple's documentation and in the Xcode
|
|
42
|
+
capabilities list; confirm it there first.
|
|
43
|
+
|
|
44
|
+
When you review code or write guidance, name the exact tier instead of saying
|
|
45
|
+
"iOS 26+". Platforms for every tier: iOS, iPadOS, Mac Catalyst, macOS and
|
|
46
|
+
visionOS.
|
|
47
|
+
|
|
48
|
+
| Tier | What becomes available |
|
|
49
|
+
|---|---|
|
|
50
|
+
| 26.0 | `CommunicationTopic`, `CommunicationHandle`, `PermissionQuestion`, `PermissionResponse`, `PermissionChoice`, `CommunicationLimits` |
|
|
51
|
+
| 26.1 | `AskError` |
|
|
52
|
+
| 26.2 | `AskCenter`, `AskCenter.ask(_:in:)`, `AskCenter.responses(for:)`, `PermissionButton`, `SignificantAppUpdateTopic` (and the `AskError.notAvailable` case) |
|
|
37
53
|
|
|
38
|
-
|
|
39
|
-
current Apple documentation and Xcode capabilities before adding signing
|
|
40
|
-
requirements.
|
|
54
|
+
Gate the 26.2 path explicitly:
|
|
41
55
|
|
|
42
56
|
```swift
|
|
43
|
-
|
|
57
|
+
if #available(iOS 26.2, macOS 26.2, visionOS 26.2, *) {
|
|
58
|
+
try await AskCenter.shared.ask(question, in: hostController)
|
|
59
|
+
} else {
|
|
60
|
+
showUpdateRequiredNotice()
|
|
61
|
+
}
|
|
44
62
|
```
|
|
45
63
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
When reviewing or correcting code, state these exact tiers instead of collapsing
|
|
49
|
-
PermissionKit to "iOS 26+":
|
|
50
|
-
|
|
51
|
-
- Core topic, handle, question, response, choice, and `CommunicationLimits`
|
|
52
|
-
APIs: iOS 26.0+, iPadOS 26.0+, Mac Catalyst 26.0+, macOS 26.0+,
|
|
53
|
-
visionOS 26.0+.
|
|
54
|
-
- `AskError`: iOS 26.1+, iPadOS 26.1+, Mac Catalyst 26.1+, macOS 26.1+,
|
|
55
|
-
visionOS 26.1+.
|
|
56
|
-
- `AskCenter`, `AskCenter.ask(_:in:)`, `AskCenter.responses(for:)`,
|
|
57
|
-
`PermissionButton`, and `SignificantAppUpdateTopic`: iOS/iPadOS/
|
|
58
|
-
Mac Catalyst/macOS/visionOS 26.2+.
|
|
59
|
-
|
|
60
|
-
## Core Concepts
|
|
61
|
-
|
|
62
|
-
PermissionKit manages a flow where:
|
|
64
|
+
## How a request flows
|
|
63
65
|
|
|
64
|
-
1.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
1. Inside your app, the child runs into a communication limit (for example,
|
|
67
|
+
tries to message a player they have not been allowed to contact).
|
|
68
|
+
2. Your app builds a `PermissionQuestion`.
|
|
69
|
+
3. The system shows the question to the child, who can send it to a parent.
|
|
70
|
+
4. The parent approves or declines.
|
|
71
|
+
5. Your app later receives a `PermissionResponse` carrying that decision.
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
Types involved:
|
|
71
74
|
|
|
72
75
|
| Type | Role |
|
|
73
76
|
|---|---|
|
|
74
|
-
| `AskCenter` |
|
|
75
|
-
| `PermissionQuestion
|
|
76
|
-
| `PermissionResponse
|
|
77
|
-
| `PermissionChoice` | The
|
|
78
|
-
| `PermissionButton` | SwiftUI
|
|
79
|
-
| `CommunicationTopic` | Topic for communication
|
|
80
|
-
| `CommunicationHandle` |
|
|
81
|
-
| `CommunicationLimits` |
|
|
82
|
-
| `SignificantAppUpdateTopic` | Topic for significant app update
|
|
83
|
-
|
|
84
|
-
##
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
bundle identifier
|
|
93
|
-
before
|
|
77
|
+
| `AskCenter` | Shared object (`AskCenter.shared`) that sends questions and delivers answers |
|
|
78
|
+
| `PermissionQuestion<Topic>` | What is being asked; has a `UUID` `id` |
|
|
79
|
+
| `PermissionResponse<Topic>` | The parent's decision, with `question` and `choice` |
|
|
80
|
+
| `PermissionChoice` | The concrete answer: approve or decline |
|
|
81
|
+
| `PermissionButton` | SwiftUI control that starts the ask flow when tapped |
|
|
82
|
+
| `CommunicationTopic` | Topic for communication requests |
|
|
83
|
+
| `CommunicationHandle` | Phone number, email address or custom identifier of a contact |
|
|
84
|
+
| `CommunicationLimits` | Tells you which handles the system already knows for your app |
|
|
85
|
+
| `SignificantAppUpdateTopic` | Topic for approving a significant app update |
|
|
86
|
+
|
|
87
|
+
## Known handles are not a limits check
|
|
88
|
+
|
|
89
|
+
`CommunicationLimits.current` answers one narrow question: does the system
|
|
90
|
+
already know this handle for my app? It does not tell you whether the parent
|
|
91
|
+
has turned communication limits on. The only reliable signal for "limits are
|
|
92
|
+
off" is `AskError.communicationLimitsNotEnabled`, thrown by
|
|
93
|
+
`AskCenter.shared.ask(_:in:)`, so that path always needs handling.
|
|
94
|
+
|
|
95
|
+
`knownHandles(in:)` needs a bundle identifier that is present and not empty.
|
|
96
|
+
Guard `Bundle.main.bundleIdentifier` before you call it.
|
|
94
97
|
|
|
95
98
|
```swift
|
|
96
|
-
import PermissionKit
|
|
99
|
+
@preconcurrency import PermissionKit
|
|
97
100
|
|
|
98
|
-
func
|
|
99
|
-
let
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
func unknownPlayers(_ ids: [String]) async -> Set<CommunicationHandle> {
|
|
102
|
+
guard let bundleID = Bundle.main.bundleIdentifier, !bundleID.isEmpty else {
|
|
103
|
+
return []
|
|
104
|
+
}
|
|
105
|
+
let handles = Set(ids.map { CommunicationHandle(value: $0, kind: .custom) })
|
|
106
|
+
let known = await CommunicationLimits.current.knownHandles(in: handles)
|
|
107
|
+
return handles.subtracting(known)
|
|
102
108
|
}
|
|
103
109
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
let limits = CommunicationLimits.current
|
|
109
|
-
return await limits.knownHandles(in: handles)
|
|
110
|
+
func isAlreadyKnown(email: String) async -> Bool {
|
|
111
|
+
let handle = CommunicationHandle(value: email, kind: .emailAddress)
|
|
112
|
+
return await CommunicationLimits.current.isKnownHandle(handle)
|
|
110
113
|
}
|
|
111
114
|
```
|
|
112
115
|
|
|
113
|
-
|
|
116
|
+
- `isKnownHandle(_:)` is async and returns `Bool` for one handle.
|
|
117
|
+
- `knownHandles(in:)` takes a `Set<CommunicationHandle>` and returns the
|
|
118
|
+
subset the system knows.
|
|
119
|
+
- `CommunicationHandle(value:kind:)` kinds: `.phoneNumber`, `.emailAddress`,
|
|
120
|
+
`.custom` (your own account or player id). Pick the kind that matches the
|
|
121
|
+
value.
|
|
114
122
|
|
|
115
|
-
|
|
116
|
-
let phoneHandle = CommunicationHandle(
|
|
117
|
-
value: "+1234567890",
|
|
118
|
-
kind: .phoneNumber
|
|
119
|
-
)
|
|
123
|
+
## Building a question
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
value: "friend@example.com",
|
|
123
|
-
kind: .emailAddress
|
|
124
|
-
)
|
|
125
|
+
Quick forms, when a handle is all you have:
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
kind: .
|
|
127
|
+
```swift
|
|
128
|
+
let single = PermissionQuestion<CommunicationTopic>(
|
|
129
|
+
handle: CommunicationHandle(value: "+15550100", kind: .phoneNumber)
|
|
129
130
|
)
|
|
131
|
+
let group = PermissionQuestion<CommunicationTopic>(handles: [
|
|
132
|
+
CommunicationHandle(value: "coach-17", kind: .custom),
|
|
133
|
+
CommunicationHandle(value: "coach-18", kind: .custom),
|
|
134
|
+
])
|
|
130
135
|
```
|
|
131
136
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
A richer prompt shows the parent a name and, optionally, an avatar
|
|
138
|
+
(`CGImage`). Wrap each contact in `CommunicationTopic.PersonInformation`,
|
|
139
|
+
list the actions you actually support, then build the question from the
|
|
140
|
+
topic:
|
|
136
141
|
|
|
137
142
|
```swift
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// Question for multiple contacts
|
|
143
|
-
let handles = [
|
|
144
|
-
CommunicationHandle(value: "+1234567890", kind: .phoneNumber),
|
|
145
|
-
CommunicationHandle(value: "friend@example.com", kind: .emailAddress)
|
|
146
|
-
]
|
|
147
|
-
let multiQuestion = PermissionQuestion<CommunicationTopic>(handles: handles)
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Using CommunicationTopic with Person Information
|
|
143
|
+
var name = PersonNameComponents()
|
|
144
|
+
name.givenName = "Rosa"
|
|
145
|
+
name.familyName = "Lindqvist"
|
|
151
146
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
let personInfo = CommunicationTopic.PersonInformation(
|
|
156
|
-
handle: CommunicationHandle(value: "+1234567890", kind: .phoneNumber),
|
|
157
|
-
nameComponents: {
|
|
158
|
-
var name = PersonNameComponents()
|
|
159
|
-
name.givenName = "Alex"
|
|
160
|
-
name.familyName = "Smith"
|
|
161
|
-
return name
|
|
162
|
-
}(),
|
|
147
|
+
let rosa = CommunicationTopic.PersonInformation(
|
|
148
|
+
handle: CommunicationHandle(value: "player-4410", kind: .custom),
|
|
149
|
+
nameComponents: name,
|
|
163
150
|
avatarImage: nil
|
|
164
151
|
)
|
|
165
|
-
|
|
166
|
-
let topic = CommunicationTopic(
|
|
167
|
-
personInformation: [personInfo],
|
|
168
|
-
actions: [.message, .audioCall]
|
|
169
|
-
)
|
|
170
|
-
|
|
152
|
+
let topic = CommunicationTopic(personInformation: [rosa], actions: [.chat, .friend])
|
|
171
153
|
let question = PermissionQuestion<CommunicationTopic>(communicationTopic: topic)
|
|
172
154
|
```
|
|
173
155
|
|
|
174
|
-
|
|
156
|
+
`CommunicationTopic.Action` values:
|
|
175
157
|
|
|
176
|
-
| Action |
|
|
158
|
+
| Action | Meaning |
|
|
177
159
|
|---|---|
|
|
178
160
|
| `.message` | Text messaging |
|
|
179
161
|
| `.audioCall` | Voice call |
|
|
180
162
|
| `.videoCall` | Video call |
|
|
181
|
-
| `.call` |
|
|
182
|
-
| `.chat` | Chat
|
|
183
|
-
| `.follow` | Follow
|
|
184
|
-
| `.beFollowed` |
|
|
163
|
+
| `.call` | A call of unspecified type |
|
|
164
|
+
| `.chat` | Chat |
|
|
165
|
+
| `.follow` | Follow someone |
|
|
166
|
+
| `.beFollowed` | Let someone follow the child |
|
|
185
167
|
| `.friend` | Friend request |
|
|
186
168
|
| `.connect` | Connection request |
|
|
187
|
-
| `.communicate` |
|
|
169
|
+
| `.communicate` | Communication in general |
|
|
188
170
|
|
|
189
|
-
|
|
171
|
+
Only list actions your app really offers; the parent is approving exactly
|
|
172
|
+
those.
|
|
190
173
|
|
|
191
|
-
|
|
192
|
-
to their parent or guardian. The async `ask` call starts the send flow; parent
|
|
193
|
-
decisions arrive later through `responses(for:)`. If the child cancels the send
|
|
194
|
-
flow, the system does not deliver a `PermissionResponse` for that question.
|
|
174
|
+
## Asking through AskCenter
|
|
195
175
|
|
|
196
|
-
|
|
197
|
-
|
|
176
|
+
`AskCenter.shared.ask(_:in:)` is `async throws`. It presents the system flow
|
|
177
|
+
in which the child sends the question; it does not return the parent's
|
|
178
|
+
answer. Decisions arrive later on `responses(for:)`. If the child backs out of
|
|
179
|
+
the send sheet, no `PermissionResponse` ever arrives, so a pending request
|
|
180
|
+
needs its own expiry or retry.
|
|
198
181
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
182
|
+
```swift
|
|
183
|
+
@available(iOS 26.2, *)
|
|
184
|
+
@MainActor
|
|
185
|
+
func requestContact(_ question: PermissionQuestion<CommunicationTopic>,
|
|
186
|
+
from host: UIViewController) async {
|
|
203
187
|
do {
|
|
204
|
-
try await AskCenter.shared.ask(question, in:
|
|
205
|
-
|
|
188
|
+
try await AskCenter.shared.ask(question, in: host)
|
|
189
|
+
pending.markPending(question.id)
|
|
206
190
|
} catch let error as AskError {
|
|
207
191
|
switch error {
|
|
208
192
|
case .communicationLimitsNotEnabled:
|
|
209
|
-
//
|
|
210
|
-
break
|
|
193
|
+
unlockChat() // no limits: carry on normally
|
|
211
194
|
case .contactSyncNotSetup:
|
|
212
|
-
|
|
213
|
-
break
|
|
195
|
+
promptForContactSync()
|
|
214
196
|
case .invalidQuestion:
|
|
215
|
-
//
|
|
216
|
-
break
|
|
197
|
+
reportBrokenQuestion() // e.g. no handles at all
|
|
217
198
|
case .notAvailable:
|
|
218
|
-
|
|
219
|
-
break
|
|
199
|
+
explainUnavailableOnDevice()
|
|
220
200
|
case .systemError(let underlying):
|
|
221
|
-
|
|
201
|
+
showFailure(underlying.localizedDescription)
|
|
222
202
|
case .unknown:
|
|
223
|
-
|
|
203
|
+
showRetryLater()
|
|
224
204
|
@unknown default:
|
|
225
|
-
|
|
205
|
+
showRetryLater()
|
|
226
206
|
}
|
|
207
|
+
} catch {
|
|
208
|
+
showFailure(error.localizedDescription)
|
|
227
209
|
}
|
|
228
210
|
}
|
|
229
211
|
```
|
|
230
212
|
|
|
231
|
-
|
|
213
|
+
In the 26.2 SDK `.systemError` is deprecated because the underlying error is
|
|
214
|
+
now thrown directly; keep the generic `catch` for that reason.
|
|
232
215
|
|
|
233
|
-
|
|
234
|
-
tapped. It uses the same response model as `AskCenter`: observe responses and
|
|
235
|
-
model a pending/canceled state instead of assuming every tap produces a parent
|
|
236
|
-
decision.
|
|
216
|
+
## PermissionButton in SwiftUI
|
|
237
217
|
|
|
238
|
-
|
|
239
|
-
import SwiftUI
|
|
240
|
-
import PermissionKit
|
|
218
|
+
`PermissionButton(question:label:)` starts the same flow when tapped:
|
|
241
219
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
var body: some View {
|
|
246
|
-
let question = PermissionQuestion<CommunicationTopic>(handle: handle)
|
|
247
|
-
|
|
248
|
-
PermissionButton(question: question) {
|
|
249
|
-
Label("Ask to Message", systemImage: "message")
|
|
250
|
-
}
|
|
251
|
-
}
|
|
220
|
+
```swift
|
|
221
|
+
PermissionButton(question: question) {
|
|
222
|
+
Label("Ask to play with Rosa", systemImage: "person.badge.plus")
|
|
252
223
|
}
|
|
253
224
|
```
|
|
254
225
|
|
|
255
|
-
|
|
256
|
-
|
|
226
|
+
It uses the same response model as `AskCenter`. Keep observing
|
|
227
|
+
`responses(for:)`, and keep pending and cancelled states in your model,
|
|
228
|
+
because a tap does not guarantee that any decision comes back. Full screen
|
|
229
|
+
examples are in the
|
|
230
|
+
[patterns reference](references/permissionkit-patterns.md#swiftui-full-flow).
|
|
257
231
|
|
|
258
|
-
## Handling
|
|
232
|
+
## Handling responses
|
|
259
233
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
bundle-identifier guard from `knownHandles(in:)`.
|
|
234
|
+
Track each question by `question.id` (a `UUID`). Since a cancelled send
|
|
235
|
+
produces nothing, give every pending entry a way out: a retry, an expiry, or
|
|
236
|
+
both. When a flow combines known-handle checks with responses, keep the
|
|
237
|
+
bundle-identifier guard from the section above.
|
|
265
238
|
|
|
266
239
|
```swift
|
|
267
|
-
enum
|
|
268
|
-
case pending, approved, denied, expired
|
|
269
|
-
}
|
|
240
|
+
enum RequestState { case pending, approved, denied, expired }
|
|
270
241
|
|
|
271
|
-
|
|
242
|
+
@MainActor @Observable
|
|
243
|
+
final class ContactRequests {
|
|
244
|
+
private(set) var states: [UUID: RequestState] = [:]
|
|
272
245
|
|
|
273
|
-
func
|
|
274
|
-
guard requestStates[id] == .pending else { return }
|
|
275
|
-
requestStates[id] = .expired
|
|
276
|
-
// Re-enable asking or show retry/canceled UI.
|
|
277
|
-
}
|
|
246
|
+
func markPending(_ id: UUID) { states[id] = .pending }
|
|
278
247
|
|
|
279
|
-
func
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
for await response in responses {
|
|
283
|
-
let choice = response.choice
|
|
284
|
-
let question = response.question
|
|
285
|
-
|
|
286
|
-
switch choice.answer {
|
|
287
|
-
case .approval:
|
|
288
|
-
// Parent approved -- enable communication
|
|
289
|
-
requestStates[question.id] = .approved
|
|
290
|
-
print("Approved for topic: \(question.topic)")
|
|
291
|
-
case .denial:
|
|
292
|
-
// Parent denied -- keep restriction
|
|
293
|
-
requestStates[question.id] = .denied
|
|
294
|
-
print("Denied")
|
|
295
|
-
@unknown default:
|
|
296
|
-
break
|
|
297
|
-
}
|
|
248
|
+
func expire(_ id: UUID) {
|
|
249
|
+
guard states[id] == .pending else { return }
|
|
250
|
+
states[id] = .expired // lets the child ask again
|
|
298
251
|
}
|
|
299
|
-
}
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
### PermissionChoice Properties
|
|
303
|
-
|
|
304
|
-
```swift
|
|
305
|
-
let choice: PermissionChoice = response.choice
|
|
306
|
-
print("Answer: \(choice.answer)") // .approval or .denial
|
|
307
|
-
print("Choice ID: \(choice.id)")
|
|
308
|
-
print("Title: \(choice.title)")
|
|
309
|
-
|
|
310
|
-
// Convenience statics
|
|
311
|
-
let approved = PermissionChoice.approve
|
|
312
|
-
let declined = PermissionChoice.decline
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
## Significant App Update Topic
|
|
316
|
-
|
|
317
|
-
Request permission for significant app updates that require parental approval.
|
|
318
|
-
Your app determines what counts as significant based on applicable regulations
|
|
319
|
-
and should consult qualified legal counsel for compliance interpretation.
|
|
320
|
-
Use concise, understandable descriptions that state the concrete change parents
|
|
321
|
-
are approving.
|
|
322
|
-
|
|
323
|
-
```swift
|
|
324
|
-
let updateTopic = SignificantAppUpdateTopic(
|
|
325
|
-
description: "This update adds multiplayer chat features"
|
|
326
|
-
)
|
|
327
252
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
case .approval:
|
|
341
|
-
// Proceed with update
|
|
342
|
-
requestStates[response.question.id] = .approved
|
|
343
|
-
case .denial:
|
|
344
|
-
// Skip update
|
|
345
|
-
requestStates[response.question.id] = .denied
|
|
346
|
-
@unknown default:
|
|
347
|
-
break
|
|
253
|
+
@available(iOS 26.2, *)
|
|
254
|
+
func listen() async {
|
|
255
|
+
for await response in AskCenter.shared.responses(for: CommunicationTopic.self) {
|
|
256
|
+
let id = response.question.id
|
|
257
|
+
switch response.choice.answer {
|
|
258
|
+
case .approval: states[id] = .approved
|
|
259
|
+
case .denial: states[id] = .denied
|
|
260
|
+
@unknown default: states[id] = .denied
|
|
261
|
+
}
|
|
262
|
+
let people = response.question.topic.personInformation
|
|
263
|
+
refreshContacts(people.map(\.handle))
|
|
264
|
+
}
|
|
348
265
|
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// If no response arrives before your pending window expires, keep the update
|
|
352
|
-
// blocked or offer a retry. Child cancellation produces no denial response.
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
## Common Mistakes
|
|
356
|
-
|
|
357
|
-
### DON'T: Treat known-handle checks as enabled-limits checks
|
|
358
266
|
|
|
359
|
-
|
|
360
|
-
replace handling `.communicationLimitsNotEnabled` from `ask(_:in:)`.
|
|
361
|
-
|
|
362
|
-
```swift
|
|
363
|
-
// WRONG: Assuming a handle lookup proves active limits
|
|
364
|
-
let isKnown = await CommunicationLimits.current.isKnownHandle(handle)
|
|
365
|
-
if !isKnown {
|
|
366
|
-
try await AskCenter.shared.ask(question, in: viewController)
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// CORRECT: Handle the case where limits are not enabled
|
|
370
|
-
do {
|
|
371
|
-
try await AskCenter.shared.ask(question, in: viewController)
|
|
372
|
-
} catch AskError.communicationLimitsNotEnabled {
|
|
373
|
-
// Communication limits not active -- continue with normal app flow.
|
|
374
|
-
allowCommunication()
|
|
375
|
-
} catch {
|
|
376
|
-
handleError(error)
|
|
267
|
+
private func refreshContacts(_ handles: [CommunicationHandle]) {}
|
|
377
268
|
}
|
|
378
269
|
```
|
|
379
270
|
|
|
380
|
-
|
|
271
|
+
- `responses(for:)` is an async sequence of `PermissionResponse<Topic>`; each
|
|
272
|
+
element has `.question` and `.choice`.
|
|
273
|
+
- `response.question.topic` gives back the topic you asked about.
|
|
274
|
+
- `choice.answer` is `.approval` (allow the communication) or `.denial` (keep
|
|
275
|
+
the restriction).
|
|
276
|
+
- `PermissionChoice` also exposes `id` and `title`, and the statics
|
|
277
|
+
`PermissionChoice.approve` and `PermissionChoice.decline`.
|
|
381
278
|
|
|
382
|
-
|
|
279
|
+
## Significant app updates
|
|
383
280
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// CORRECT: Handle each case
|
|
393
|
-
do {
|
|
394
|
-
try await AskCenter.shared.ask(question, in: viewController)
|
|
395
|
-
} catch let error as AskError {
|
|
396
|
-
switch error {
|
|
397
|
-
case .communicationLimitsNotEnabled:
|
|
398
|
-
allowCommunication()
|
|
399
|
-
case .contactSyncNotSetup:
|
|
400
|
-
showContactSyncPrompt()
|
|
401
|
-
case .invalidQuestion:
|
|
402
|
-
showInvalidQuestionAlert()
|
|
403
|
-
case .notAvailable:
|
|
404
|
-
showUnavailableMessage()
|
|
405
|
-
case .systemError(let underlying):
|
|
406
|
-
showSystemError(underlying)
|
|
407
|
-
case .unknown:
|
|
408
|
-
showGenericError()
|
|
409
|
-
@unknown default:
|
|
410
|
-
break
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
### DON'T: Create questions with empty handles
|
|
416
|
-
|
|
417
|
-
A question with no handles or person information is invalid.
|
|
281
|
+
`SignificantAppUpdateTopic` asks a parent to approve a significant change to
|
|
282
|
+
the app. You decide what is significant according to the regulations that
|
|
283
|
+
apply to you; get interpretation from qualified legal counsel, not from this
|
|
284
|
+
skill. The description should be short and name the concrete change the
|
|
285
|
+
parent is agreeing to, for example "Adds voice chat with players outside
|
|
286
|
+
your child's friend list", not "We updated our app".
|
|
418
287
|
|
|
419
288
|
```swift
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
Presenting a question without listening for the response means you never know
|
|
431
|
-
if the parent approved. A child can also cancel the send flow, so do not wait
|
|
432
|
-
forever for a response to every question.
|
|
433
|
-
|
|
434
|
-
```swift
|
|
435
|
-
// WRONG: Fire and forget
|
|
436
|
-
try await AskCenter.shared.ask(question, in: viewController)
|
|
437
|
-
|
|
438
|
-
// CORRECT: Observe responses
|
|
439
|
-
Task {
|
|
440
|
-
for await response in AskCenter.shared.responses(for: CommunicationTopic.self) {
|
|
441
|
-
handleResponse(response)
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
try await AskCenter.shared.ask(question, in: viewController)
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### DON'T: Use deprecated CommunicationLimitsButton
|
|
448
|
-
|
|
449
|
-
Use `PermissionButton` instead of the deprecated `CommunicationLimitsButton`.
|
|
450
|
-
|
|
451
|
-
```swift
|
|
452
|
-
// WRONG: Deprecated
|
|
453
|
-
CommunicationLimitsButton(question: question) {
|
|
454
|
-
Text("Ask Permission")
|
|
289
|
+
@available(iOS 26.2, *)
|
|
290
|
+
@MainActor
|
|
291
|
+
func askAboutVoiceChat(from host: UIViewController) async throws {
|
|
292
|
+
let topic = SignificantAppUpdateTopic(
|
|
293
|
+
description: "Adds voice chat with players outside your child's friend list."
|
|
294
|
+
)
|
|
295
|
+
let question = PermissionQuestion<SignificantAppUpdateTopic>(significantAppUpdateTopic: topic)
|
|
296
|
+
try await AskCenter.shared.ask(question, in: host)
|
|
297
|
+
updateGate.markPending(question.id)
|
|
298
|
+
updateGate.scheduleExpiry(for: question.id, after: .seconds(72 * 3600))
|
|
455
299
|
}
|
|
456
300
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
301
|
+
@available(iOS 26.2, *)
|
|
302
|
+
@MainActor
|
|
303
|
+
func watchUpdateDecisions() async {
|
|
304
|
+
for await response in AskCenter.shared.responses(for: SignificantAppUpdateTopic.self) {
|
|
305
|
+
switch response.choice.answer {
|
|
306
|
+
case .approval: updateGate.enableVoiceChat()
|
|
307
|
+
case .denial: updateGate.keepVoiceChatOff()
|
|
308
|
+
@unknown default: updateGate.keepVoiceChatOff()
|
|
309
|
+
}
|
|
310
|
+
}
|
|
460
311
|
}
|
|
461
312
|
```
|
|
462
313
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
-
|
|
482
|
-
|
|
314
|
+
If the pending window closes with no answer, keep the update blocked or offer
|
|
315
|
+
another request. A child cancelling the send flow is not a denial; it simply
|
|
316
|
+
produces no response.
|
|
317
|
+
|
|
318
|
+
## Common mistakes
|
|
319
|
+
|
|
320
|
+
- Reading `isKnownHandle(_:)` or `knownHandles(in:)` as "limits are on".
|
|
321
|
+
Those calls only classify handles. Still catch the limits-off case:
|
|
322
|
+
|
|
323
|
+
```swift
|
|
324
|
+
do { try await AskCenter.shared.ask(question, in: host) }
|
|
325
|
+
catch AskError.communicationLimitsNotEnabled { unlockChat() }
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
- One catch-all that shows nothing. Give each `AskError` case its own
|
|
329
|
+
outcome: allow communication, prompt for contact sync, flag an invalid
|
|
330
|
+
question, explain that the device cannot do it, surface a system error, or
|
|
331
|
+
show a generic retry message.
|
|
332
|
+
- Empty handles. `PermissionQuestion(handles: [])` is an invalid question.
|
|
333
|
+
Provide at least one handle or one `PersonInformation`.
|
|
334
|
+
- Fire-and-forget asking. Start the task that observes `responses(for:)`
|
|
335
|
+
before or next to the `ask` call, and never wait forever: the child can
|
|
336
|
+
cancel.
|
|
337
|
+
- Using `CommunicationLimitsButton`, or the `CommunicationLimits` ask and
|
|
338
|
+
`updates` APIs. They are deprecated; use `PermissionButton`,
|
|
339
|
+
`AskCenter.ask(_:in:)` and `AskCenter.responses(for:)`.
|
|
340
|
+
|
|
341
|
+
## Review checklist
|
|
342
|
+
|
|
343
|
+
- [ ] The team knows the request goes through iMessage only before picking
|
|
344
|
+
PermissionKit.
|
|
345
|
+
- [ ] Availability is stated per tier: 26.0 core types and limits, 26.1
|
|
346
|
+
`AskError`, 26.2 `AskCenter`, `PermissionButton`, responses and
|
|
347
|
+
`SignificantAppUpdateTopic`.
|
|
348
|
+
- [ ] `.communicationLimitsNotEnabled` falls back to normal communication;
|
|
349
|
+
every other `AskError` case has its own user feedback.
|
|
350
|
+
- [ ] Each `CommunicationHandle` uses the right `Kind`.
|
|
351
|
+
- [ ] Known-handle code guards a nonempty bundle identifier and is not used
|
|
352
|
+
as a limits-enabled check.
|
|
353
|
+
- [ ] Every question has at least one handle or person information, and
|
|
354
|
+
person information carries name components.
|
|
355
|
+
- [ ] `responses(for:)` is observed; `PermissionButton` is used, not the
|
|
356
|
+
deprecated button.
|
|
357
|
+
- [ ] Listed actions match what the app really lets people do.
|
|
358
|
+
- [ ] Pending, cancelled and expired requests are handled when no answer
|
|
359
|
+
arrives.
|
|
360
|
+
- [ ] Response handling updates UI on the main actor and errors tell the user
|
|
361
|
+
what to do next.
|
|
483
362
|
|
|
484
363
|
## References
|
|
485
364
|
|
|
486
|
-
-
|
|
487
|
-
-
|
|
488
|
-
|
|
489
|
-
-
|
|
490
|
-
-
|
|
491
|
-
-
|
|
492
|
-
-
|
|
493
|
-
-
|
|
494
|
-
-
|
|
495
|
-
-
|
|
496
|
-
-
|
|
497
|
-
-
|
|
365
|
+
- [Patterns reference](references/permissionkit-patterns.md): UIKit
|
|
366
|
+
controller, response manager, multi-contact questions, limits-check view
|
|
367
|
+
model, SwiftUI full flow, macOS, error recovery.
|
|
368
|
+
- Apple documentation:
|
|
369
|
+
- https://developer.apple.com/documentation/permissionkit
|
|
370
|
+
- https://developer.apple.com/documentation/permissionkit/askcenter
|
|
371
|
+
- https://developer.apple.com/documentation/permissionkit/permissionquestion
|
|
372
|
+
- https://developer.apple.com/documentation/permissionkit/permissionbutton
|
|
373
|
+
- https://developer.apple.com/documentation/permissionkit/permissionresponse
|
|
374
|
+
- https://developer.apple.com/documentation/permissionkit/communicationtopic
|
|
375
|
+
- https://developer.apple.com/documentation/permissionkit/communicationhandle
|
|
376
|
+
- https://developer.apple.com/documentation/permissionkit/communicationlimits
|
|
377
|
+
- https://developer.apple.com/documentation/permissionkit/significantappupdatetopic
|
|
378
|
+
- https://developer.apple.com/documentation/permissionkit/askerror
|
|
379
|
+
- https://developer.apple.com/documentation/permissionkit/creating-a-communication-experience
|