@devo-bmad-custom/agent-orchestration 1.0.2 → 1.0.3
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/lib/installer.js +33 -0
- package/package.json +1 -1
- package/src/.agents/skills/audit-website/README.md +20 -20
- package/src/.agents/skills/audit-website/SKILL.md +470 -470
- package/src/.agents/skills/audit-website/agents/openai.yaml +6 -6
- package/src/.agents/skills/audit-website/assets/icon-small.svg +41 -41
- package/src/.agents/skills/audit-website/references/OUTPUT-FORMAT.md +250 -250
- package/src/.agents/skills/clean-code-standards/SKILL.md +104 -104
- package/src/.agents/skills/excalidraw-dark-standard/SKILL.md +281 -281
- package/src/.agents/skills/frontend-responsive-design-standards/SKILL.md +434 -434
- package/src/.agents/skills/java-fundamentals/SKILL.md +116 -116
- package/src/.agents/skills/java-performance/SKILL.md +119 -119
- package/src/.agents/skills/next-best-practices/SKILL.md +153 -153
- package/src/.agents/skills/next-best-practices/async-patterns.md +87 -87
- package/src/.agents/skills/next-best-practices/bundling.md +180 -180
- package/src/.agents/skills/next-best-practices/data-patterns.md +297 -297
- package/src/.agents/skills/next-best-practices/debug-tricks.md +105 -105
- package/src/.agents/skills/next-best-practices/directives.md +73 -73
- package/src/.agents/skills/next-best-practices/error-handling.md +227 -227
- package/src/.agents/skills/next-best-practices/file-conventions.md +140 -140
- package/src/.agents/skills/next-best-practices/font.md +245 -245
- package/src/.agents/skills/next-best-practices/functions.md +108 -108
- package/src/.agents/skills/next-best-practices/hydration-error.md +91 -91
- package/src/.agents/skills/next-best-practices/image.md +173 -173
- package/src/.agents/skills/next-best-practices/metadata.md +301 -301
- package/src/.agents/skills/next-best-practices/parallel-routes.md +287 -287
- package/src/.agents/skills/next-best-practices/route-handlers.md +146 -146
- package/src/.agents/skills/next-best-practices/rsc-boundaries.md +159 -159
- package/src/.agents/skills/next-best-practices/runtime-selection.md +39 -39
- package/src/.agents/skills/next-best-practices/scripts.md +141 -141
- package/src/.agents/skills/next-best-practices/self-hosting.md +371 -371
- package/src/.agents/skills/next-best-practices/suspense-boundaries.md +67 -67
- package/src/.agents/skills/nextjs-app-router-patterns/SKILL.md +537 -537
- package/src/.agents/skills/postgresql-optimization/SKILL.md +404 -404
- package/src/.agents/skills/python-backend/SKILL.md +153 -153
- package/src/.agents/skills/python-fundamentals/SKILL.md +234 -234
- package/src/.agents/skills/python-performance/SKILL.md +404 -404
- package/src/.agents/skills/react-expert/SKILL.md +335 -335
- package/src/.agents/skills/redis-best-practices/SKILL.md +438 -438
- package/src/.agents/skills/security-best-practices/SKILL.md +288 -288
- package/src/.agents/skills/security-review/LICENSE +22 -22
- package/src/.agents/skills/security-review/SKILL.md +312 -312
- package/src/.agents/skills/security-review/infrastructure/docker.md +432 -432
- package/src/.agents/skills/security-review/languages/javascript.md +388 -388
- package/src/.agents/skills/security-review/languages/python.md +363 -363
- package/src/.agents/skills/security-review/references/api-security.md +519 -519
- package/src/.agents/skills/security-review/references/authentication.md +353 -353
- package/src/.agents/skills/security-review/references/authorization.md +372 -372
- package/src/.agents/skills/security-review/references/business-logic.md +443 -443
- package/src/.agents/skills/security-review/references/cryptography.md +329 -329
- package/src/.agents/skills/security-review/references/csrf.md +398 -398
- package/src/.agents/skills/security-review/references/data-protection.md +378 -378
- package/src/.agents/skills/security-review/references/deserialization.md +410 -410
- package/src/.agents/skills/security-review/references/error-handling.md +436 -436
- package/src/.agents/skills/security-review/references/file-security.md +457 -457
- package/src/.agents/skills/security-review/references/injection.md +259 -259
- package/src/.agents/skills/security-review/references/logging.md +433 -433
- package/src/.agents/skills/security-review/references/misconfiguration.md +435 -435
- package/src/.agents/skills/security-review/references/modern-threats.md +475 -475
- package/src/.agents/skills/security-review/references/ssrf.md +415 -415
- package/src/.agents/skills/security-review/references/supply-chain.md +405 -405
- package/src/.agents/skills/security-review/references/xss.md +336 -336
- package/src/.agents/skills/subagent-driven-development/SKILL.md +275 -275
- package/src/.agents/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -26
- package/src/.agents/skills/subagent-driven-development/implementer-prompt.md +113 -113
- package/src/.agents/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -61
- package/src/.agents/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/src/.agents/skills/systematic-debugging/SKILL.md +296 -296
- package/src/.agents/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/src/.agents/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/src/.agents/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/src/.agents/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/src/.agents/skills/systematic-debugging/test-academic.md +14 -14
- package/src/.agents/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/src/.agents/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/src/.agents/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/src/.agents/skills/typescript-best-practices/SKILL.md +373 -373
- package/src/.agents/skills/ui-ux-pro-custom/SKILL.md +348 -348
- package/src/.agents/skills/ui-ux-pro-custom/data/charts.csv +26 -26
- package/src/.agents/skills/ui-ux-pro-custom/data/colors.csv +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/icons.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/SKILL.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/accessibility.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/animation.md +466 -466
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/composition-locals.md +231 -231
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/deprecated-patterns.md +323 -323
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/lists-scrolling.md +400 -400
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/modifiers.md +331 -331
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/navigation.md +416 -416
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/performance.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/side-effects.md +516 -516
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/foundation-source.md +13327 -13327
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/material3-source.md +19097 -19097
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/navigation-source.md +2947 -2947
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/runtime-source.md +11316 -11316
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/ui-source.md +7896 -7896
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/state-management.md +377 -377
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/styles-experimental.md +470 -470
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/theming-material3.md +349 -349
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/view-composition.md +595 -595
- package/src/.agents/skills/ui-ux-pro-custom/data/landing.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/data/mobile-ui-layout.md +654 -654
- package/src/.agents/skills/ui-ux-pro-custom/data/products.csv +96 -96
- package/src/.agents/skills/ui-ux-pro-custom/data/react-performance.csv +45 -45
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/astro.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/flutter.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/html-tailwind.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/jetpack-compose.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nextjs.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxt-ui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxtjs.csv +59 -59
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react-native.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/shadcn.csv +61 -61
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/svelte.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/swiftui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/vue.csv +50 -50
- package/src/.agents/skills/ui-ux-pro-custom/data/styles.csv +68 -68
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/SKILL.md +438 -438
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/references/alarmkit-patterns.md +584 -584
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-clips/SKILL.md +436 -436
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/SKILL.md +489 -489
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/references/appintents-advanced.md +1076 -1076
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/SKILL.md +340 -340
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/privacy-manifest.md +90 -90
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/review-checklists.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-conversion.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-optimization.md +344 -344
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/foundation-models.md +508 -508
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/mlx-swift.md +285 -285
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/references/keychain-biometric.md +211 -211
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/SKILL.md +499 -499
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/references/background-task-patterns.md +390 -390
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/SKILL.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/references/callkit-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/SKILL.md +492 -492
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/references/cloudkit-patterns.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/codable-patterns/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/SKILL.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/references/contacts-patterns.md +409 -409
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/references/ble-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/SKILL.md +388 -388
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/references/motion-patterns.md +405 -405
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/SKILL.md +495 -495
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/references/nfc-patterns.md +420 -420
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/SKILL.md +459 -459
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/references/coreml-swift-integration.md +765 -765
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/SKILL.md +422 -422
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/instruments-guide.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/lldb-patterns.md +298 -298
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/device-integrity/SKILL.md +477 -477
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/SKILL.md +460 -460
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/references/energykit-patterns.md +541 -541
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/references/eventkit-patterns.md +326 -326
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/references/healthkit-patterns.md +602 -602
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/references/matter-commissioning.md +455 -455
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/SKILL.md +301 -301
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/references/a11y-patterns.md +140 -140
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/SKILL.md +418 -418
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/formatstyle-locale.md +627 -627
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/string-catalogs.md +462 -462
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/SKILL.md +441 -441
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/background-websocket.md +862 -862
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/lightweight-clients.md +93 -93
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/network-framework.md +563 -563
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/urlsession-patterns.md +1116 -1116
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/app-review-guidelines.md +174 -174
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/cryptokit-advanced.md +296 -296
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/file-storage-patterns.md +354 -354
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/privacy-manifest.md +117 -117
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/references/live-activity-patterns.md +868 -868
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/corelocation-patterns.md +730 -730
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/mapkit-patterns.md +748 -748
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/metrickit-diagnostics/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/SKILL.md +395 -395
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/references/musickit-patterns.md +363 -363
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/SKILL.md +412 -412
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/references/translation-patterns.md +311 -311
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/SKILL.md +398 -398
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/references/wallet-passes.md +254 -254
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/SKILL.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/paperkit-integration.md +376 -376
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/pencilkit-patterns.md +302 -302
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/SKILL.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/references/permissionkit-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/av-playback.md +701 -701
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/camera-capture.md +774 -774
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/image-loading-caching.md +869 -869
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/photospicker-patterns.md +597 -597
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/notification-patterns.md +677 -677
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/rich-notifications.md +745 -745
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/references/realitykit-patterns.md +480 -480
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/references/shareplay-patterns.md +544 -544
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/speech-recognition/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/SKILL.md +478 -478
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/app-review-guidelines.md +58 -58
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/storekit-advanced.md +755 -755
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/SKILL.md +487 -487
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/references/charts-patterns.md +895 -895
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/SKILL.md +408 -408
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/approachable-concurrency.md +80 -80
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swift-6-2-concurrency.md +233 -233
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swiftui-concurrency.md +187 -187
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/synchronization-primitives.md +341 -341
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/references/swift-patterns-extended.md +505 -505
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/references/testing-patterns.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/SKILL.md +334 -334
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/core-data-coexistence.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-advanced.md +975 -975
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-queries.md +675 -675
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/SKILL.md +481 -481
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/animation-advanced.md +804 -804
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/core-animation-bridge.md +553 -553
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/SKILL.md +450 -450
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/references/gesture-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/SKILL.md +336 -336
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/form.md +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/grids.md +69 -69
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/list.md +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/scrollview.md +147 -147
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/SKILL.md +325 -325
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/references/liquid-glass.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/SKILL.md +262 -262
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/deeplinks.md +207 -207
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/navigationstack.md +177 -177
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/sheets.md +169 -169
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/tabview.md +178 -178
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/SKILL.md +381 -381
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/architecture-patterns.md +486 -486
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/deprecated-migration.md +1097 -1097
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/design-polish.md +780 -780
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/platform-and-sharing.md +696 -696
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +46 -46
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +29 -29
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-hangs-in-your-app.md +33 -33
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-improving-swiftui-performance.md +52 -52
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/SKILL.md +428 -428
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/hosting-migration.md +534 -534
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/representable-recipes.md +1133 -1133
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/SKILL.md +494 -494
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/references/tipkit-patterns.md +782 -782
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/SKILL.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/vision-requests.md +736 -736
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/visionkit-scanner.md +738 -738
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/SKILL.md +410 -410
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/references/weatherkit-patterns.md +567 -567
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/SKILL.md +497 -497
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/references/widgetkit-advanced.md +871 -871
- package/src/.agents/skills/ui-ux-pro-custom/data/typography.csv +57 -57
- package/src/.agents/skills/ui-ux-pro-custom/data/ui-reasoning.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/ux-guidelines.csv +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/web-interface.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/scripts/core.py +253 -253
- package/src/.agents/skills/ui-ux-pro-custom/scripts/design_system.py +1067 -1067
- package/src/.agents/skills/ui-ux-pro-custom/scripts/search.py +114 -114
- package/src/.agents/skills/ux-audit/SKILL.md +150 -150
- package/src/.agents/skills/websocket-engineer/SKILL.md +168 -168
- package/src/.agents/skills/websocket-engineer/references/alternatives.md +391 -391
- package/src/.agents/skills/websocket-engineer/references/patterns.md +400 -400
- package/src/.agents/skills/websocket-engineer/references/protocol.md +195 -195
- package/src/.agents/skills/websocket-engineer/references/scaling.md +333 -333
- package/src/.agents/skills/websocket-engineer/references/security.md +474 -474
- package/src/.agents/skills/writing-skills/SKILL.md +655 -655
- package/src/.agents/skills/writing-skills/anthropic-best-practices.md +1150 -1150
- package/src/.agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -189
- package/src/.agents/skills/writing-skills/graphviz-conventions.dot +171 -171
- package/src/.agents/skills/writing-skills/persuasion-principles.md +187 -187
- package/src/.agents/skills/writing-skills/render-graphs.js +168 -168
- package/src/.agents/skills/writing-skills/testing-skills-with-subagents.md +384 -384
- package/src/.claude/commands/master-orchestrator.md +15 -0
- package/src/_memory/config.yaml +11 -11
- package/src/_memory/master-orchestrator-sidecar/instructions.md +85 -32
- package/src/_memory/skills/nimbalyst-tracking/SKILL.md +103 -103
- package/src/_memory/skills/writing-skills/SKILL.md +655 -655
- package/src/bmb/agents/agent-builder.md +59 -59
- package/src/bmb/agents/module-builder.md +60 -60
- package/src/bmb/agents/workflow-builder.md +61 -61
- package/src/bmb/config.yaml +12 -12
- package/src/bmb/module-help.csv +13 -13
- package/src/bmb/workflows/agent/data/agent-architecture.md +258 -258
- package/src/bmb/workflows/agent/data/agent-compilation.md +185 -185
- package/src/bmb/workflows/agent/data/agent-menu-patterns.md +189 -189
- package/src/bmb/workflows/agent/data/agent-metadata.md +133 -133
- package/src/bmb/workflows/agent/data/agent-validation.md +111 -111
- package/src/bmb/workflows/agent/data/brainstorm-context.md +96 -96
- package/src/bmb/workflows/agent/data/communication-presets.csv +61 -61
- package/src/bmb/workflows/agent/data/critical-actions.md +75 -75
- package/src/bmb/workflows/agent/data/persona-properties.md +252 -252
- package/src/bmb/workflows/agent/data/principles-crafting.md +142 -142
- package/src/bmb/workflows/agent/data/reference/module-examples/architect.md +68 -68
- package/src/bmb/workflows/agent/data/reference/with-sidecar/journal-keeper/journal-keeper-sidecar/entries/yy-mm-dd-entry-template.md +16 -16
- package/src/bmb/workflows/agent/data/understanding-agent-types.md +126 -126
- package/src/bmb/workflows/agent/steps-c/step-01-brainstorm.md +129 -129
- package/src/bmb/workflows/agent/steps-c/step-02-discovery.md +170 -170
- package/src/bmb/workflows/agent/steps-c/step-03-sidecar-metadata.md +309 -309
- package/src/bmb/workflows/agent/steps-c/step-04-persona.md +213 -213
- package/src/bmb/workflows/agent/steps-c/step-05-commands-menu.md +179 -179
- package/src/bmb/workflows/agent/steps-c/step-06-activation.md +278 -278
- package/src/bmb/workflows/agent/steps-c/step-07-build-agent.md +316 -316
- package/src/bmb/workflows/agent/steps-c/step-08-celebrate.md +247 -247
- package/src/bmb/workflows/agent/steps-e/e-01-load-existing.md +221 -221
- package/src/bmb/workflows/agent/steps-e/e-02-discover-edits.md +195 -195
- package/src/bmb/workflows/agent/steps-e/e-04-sidecar-metadata.md +126 -126
- package/src/bmb/workflows/agent/steps-e/e-05-persona.md +135 -135
- package/src/bmb/workflows/agent/steps-e/e-06-commands-menu.md +123 -123
- package/src/bmb/workflows/agent/steps-e/e-07-activation.md +124 -124
- package/src/bmb/workflows/agent/steps-e/e-08-edit-agent.md +197 -197
- package/src/bmb/workflows/agent/steps-e/e-09-celebrate.md +155 -155
- package/src/bmb/workflows/agent/steps-v/v-01-load-review.md +137 -137
- package/src/bmb/workflows/agent/steps-v/v-02a-validate-metadata.md +116 -116
- package/src/bmb/workflows/agent/steps-v/v-02b-validate-persona.md +124 -124
- package/src/bmb/workflows/agent/steps-v/v-02c-validate-menu.md +127 -127
- package/src/bmb/workflows/agent/steps-v/v-02d-validate-structure.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-02e-validate-sidecar.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-03-summary.md +104 -104
- package/src/bmb/workflows/agent/templates/agent-plan.template.md +5 -5
- package/src/bmb/workflows/agent/templates/agent-template.md +89 -89
- package/src/bmb/workflows/agent/workflow-create-agent.md +72 -72
- package/src/bmb/workflows/agent/workflow-edit-agent.md +75 -75
- package/src/bmb/workflows/agent/workflow-validate-agent.md +73 -73
- package/src/bmb/workflows/module/data/agent-architecture.md +179 -179
- package/src/bmb/workflows/module/data/agent-spec-template.md +79 -79
- package/src/bmb/workflows/module/data/module-standards.md +263 -263
- package/src/bmb/workflows/module/data/module-yaml-conventions.md +392 -392
- package/src/bmb/workflows/module/module-help-generate.md +254 -254
- package/src/bmb/workflows/module/steps-b/step-01-welcome.md +148 -148
- package/src/bmb/workflows/module/steps-b/step-02-spark.md +141 -141
- package/src/bmb/workflows/module/steps-b/step-03-module-type.md +149 -149
- package/src/bmb/workflows/module/steps-b/step-04-vision.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-05-identity.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-06-users.md +86 -86
- package/src/bmb/workflows/module/steps-b/step-07-value.md +76 -76
- package/src/bmb/workflows/module/steps-b/step-08-agents.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-09-workflows.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-10-tools.md +91 -91
- package/src/bmb/workflows/module/steps-b/step-11-scenarios.md +84 -84
- package/src/bmb/workflows/module/steps-b/step-12-creative.md +95 -95
- package/src/bmb/workflows/module/steps-b/step-13-review.md +105 -105
- package/src/bmb/workflows/module/steps-b/step-14-finalize.md +117 -117
- package/src/bmb/workflows/module/steps-c/step-01-load-brief.md +179 -179
- package/src/bmb/workflows/module/steps-c/step-01b-continue.md +82 -82
- package/src/bmb/workflows/module/steps-c/step-02-structure.md +105 -105
- package/src/bmb/workflows/module/steps-c/step-03-config.md +119 -119
- package/src/bmb/workflows/module/steps-c/step-04-agents.md +168 -168
- package/src/bmb/workflows/module/steps-c/step-05-workflows.md +184 -184
- package/src/bmb/workflows/module/steps-c/step-06-docs.md +401 -401
- package/src/bmb/workflows/module/steps-c/step-07-complete.md +152 -152
- package/src/bmb/workflows/module/steps-e/step-01-load-target.md +81 -81
- package/src/bmb/workflows/module/steps-e/step-02-select-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-03-apply-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-04-review.md +80 -80
- package/src/bmb/workflows/module/steps-e/step-05-confirm.md +75 -75
- package/src/bmb/workflows/module/steps-v/step-01-load-target.md +96 -96
- package/src/bmb/workflows/module/steps-v/step-02-file-structure.md +93 -93
- package/src/bmb/workflows/module/steps-v/step-03-module-yaml.md +99 -99
- package/src/bmb/workflows/module/steps-v/step-04-agent-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-05-workflow-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-06-documentation.md +143 -143
- package/src/bmb/workflows/module/steps-v/step-07-installation.md +102 -102
- package/src/bmb/workflows/module/steps-v/step-08-report.md +197 -197
- package/src/bmb/workflows/module/templates/brief-template.md +154 -154
- package/src/bmb/workflows/module/templates/workflow-spec-template.md +96 -96
- package/src/bmb/workflows/module/workflow-create-module-brief.md +71 -71
- package/src/bmb/workflows/module/workflow-create-module.md +86 -86
- package/src/bmb/workflows/module/workflow-edit-module.md +66 -66
- package/src/bmb/workflows/module/workflow-validate-module.md +66 -66
- package/src/bmb/workflows/workflow/data/architecture.md +150 -150
- package/src/bmb/workflows/workflow/data/common-workflow-tools.csv +19 -19
- package/src/bmb/workflows/workflow/data/csv-data-file-standards.md +53 -53
- package/src/bmb/workflows/workflow/data/frontmatter-standards.md +184 -184
- package/src/bmb/workflows/workflow/data/input-discovery-standards.md +191 -191
- package/src/bmb/workflows/workflow/data/intent-vs-prescriptive-spectrum.md +44 -44
- package/src/bmb/workflows/workflow/data/menu-handling-standards.md +133 -133
- package/src/bmb/workflows/workflow/data/output-format-standards.md +135 -135
- package/src/bmb/workflows/workflow/data/step-file-rules.md +235 -235
- package/src/bmb/workflows/workflow/data/step-type-patterns.md +257 -257
- package/src/bmb/workflows/workflow/data/subprocess-optimization-patterns.md +188 -188
- package/src/bmb/workflows/workflow/data/trimodal-workflow-structure.md +164 -164
- package/src/bmb/workflows/workflow/data/workflow-chaining-standards.md +222 -222
- package/src/bmb/workflows/workflow/data/workflow-examples.md +232 -232
- package/src/bmb/workflows/workflow/data/workflow-type-criteria.md +134 -134
- package/src/bmb/workflows/workflow/steps-c/step-00-conversion.md +263 -263
- package/src/bmb/workflows/workflow/steps-c/step-01-discovery.md +194 -194
- package/src/bmb/workflows/workflow/steps-c/step-01b-continuation.md +3 -3
- package/src/bmb/workflows/workflow/steps-c/step-02-classification.md +270 -270
- package/src/bmb/workflows/workflow/steps-c/step-03-requirements.md +283 -283
- package/src/bmb/workflows/workflow/steps-c/step-04-tools.md +282 -282
- package/src/bmb/workflows/workflow/steps-c/step-05-plan-review.md +243 -243
- package/src/bmb/workflows/workflow/steps-c/step-06-design.md +330 -330
- package/src/bmb/workflows/workflow/steps-c/step-07-foundation.md +239 -239
- package/src/bmb/workflows/workflow/steps-c/step-08-build-step-01.md +379 -379
- package/src/bmb/workflows/workflow/steps-c/step-09-build-next-step.md +350 -350
- package/src/bmb/workflows/workflow/steps-c/step-10-confirmation.md +322 -322
- package/src/bmb/workflows/workflow/steps-c/step-11-completion.md +191 -191
- package/src/bmb/workflows/workflow/steps-e/step-e-01-assess-workflow.md +237 -237
- package/src/bmb/workflows/workflow/steps-e/step-e-02-discover-edits.md +251 -251
- package/src/bmb/workflows/workflow/steps-e/step-e-03-fix-validation.md +254 -254
- package/src/bmb/workflows/workflow/steps-e/step-e-04-direct-edit.md +277 -277
- package/src/bmb/workflows/workflow/steps-e/step-e-05-apply-edit.md +154 -154
- package/src/bmb/workflows/workflow/steps-e/step-e-06-validate-after.md +190 -190
- package/src/bmb/workflows/workflow/steps-e/step-e-07-complete.md +206 -206
- package/src/bmb/workflows/workflow/steps-v/step-01-validate-max-mode.md +109 -109
- package/src/bmb/workflows/workflow/steps-v/step-01-validate.md +221 -221
- package/src/bmb/workflows/workflow/steps-v/step-01b-structure.md +152 -152
- package/src/bmb/workflows/workflow/steps-v/step-02-frontmatter-validation.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-02b-path-violations.md +265 -265
- package/src/bmb/workflows/workflow/steps-v/step-03-menu-validation.md +164 -164
- package/src/bmb/workflows/workflow/steps-v/step-04-step-type-validation.md +211 -211
- package/src/bmb/workflows/workflow/steps-v/step-05-output-format-validation.md +200 -200
- package/src/bmb/workflows/workflow/steps-v/step-06-validation-design-check.md +195 -195
- package/src/bmb/workflows/workflow/steps-v/step-07-instruction-style-check.md +209 -209
- package/src/bmb/workflows/workflow/steps-v/step-08-collaborative-experience-check.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-08b-subprocess-optimization.md +179 -179
- package/src/bmb/workflows/workflow/steps-v/step-09-cohesive-review.md +186 -186
- package/src/bmb/workflows/workflow/steps-v/step-10-report-complete.md +154 -154
- package/src/bmb/workflows/workflow/steps-v/step-11-plan-validation.md +237 -237
- package/src/bmb/workflows/workflow/templates/minimal-output-template.md +11 -11
- package/src/bmb/workflows/workflow/templates/step-01-init-continuable-template.md +241 -241
- package/src/bmb/workflows/workflow/templates/step-1b-template.md +224 -224
- package/src/bmb/workflows/workflow/templates/step-template.md +294 -294
- package/src/bmb/workflows/workflow/templates/workflow-template.md +102 -102
- package/src/bmb/workflows/workflow/workflow-create-workflow.md +79 -79
- package/src/bmb/workflows/workflow/workflow-edit-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-rework-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-validate-max-parallel-workflow.md +66 -66
- package/src/bmb/workflows/workflow/workflow-validate-workflow.md +65 -65
- package/src/bmm/agents/analyst.md +104 -104
- package/src/bmm/agents/dev.md +100 -100
- package/src/bmm/agents/qa.md +100 -90
- package/src/bmm/agents/tech-writer/tech-writer.md +94 -94
- package/src/bmm/module-help.csv +31 -31
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +115 -115
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +107 -107
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +141 -141
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +144 -144
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +147 -147
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +161 -161
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +99 -99
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +57 -57
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +156 -156
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +165 -165
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +140 -140
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +152 -152
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +345 -345
- package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +92 -92
- package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +164 -164
- package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +174 -174
- package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +184 -184
- package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +105 -105
- package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +360 -360
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +165 -165
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +174 -174
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +141 -141
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +159 -159
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +387 -387
- package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -54
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +100 -100
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +160 -160
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +88 -88
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +99 -99
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +169 -169
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +156 -156
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +176 -176
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +184 -184
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +174 -174
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +175 -175
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +189 -189
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +162 -162
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +79 -79
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +183 -183
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +149 -149
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +187 -187
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +108 -108
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +166 -166
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +131 -131
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +150 -150
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +155 -155
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +170 -170
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +158 -158
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +147 -147
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +182 -182
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +202 -202
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +148 -148
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +201 -201
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +179 -179
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +164 -164
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +106 -106
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +111 -111
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +115 -115
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +127 -127
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +167 -167
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +143 -143
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +154 -154
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +165 -165
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +135 -135
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +101 -101
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +45 -45
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +185 -185
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +130 -130
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +93 -93
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +196 -196
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +54 -54
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +82 -82
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +106 -106
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +138 -138
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +129 -129
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +166 -166
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +186 -186
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +163 -163
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +38 -38
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +49 -49
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +124 -124
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +122 -122
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +84 -84
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +58 -58
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +43 -43
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +53 -53
- package/src/bmm/workflows/4-implementation/create-story/checklist.md +159 -159
- package/src/bmm/workflows/4-implementation/create-story/template.md +79 -79
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +20 -20
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +25 -25
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +158 -158
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +122 -122
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +87 -87
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +146 -146
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -50
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +152 -152
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +123 -123
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +201 -201
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -79
- package/src/bmm/workflows/document-project/workflow.yaml +22 -22
- package/src/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -184
- package/src/bmm/workflows/generate-project-context/steps/step-02-generate.md +322 -322
- package/src/bmm/workflows/generate-project-context/steps/step-03-complete.md +235 -235
- package/src/bmm/workflows/generate-project-context/workflow.md +49 -49
- package/src/bmm/workflows/qa/automate/workflow.yaml +233 -233
- package/src/bmm/workflows/qa-generate-e2e-tests/workflow.yaml +42 -42
- package/src/core/config.yaml +9 -9
- package/src/core/module-help.csv +10 -10
- package/src/core/scripts/generate-loop-report.py +72 -72
- package/src/core/tasks/editorial-review-prose.xml +101 -101
- package/src/core/tasks/editorial-review-structure.xml +207 -207
- package/src/core/tasks/help.md +86 -86
- package/src/core/tasks/index-docs.xml +64 -64
- package/src/core/tasks/review-adversarial-general.xml +66 -66
- package/src/core/tasks/review-adversarial-loop.xml +46 -46
- package/src/core/tasks/review-edge-case-hunter.xml +63 -63
- package/src/core/tasks/review-party-loop.xml +46 -46
- package/src/core/tasks/shard-doc.xml +107 -107
- package/src/core/tasks/workflow.xml +235 -235
- package/src/core/templates/review-loop-report.html +88 -88
- package/src/core/templates/review-loop-report.md +5 -5
- package/src/core/workflows/advanced-elicitation/workflow.xml +117 -117
- package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +212 -212
- package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -122
- package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -225
- package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -237
- package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -209
- package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -264
- package/src/core/workflows/brainstorming/steps/step-02e-deep-dive.md +68 -68
- package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +403 -403
- package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -303
- package/src/core/workflows/brainstorming/workflow.md +60 -60
- package/src/core/workflows/extract-trackers/workflow.md +45 -45
- package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +142 -142
- package/src/core/workflows/party-mode/workflow.md +194 -194
- package/src/docs/dev/tmux/actions_popup.py +291 -291
- package/src/docs/dev/tmux/tmux-setup.md +62 -1
package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/SKILL.md
CHANGED
|
@@ -1,501 +1,501 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: push-notifications
|
|
3
|
-
description: "Implement, review, or debug push notifications in iOS/macOS apps — local notifications, remote (APNs) notifications, rich notifications, notification actions, silent pushes, and notification service/content extensions. Use when working with UNUserNotificationCenter, registering for remote notifications, handling notification payloads, setting up notification categories and actions, creating rich notification content, or debugging notification delivery. Also use when working with alerts, badges, sounds, background pushes, or user notification permissions in Swift apps."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Push Notifications
|
|
7
|
-
|
|
8
|
-
Implement, review, and debug local and remote notifications on iOS/macOS using `UserNotifications` and APNs. Covers permission flow, token registration, payload structure, foreground handling, notification actions, grouping, and rich notifications. Targets iOS 26+ with Swift 6.2, backward-compatible to iOS 16 unless noted.
|
|
9
|
-
|
|
10
|
-
## Contents
|
|
11
|
-
|
|
12
|
-
- [Permission Flow](#permission-flow)
|
|
13
|
-
- [APNs Registration](#apns-registration)
|
|
14
|
-
- [Local Notifications](#local-notifications)
|
|
15
|
-
- [Remote Notification Payload](#remote-notification-payload)
|
|
16
|
-
- [Notification Handling](#notification-handling)
|
|
17
|
-
- [Notification Actions and Categories](#notification-actions-and-categories)
|
|
18
|
-
- [Notification Grouping](#notification-grouping)
|
|
19
|
-
- [Common Mistakes](#common-mistakes)
|
|
20
|
-
- [Review Checklist](#review-checklist)
|
|
21
|
-
- [References](#references)
|
|
22
|
-
|
|
23
|
-
## Permission Flow
|
|
24
|
-
|
|
25
|
-
Request notification authorization before doing anything else. The system prompt appears only once; subsequent calls return the stored decision.
|
|
26
|
-
|
|
27
|
-
```swift
|
|
28
|
-
import UserNotifications
|
|
29
|
-
|
|
30
|
-
@MainActor
|
|
31
|
-
func requestNotificationPermission() async -> Bool {
|
|
32
|
-
let center = UNUserNotificationCenter.current()
|
|
33
|
-
do {
|
|
34
|
-
let granted = try await center.requestAuthorization(
|
|
35
|
-
options: [.alert, .sound, .badge]
|
|
36
|
-
)
|
|
37
|
-
return granted
|
|
38
|
-
} catch {
|
|
39
|
-
print("Authorization request failed: \(error)")
|
|
40
|
-
return false
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### Checking Current Status
|
|
46
|
-
|
|
47
|
-
Always check status before assuming permissions. The user can change settings at any time.
|
|
48
|
-
|
|
49
|
-
```swift
|
|
50
|
-
@MainActor
|
|
51
|
-
func checkNotificationStatus() async -> UNAuthorizationStatus {
|
|
52
|
-
let settings = await UNUserNotificationCenter.current().notificationSettings()
|
|
53
|
-
return settings.authorizationStatus
|
|
54
|
-
// .notDetermined, .denied, .authorized, .provisional, .ephemeral
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Provisional Notifications
|
|
59
|
-
|
|
60
|
-
Provisional notifications deliver quietly to the notification center without interrupting the user. The user can then choose to keep or turn them off. Use for onboarding flows where you want to demonstrate value before asking for full permission.
|
|
61
|
-
|
|
62
|
-
```swift
|
|
63
|
-
// Delivers silently -- no permission prompt shown to the user
|
|
64
|
-
try await center.requestAuthorization(options: [.alert, .sound, .badge, .provisional])
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Critical Alerts
|
|
68
|
-
|
|
69
|
-
Critical alerts bypass Do Not Disturb and the mute switch. Requires a special entitlement from Apple (request via developer portal). Use only for health, safety, or security scenarios.
|
|
70
|
-
|
|
71
|
-
```swift
|
|
72
|
-
// Requires com.apple.developer.usernotifications.critical-alerts entitlement
|
|
73
|
-
try await center.requestAuthorization(
|
|
74
|
-
options: [.alert, .sound, .badge, .criticalAlert]
|
|
75
|
-
)
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Handling Denied Permissions
|
|
79
|
-
|
|
80
|
-
When the user has denied notifications, guide them to Settings. Do not repeatedly prompt or nag.
|
|
81
|
-
|
|
82
|
-
```swift
|
|
83
|
-
struct NotificationSettingsButton: View {
|
|
84
|
-
@Environment(\.openURL) private var openURL
|
|
85
|
-
|
|
86
|
-
var body: some View {
|
|
87
|
-
Button("Open Settings") {
|
|
88
|
-
if let url = URL(string: UIApplication.openSettingsURLString) {
|
|
89
|
-
openURL(url)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## APNs Registration
|
|
98
|
-
|
|
99
|
-
Use `UIApplicationDelegateAdaptor` to receive the device token in a SwiftUI app. The AppDelegate callbacks are the only way to receive APNs tokens.
|
|
100
|
-
|
|
101
|
-
```swift
|
|
102
|
-
@main
|
|
103
|
-
struct MyApp: App {
|
|
104
|
-
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
|
105
|
-
|
|
106
|
-
var body: some Scene {
|
|
107
|
-
WindowGroup {
|
|
108
|
-
ContentView()
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
class AppDelegate: NSObject, UIApplicationDelegate {
|
|
114
|
-
func application(
|
|
115
|
-
_ application: UIApplication,
|
|
116
|
-
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
|
117
|
-
) -> Bool {
|
|
118
|
-
UNUserNotificationCenter.current().delegate = NotificationDelegate.shared
|
|
119
|
-
return true
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
func application(
|
|
123
|
-
_ application: UIApplication,
|
|
124
|
-
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
|
|
125
|
-
) {
|
|
126
|
-
let token = deviceToken.map { String(format: "%02x", $0) }.joined()
|
|
127
|
-
print("APNs token: \(token)")
|
|
128
|
-
// Send token to your server
|
|
129
|
-
Task { await TokenService.shared.upload(token: token) }
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
func application(
|
|
133
|
-
_ application: UIApplication,
|
|
134
|
-
didFailToRegisterForRemoteNotificationsWithError error: Error
|
|
135
|
-
) {
|
|
136
|
-
print("APNs registration failed: \(error.localizedDescription)")
|
|
137
|
-
// Simulator always fails -- this is expected during development
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Registration Order
|
|
143
|
-
|
|
144
|
-
Request authorization first, then register for remote notifications. Registration triggers the system to contact APNs and return a device token.
|
|
145
|
-
|
|
146
|
-
```swift
|
|
147
|
-
@MainActor
|
|
148
|
-
func registerForPush() async {
|
|
149
|
-
let granted = await requestNotificationPermission()
|
|
150
|
-
guard granted else { return }
|
|
151
|
-
UIApplication.shared.registerForRemoteNotifications()
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Token Handling
|
|
156
|
-
|
|
157
|
-
Device tokens change. Re-send the token to your server every time `didRegisterForRemoteNotificationsWithDeviceToken` fires, not just the first time. The system calls this method on every app launch that calls `registerForRemoteNotifications()`.
|
|
158
|
-
|
|
159
|
-
## Local Notifications
|
|
160
|
-
|
|
161
|
-
Schedule notifications directly from the device without a server. Useful for reminders, timers, and location-based alerts.
|
|
162
|
-
|
|
163
|
-
### Creating Content
|
|
164
|
-
|
|
165
|
-
```swift
|
|
166
|
-
let content = UNMutableNotificationContent()
|
|
167
|
-
content.title = "Workout Reminder"
|
|
168
|
-
content.subtitle = "Time to move"
|
|
169
|
-
content.body = "You have a scheduled workout in 15 minutes."
|
|
170
|
-
content.sound = .default
|
|
171
|
-
content.badge = 1
|
|
172
|
-
content.userInfo = ["workoutId": "abc123"]
|
|
173
|
-
content.threadIdentifier = "workouts" // groups in notification center
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Trigger Types
|
|
177
|
-
|
|
178
|
-
```swift
|
|
179
|
-
// Fire after a time interval (minimum 60 seconds for repeating)
|
|
180
|
-
let timeTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 300, repeats: false)
|
|
181
|
-
|
|
182
|
-
// Fire at a specific date/time
|
|
183
|
-
var dateComponents = DateComponents()
|
|
184
|
-
dateComponents.hour = 8
|
|
185
|
-
dateComponents.minute = 30
|
|
186
|
-
let calendarTrigger = UNCalendarNotificationTrigger(
|
|
187
|
-
dateMatching: dateComponents, repeats: true // daily at 8:30 AM
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
// Fire when entering a geographic region
|
|
191
|
-
let region = CLCircularRegion(
|
|
192
|
-
center: CLLocationCoordinate2D(latitude: 37.33, longitude: -122.01),
|
|
193
|
-
radius: 100,
|
|
194
|
-
identifier: "gym"
|
|
195
|
-
)
|
|
196
|
-
region.notifyOnEntry = true
|
|
197
|
-
region.notifyOnExit = false
|
|
198
|
-
let locationTrigger = UNLocationNotificationTrigger(region: region, repeats: false)
|
|
199
|
-
// Requires "When In Use" location permission at minimum
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Scheduling and Managing
|
|
203
|
-
|
|
204
|
-
```swift
|
|
205
|
-
let request = UNNotificationRequest(
|
|
206
|
-
identifier: "workout-reminder-abc123",
|
|
207
|
-
content: content,
|
|
208
|
-
trigger: timeTrigger
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
let center = UNUserNotificationCenter.current()
|
|
212
|
-
try await center.add(request)
|
|
213
|
-
|
|
214
|
-
// Remove specific pending notifications
|
|
215
|
-
center.removePendingNotificationRequests(withIdentifiers: ["workout-reminder-abc123"])
|
|
216
|
-
|
|
217
|
-
// Remove all pending
|
|
218
|
-
center.removeAllPendingNotificationRequests()
|
|
219
|
-
|
|
220
|
-
// Remove delivered notifications from notification center
|
|
221
|
-
center.removeDeliveredNotifications(withIdentifiers: ["workout-reminder-abc123"])
|
|
222
|
-
center.removeAllDeliveredNotifications()
|
|
223
|
-
|
|
224
|
-
// List all pending requests
|
|
225
|
-
let pending = await center.pendingNotificationRequests()
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## Remote Notification Payload
|
|
229
|
-
|
|
230
|
-
### Standard APNs Payload
|
|
231
|
-
|
|
232
|
-
```json
|
|
233
|
-
{
|
|
234
|
-
"aps": {
|
|
235
|
-
"alert": {
|
|
236
|
-
"title": "New Message",
|
|
237
|
-
"subtitle": "From Alice",
|
|
238
|
-
"body": "Hey, are you free for lunch?"
|
|
239
|
-
},
|
|
240
|
-
"badge": 3,
|
|
241
|
-
"sound": "default",
|
|
242
|
-
"thread-id": "chat-alice",
|
|
243
|
-
"category": "MESSAGE_CATEGORY"
|
|
244
|
-
},
|
|
245
|
-
"messageId": "msg-789",
|
|
246
|
-
"senderId": "user-alice"
|
|
247
|
-
}
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### Silent / Background Push
|
|
251
|
-
|
|
252
|
-
Set `content-available: 1` with no alert, sound, or badge. The system wakes the app in the background. Requires the "Background Modes > Remote notifications" capability.
|
|
253
|
-
|
|
254
|
-
```json
|
|
255
|
-
{
|
|
256
|
-
"aps": {
|
|
257
|
-
"content-available": 1
|
|
258
|
-
},
|
|
259
|
-
"updateType": "new-data"
|
|
260
|
-
}
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
Handle in AppDelegate:
|
|
264
|
-
```swift
|
|
265
|
-
func application(
|
|
266
|
-
_ application: UIApplication,
|
|
267
|
-
didReceiveRemoteNotification userInfo: [AnyHashable: Any]
|
|
268
|
-
) async -> UIBackgroundFetchResult {
|
|
269
|
-
guard let updateType = userInfo["updateType"] as? String else {
|
|
270
|
-
return .noData
|
|
271
|
-
}
|
|
272
|
-
do {
|
|
273
|
-
try await DataSyncService.shared.sync(trigger: updateType)
|
|
274
|
-
return .newData
|
|
275
|
-
} catch {
|
|
276
|
-
return .failed
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### Mutable Content
|
|
282
|
-
|
|
283
|
-
Set `mutable-content: 1` to allow a Notification Service Extension to modify content before display. Use for downloading images, decrypting content, or adding attachments.
|
|
284
|
-
|
|
285
|
-
```json
|
|
286
|
-
{
|
|
287
|
-
"aps": {
|
|
288
|
-
"alert": { "title": "Photo", "body": "Alice sent a photo" },
|
|
289
|
-
"mutable-content": 1
|
|
290
|
-
},
|
|
291
|
-
"imageUrl": "https://example.com/photo.jpg"
|
|
292
|
-
}
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
### Localized Notifications
|
|
296
|
-
|
|
297
|
-
Use localization keys so the notification displays in the user's language:
|
|
298
|
-
|
|
299
|
-
```json
|
|
300
|
-
{
|
|
301
|
-
"aps": {
|
|
302
|
-
"alert": {
|
|
303
|
-
"title-loc-key": "NEW_MESSAGE_TITLE",
|
|
304
|
-
"loc-key": "NEW_MESSAGE_BODY",
|
|
305
|
-
"loc-args": ["Alice"]
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
## Notification Handling
|
|
312
|
-
|
|
313
|
-
### UNUserNotificationCenterDelegate
|
|
314
|
-
|
|
315
|
-
Implement the delegate to control foreground display and handle user taps. Set the delegate as early as possible -- in `application(_:didFinishLaunchingWithOptions:)` or `App.init`.
|
|
316
|
-
|
|
317
|
-
```swift
|
|
318
|
-
@MainActor
|
|
319
|
-
final class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate, Sendable {
|
|
320
|
-
static let shared = NotificationDelegate()
|
|
321
|
-
|
|
322
|
-
// Called when notification arrives while app is in FOREGROUND
|
|
323
|
-
func userNotificationCenter(
|
|
324
|
-
_ center: UNUserNotificationCenter,
|
|
325
|
-
willPresent notification: UNNotification
|
|
326
|
-
) async -> UNNotificationPresentationOptions {
|
|
327
|
-
// Return which presentation elements to show
|
|
328
|
-
// Without this, foreground notifications are silently suppressed
|
|
329
|
-
return [.banner, .sound, .badge]
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Called when user TAPS the notification
|
|
333
|
-
func userNotificationCenter(
|
|
334
|
-
_ center: UNUserNotificationCenter,
|
|
335
|
-
didReceive response: UNNotificationResponse
|
|
336
|
-
) async {
|
|
337
|
-
let userInfo = response.notification.request.content.userInfo
|
|
338
|
-
let actionIdentifier = response.actionIdentifier
|
|
339
|
-
|
|
340
|
-
switch actionIdentifier {
|
|
341
|
-
case UNNotificationDefaultActionIdentifier:
|
|
342
|
-
// User tapped the notification body
|
|
343
|
-
await handleNotificationTap(userInfo: userInfo)
|
|
344
|
-
case UNNotificationDismissActionIdentifier:
|
|
345
|
-
// User dismissed the notification
|
|
346
|
-
break
|
|
347
|
-
default:
|
|
348
|
-
// Custom action button tapped
|
|
349
|
-
await handleCustomAction(actionIdentifier, userInfo: userInfo)
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
### Deep Linking from Notifications
|
|
356
|
-
|
|
357
|
-
Route notification taps to the correct screen using a shared `@Observable` router. The delegate writes a pending destination; the SwiftUI view observes and consumes it.
|
|
358
|
-
|
|
359
|
-
```swift
|
|
360
|
-
@Observable @MainActor
|
|
361
|
-
final class DeepLinkRouter {
|
|
362
|
-
var pendingDestination: AppDestination?
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// In NotificationDelegate:
|
|
366
|
-
func handleNotificationTap(userInfo: [AnyHashable: Any]) async {
|
|
367
|
-
guard let id = userInfo["messageId"] as? String else { return }
|
|
368
|
-
DeepLinkRouter.shared.pendingDestination = .chat(id: id)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// In SwiftUI -- observe and consume:
|
|
372
|
-
.onChange(of: router.pendingDestination) { _, destination in
|
|
373
|
-
if let destination {
|
|
374
|
-
path.append(destination)
|
|
375
|
-
router.pendingDestination = nil
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
See `references/notification-patterns.md` for the full deep-linking handler with tab switching.
|
|
381
|
-
|
|
382
|
-
## Notification Actions and Categories
|
|
383
|
-
|
|
384
|
-
Define interactive actions that appear as buttons on the notification. Register categories at launch.
|
|
385
|
-
|
|
386
|
-
### Defining Categories and Actions
|
|
387
|
-
|
|
388
|
-
```swift
|
|
389
|
-
func registerNotificationCategories() {
|
|
390
|
-
let replyAction = UNTextInputNotificationAction(
|
|
391
|
-
identifier: "REPLY_ACTION",
|
|
392
|
-
title: "Reply",
|
|
393
|
-
options: [],
|
|
394
|
-
textInputButtonTitle: "Send",
|
|
395
|
-
textInputPlaceholder: "Type a reply..."
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
let likeAction = UNNotificationAction(
|
|
399
|
-
identifier: "LIKE_ACTION",
|
|
400
|
-
title: "Like",
|
|
401
|
-
options: []
|
|
402
|
-
)
|
|
403
|
-
|
|
404
|
-
let deleteAction = UNNotificationAction(
|
|
405
|
-
identifier: "DELETE_ACTION",
|
|
406
|
-
title: "Delete",
|
|
407
|
-
options: [.destructive, .authenticationRequired]
|
|
408
|
-
)
|
|
409
|
-
|
|
410
|
-
let messageCategory = UNNotificationCategory(
|
|
411
|
-
identifier: "MESSAGE_CATEGORY",
|
|
412
|
-
actions: [replyAction, likeAction, deleteAction],
|
|
413
|
-
intentIdentifiers: [],
|
|
414
|
-
options: [.customDismissAction] // fires didReceive on dismiss too
|
|
415
|
-
)
|
|
416
|
-
|
|
417
|
-
UNUserNotificationCenter.current().setNotificationCategories([messageCategory])
|
|
418
|
-
}
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
### Handling Action Responses
|
|
422
|
-
|
|
423
|
-
```swift
|
|
424
|
-
func handleCustomAction(_ identifier: String, userInfo: [AnyHashable: Any]) async {
|
|
425
|
-
switch identifier {
|
|
426
|
-
case "REPLY_ACTION":
|
|
427
|
-
// response is UNTextInputNotificationResponse for text input actions
|
|
428
|
-
break
|
|
429
|
-
case "LIKE_ACTION":
|
|
430
|
-
guard let messageId = userInfo["messageId"] as? String else { return }
|
|
431
|
-
await MessageService.shared.likeMessage(id: messageId)
|
|
432
|
-
case "DELETE_ACTION":
|
|
433
|
-
guard let messageId = userInfo["messageId"] as? String else { return }
|
|
434
|
-
await MessageService.shared.deleteMessage(id: messageId)
|
|
435
|
-
default:
|
|
436
|
-
break
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
Action options:
|
|
442
|
-
- `.authenticationRequired` -- device must be unlocked to perform the action
|
|
443
|
-
- `.destructive` -- displayed in red; use for delete/remove actions
|
|
444
|
-
- `.foreground` -- launches the app to the foreground when tapped
|
|
445
|
-
|
|
446
|
-
## Notification Grouping
|
|
447
|
-
|
|
448
|
-
Group related notifications with `threadIdentifier` (or `thread-id` in the APNs payload). Each unique thread becomes a separate group in Notification Center.
|
|
449
|
-
|
|
450
|
-
```swift
|
|
451
|
-
content.threadIdentifier = "chat-alice" // all messages from Alice group together
|
|
452
|
-
content.summaryArgument = "Alice"
|
|
453
|
-
content.summaryArgumentCount = 3 // "3 more notifications from Alice"
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
Customize the summary format string in the category:
|
|
457
|
-
|
|
458
|
-
```swift
|
|
459
|
-
let category = UNNotificationCategory(
|
|
460
|
-
identifier: "MESSAGE_CATEGORY",
|
|
461
|
-
actions: [replyAction],
|
|
462
|
-
intentIdentifiers: [],
|
|
463
|
-
categorySummaryFormat: "%u more messages from %@",
|
|
464
|
-
options: []
|
|
465
|
-
)
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
## Common Mistakes
|
|
469
|
-
|
|
470
|
-
**DON'T:** Register for remote notifications before requesting authorization.
|
|
471
|
-
**DO:** Call `requestAuthorization` first, then `registerForRemoteNotifications()`.
|
|
472
|
-
|
|
473
|
-
**DON'T:** Convert device token with `String(data: deviceToken, encoding: .utf8)`.
|
|
474
|
-
**DO:** Use hex: `deviceToken.map { String(format: "%02x", $0) }.joined()`.
|
|
475
|
-
|
|
476
|
-
**DON'T:** Assume notifications always arrive. APNs is best-effort.
|
|
477
|
-
**DO:** Design features that degrade gracefully; use background refresh as fallback.
|
|
478
|
-
|
|
479
|
-
**DON'T:** Put sensitive data directly in the notification payload.
|
|
480
|
-
**DO:** Use `mutable-content: 1` with a Notification Service Extension.
|
|
481
|
-
|
|
482
|
-
**DON'T:** Forget foreground handling. Without `willPresent`, notifications are silently suppressed.
|
|
483
|
-
**DO:** Implement `willPresent` and return `.banner`, `.sound`, `.badge`.
|
|
484
|
-
|
|
485
|
-
**DON'T:** Set delegate too late or register from SwiftUI views without AppDelegate adaptor.
|
|
486
|
-
**DO:** Set delegate in `App.init`; use `UIApplicationDelegateAdaptor` for APNs.
|
|
487
|
-
|
|
488
|
-
**DON'T:** Send device token only once — tokens change. Re-send on every callback.
|
|
489
|
-
|
|
490
|
-
## Review Checklist
|
|
491
|
-
|
|
492
|
-
- [ ] Authorization requested before registering; denied case handled (Settings link)
|
|
493
|
-
- [ ] Device token converted to hex string (not `String(data:encoding:)`)
|
|
494
|
-
- [ ] `UNUserNotificationCenterDelegate` set in `App.init` or `application(_:didFinishLaunching:)`
|
|
495
|
-
- [ ] Foreground (`willPresent`) and tap (`didReceive`) handling implemented
|
|
496
|
-
- [ ] Categories/actions registered at launch if interactive notifications needed
|
|
497
|
-
- [ ] Silent push configured (Background Modes enabled); `UIApplicationDelegateAdaptor` for APNs
|
|
498
|
-
|
|
499
|
-
## References
|
|
500
|
-
- `references/notification-patterns.md` — AppDelegate setup, APNs callbacks, deep-link router, silent push, debugging
|
|
1
|
+
---
|
|
2
|
+
name: push-notifications
|
|
3
|
+
description: "Implement, review, or debug push notifications in iOS/macOS apps — local notifications, remote (APNs) notifications, rich notifications, notification actions, silent pushes, and notification service/content extensions. Use when working with UNUserNotificationCenter, registering for remote notifications, handling notification payloads, setting up notification categories and actions, creating rich notification content, or debugging notification delivery. Also use when working with alerts, badges, sounds, background pushes, or user notification permissions in Swift apps."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Push Notifications
|
|
7
|
+
|
|
8
|
+
Implement, review, and debug local and remote notifications on iOS/macOS using `UserNotifications` and APNs. Covers permission flow, token registration, payload structure, foreground handling, notification actions, grouping, and rich notifications. Targets iOS 26+ with Swift 6.2, backward-compatible to iOS 16 unless noted.
|
|
9
|
+
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- [Permission Flow](#permission-flow)
|
|
13
|
+
- [APNs Registration](#apns-registration)
|
|
14
|
+
- [Local Notifications](#local-notifications)
|
|
15
|
+
- [Remote Notification Payload](#remote-notification-payload)
|
|
16
|
+
- [Notification Handling](#notification-handling)
|
|
17
|
+
- [Notification Actions and Categories](#notification-actions-and-categories)
|
|
18
|
+
- [Notification Grouping](#notification-grouping)
|
|
19
|
+
- [Common Mistakes](#common-mistakes)
|
|
20
|
+
- [Review Checklist](#review-checklist)
|
|
21
|
+
- [References](#references)
|
|
22
|
+
|
|
23
|
+
## Permission Flow
|
|
24
|
+
|
|
25
|
+
Request notification authorization before doing anything else. The system prompt appears only once; subsequent calls return the stored decision.
|
|
26
|
+
|
|
27
|
+
```swift
|
|
28
|
+
import UserNotifications
|
|
29
|
+
|
|
30
|
+
@MainActor
|
|
31
|
+
func requestNotificationPermission() async -> Bool {
|
|
32
|
+
let center = UNUserNotificationCenter.current()
|
|
33
|
+
do {
|
|
34
|
+
let granted = try await center.requestAuthorization(
|
|
35
|
+
options: [.alert, .sound, .badge]
|
|
36
|
+
)
|
|
37
|
+
return granted
|
|
38
|
+
} catch {
|
|
39
|
+
print("Authorization request failed: \(error)")
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Checking Current Status
|
|
46
|
+
|
|
47
|
+
Always check status before assuming permissions. The user can change settings at any time.
|
|
48
|
+
|
|
49
|
+
```swift
|
|
50
|
+
@MainActor
|
|
51
|
+
func checkNotificationStatus() async -> UNAuthorizationStatus {
|
|
52
|
+
let settings = await UNUserNotificationCenter.current().notificationSettings()
|
|
53
|
+
return settings.authorizationStatus
|
|
54
|
+
// .notDetermined, .denied, .authorized, .provisional, .ephemeral
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Provisional Notifications
|
|
59
|
+
|
|
60
|
+
Provisional notifications deliver quietly to the notification center without interrupting the user. The user can then choose to keep or turn them off. Use for onboarding flows where you want to demonstrate value before asking for full permission.
|
|
61
|
+
|
|
62
|
+
```swift
|
|
63
|
+
// Delivers silently -- no permission prompt shown to the user
|
|
64
|
+
try await center.requestAuthorization(options: [.alert, .sound, .badge, .provisional])
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Critical Alerts
|
|
68
|
+
|
|
69
|
+
Critical alerts bypass Do Not Disturb and the mute switch. Requires a special entitlement from Apple (request via developer portal). Use only for health, safety, or security scenarios.
|
|
70
|
+
|
|
71
|
+
```swift
|
|
72
|
+
// Requires com.apple.developer.usernotifications.critical-alerts entitlement
|
|
73
|
+
try await center.requestAuthorization(
|
|
74
|
+
options: [.alert, .sound, .badge, .criticalAlert]
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Handling Denied Permissions
|
|
79
|
+
|
|
80
|
+
When the user has denied notifications, guide them to Settings. Do not repeatedly prompt or nag.
|
|
81
|
+
|
|
82
|
+
```swift
|
|
83
|
+
struct NotificationSettingsButton: View {
|
|
84
|
+
@Environment(\.openURL) private var openURL
|
|
85
|
+
|
|
86
|
+
var body: some View {
|
|
87
|
+
Button("Open Settings") {
|
|
88
|
+
if let url = URL(string: UIApplication.openSettingsURLString) {
|
|
89
|
+
openURL(url)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## APNs Registration
|
|
98
|
+
|
|
99
|
+
Use `UIApplicationDelegateAdaptor` to receive the device token in a SwiftUI app. The AppDelegate callbacks are the only way to receive APNs tokens.
|
|
100
|
+
|
|
101
|
+
```swift
|
|
102
|
+
@main
|
|
103
|
+
struct MyApp: App {
|
|
104
|
+
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
|
105
|
+
|
|
106
|
+
var body: some Scene {
|
|
107
|
+
WindowGroup {
|
|
108
|
+
ContentView()
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
class AppDelegate: NSObject, UIApplicationDelegate {
|
|
114
|
+
func application(
|
|
115
|
+
_ application: UIApplication,
|
|
116
|
+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
|
117
|
+
) -> Bool {
|
|
118
|
+
UNUserNotificationCenter.current().delegate = NotificationDelegate.shared
|
|
119
|
+
return true
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
func application(
|
|
123
|
+
_ application: UIApplication,
|
|
124
|
+
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
|
|
125
|
+
) {
|
|
126
|
+
let token = deviceToken.map { String(format: "%02x", $0) }.joined()
|
|
127
|
+
print("APNs token: \(token)")
|
|
128
|
+
// Send token to your server
|
|
129
|
+
Task { await TokenService.shared.upload(token: token) }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
func application(
|
|
133
|
+
_ application: UIApplication,
|
|
134
|
+
didFailToRegisterForRemoteNotificationsWithError error: Error
|
|
135
|
+
) {
|
|
136
|
+
print("APNs registration failed: \(error.localizedDescription)")
|
|
137
|
+
// Simulator always fails -- this is expected during development
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Registration Order
|
|
143
|
+
|
|
144
|
+
Request authorization first, then register for remote notifications. Registration triggers the system to contact APNs and return a device token.
|
|
145
|
+
|
|
146
|
+
```swift
|
|
147
|
+
@MainActor
|
|
148
|
+
func registerForPush() async {
|
|
149
|
+
let granted = await requestNotificationPermission()
|
|
150
|
+
guard granted else { return }
|
|
151
|
+
UIApplication.shared.registerForRemoteNotifications()
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Token Handling
|
|
156
|
+
|
|
157
|
+
Device tokens change. Re-send the token to your server every time `didRegisterForRemoteNotificationsWithDeviceToken` fires, not just the first time. The system calls this method on every app launch that calls `registerForRemoteNotifications()`.
|
|
158
|
+
|
|
159
|
+
## Local Notifications
|
|
160
|
+
|
|
161
|
+
Schedule notifications directly from the device without a server. Useful for reminders, timers, and location-based alerts.
|
|
162
|
+
|
|
163
|
+
### Creating Content
|
|
164
|
+
|
|
165
|
+
```swift
|
|
166
|
+
let content = UNMutableNotificationContent()
|
|
167
|
+
content.title = "Workout Reminder"
|
|
168
|
+
content.subtitle = "Time to move"
|
|
169
|
+
content.body = "You have a scheduled workout in 15 minutes."
|
|
170
|
+
content.sound = .default
|
|
171
|
+
content.badge = 1
|
|
172
|
+
content.userInfo = ["workoutId": "abc123"]
|
|
173
|
+
content.threadIdentifier = "workouts" // groups in notification center
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Trigger Types
|
|
177
|
+
|
|
178
|
+
```swift
|
|
179
|
+
// Fire after a time interval (minimum 60 seconds for repeating)
|
|
180
|
+
let timeTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 300, repeats: false)
|
|
181
|
+
|
|
182
|
+
// Fire at a specific date/time
|
|
183
|
+
var dateComponents = DateComponents()
|
|
184
|
+
dateComponents.hour = 8
|
|
185
|
+
dateComponents.minute = 30
|
|
186
|
+
let calendarTrigger = UNCalendarNotificationTrigger(
|
|
187
|
+
dateMatching: dateComponents, repeats: true // daily at 8:30 AM
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
// Fire when entering a geographic region
|
|
191
|
+
let region = CLCircularRegion(
|
|
192
|
+
center: CLLocationCoordinate2D(latitude: 37.33, longitude: -122.01),
|
|
193
|
+
radius: 100,
|
|
194
|
+
identifier: "gym"
|
|
195
|
+
)
|
|
196
|
+
region.notifyOnEntry = true
|
|
197
|
+
region.notifyOnExit = false
|
|
198
|
+
let locationTrigger = UNLocationNotificationTrigger(region: region, repeats: false)
|
|
199
|
+
// Requires "When In Use" location permission at minimum
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Scheduling and Managing
|
|
203
|
+
|
|
204
|
+
```swift
|
|
205
|
+
let request = UNNotificationRequest(
|
|
206
|
+
identifier: "workout-reminder-abc123",
|
|
207
|
+
content: content,
|
|
208
|
+
trigger: timeTrigger
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
let center = UNUserNotificationCenter.current()
|
|
212
|
+
try await center.add(request)
|
|
213
|
+
|
|
214
|
+
// Remove specific pending notifications
|
|
215
|
+
center.removePendingNotificationRequests(withIdentifiers: ["workout-reminder-abc123"])
|
|
216
|
+
|
|
217
|
+
// Remove all pending
|
|
218
|
+
center.removeAllPendingNotificationRequests()
|
|
219
|
+
|
|
220
|
+
// Remove delivered notifications from notification center
|
|
221
|
+
center.removeDeliveredNotifications(withIdentifiers: ["workout-reminder-abc123"])
|
|
222
|
+
center.removeAllDeliveredNotifications()
|
|
223
|
+
|
|
224
|
+
// List all pending requests
|
|
225
|
+
let pending = await center.pendingNotificationRequests()
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Remote Notification Payload
|
|
229
|
+
|
|
230
|
+
### Standard APNs Payload
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"aps": {
|
|
235
|
+
"alert": {
|
|
236
|
+
"title": "New Message",
|
|
237
|
+
"subtitle": "From Alice",
|
|
238
|
+
"body": "Hey, are you free for lunch?"
|
|
239
|
+
},
|
|
240
|
+
"badge": 3,
|
|
241
|
+
"sound": "default",
|
|
242
|
+
"thread-id": "chat-alice",
|
|
243
|
+
"category": "MESSAGE_CATEGORY"
|
|
244
|
+
},
|
|
245
|
+
"messageId": "msg-789",
|
|
246
|
+
"senderId": "user-alice"
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Silent / Background Push
|
|
251
|
+
|
|
252
|
+
Set `content-available: 1` with no alert, sound, or badge. The system wakes the app in the background. Requires the "Background Modes > Remote notifications" capability.
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"aps": {
|
|
257
|
+
"content-available": 1
|
|
258
|
+
},
|
|
259
|
+
"updateType": "new-data"
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Handle in AppDelegate:
|
|
264
|
+
```swift
|
|
265
|
+
func application(
|
|
266
|
+
_ application: UIApplication,
|
|
267
|
+
didReceiveRemoteNotification userInfo: [AnyHashable: Any]
|
|
268
|
+
) async -> UIBackgroundFetchResult {
|
|
269
|
+
guard let updateType = userInfo["updateType"] as? String else {
|
|
270
|
+
return .noData
|
|
271
|
+
}
|
|
272
|
+
do {
|
|
273
|
+
try await DataSyncService.shared.sync(trigger: updateType)
|
|
274
|
+
return .newData
|
|
275
|
+
} catch {
|
|
276
|
+
return .failed
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Mutable Content
|
|
282
|
+
|
|
283
|
+
Set `mutable-content: 1` to allow a Notification Service Extension to modify content before display. Use for downloading images, decrypting content, or adding attachments.
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"aps": {
|
|
288
|
+
"alert": { "title": "Photo", "body": "Alice sent a photo" },
|
|
289
|
+
"mutable-content": 1
|
|
290
|
+
},
|
|
291
|
+
"imageUrl": "https://example.com/photo.jpg"
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Localized Notifications
|
|
296
|
+
|
|
297
|
+
Use localization keys so the notification displays in the user's language:
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"aps": {
|
|
302
|
+
"alert": {
|
|
303
|
+
"title-loc-key": "NEW_MESSAGE_TITLE",
|
|
304
|
+
"loc-key": "NEW_MESSAGE_BODY",
|
|
305
|
+
"loc-args": ["Alice"]
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Notification Handling
|
|
312
|
+
|
|
313
|
+
### UNUserNotificationCenterDelegate
|
|
314
|
+
|
|
315
|
+
Implement the delegate to control foreground display and handle user taps. Set the delegate as early as possible -- in `application(_:didFinishLaunchingWithOptions:)` or `App.init`.
|
|
316
|
+
|
|
317
|
+
```swift
|
|
318
|
+
@MainActor
|
|
319
|
+
final class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate, Sendable {
|
|
320
|
+
static let shared = NotificationDelegate()
|
|
321
|
+
|
|
322
|
+
// Called when notification arrives while app is in FOREGROUND
|
|
323
|
+
func userNotificationCenter(
|
|
324
|
+
_ center: UNUserNotificationCenter,
|
|
325
|
+
willPresent notification: UNNotification
|
|
326
|
+
) async -> UNNotificationPresentationOptions {
|
|
327
|
+
// Return which presentation elements to show
|
|
328
|
+
// Without this, foreground notifications are silently suppressed
|
|
329
|
+
return [.banner, .sound, .badge]
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Called when user TAPS the notification
|
|
333
|
+
func userNotificationCenter(
|
|
334
|
+
_ center: UNUserNotificationCenter,
|
|
335
|
+
didReceive response: UNNotificationResponse
|
|
336
|
+
) async {
|
|
337
|
+
let userInfo = response.notification.request.content.userInfo
|
|
338
|
+
let actionIdentifier = response.actionIdentifier
|
|
339
|
+
|
|
340
|
+
switch actionIdentifier {
|
|
341
|
+
case UNNotificationDefaultActionIdentifier:
|
|
342
|
+
// User tapped the notification body
|
|
343
|
+
await handleNotificationTap(userInfo: userInfo)
|
|
344
|
+
case UNNotificationDismissActionIdentifier:
|
|
345
|
+
// User dismissed the notification
|
|
346
|
+
break
|
|
347
|
+
default:
|
|
348
|
+
// Custom action button tapped
|
|
349
|
+
await handleCustomAction(actionIdentifier, userInfo: userInfo)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Deep Linking from Notifications
|
|
356
|
+
|
|
357
|
+
Route notification taps to the correct screen using a shared `@Observable` router. The delegate writes a pending destination; the SwiftUI view observes and consumes it.
|
|
358
|
+
|
|
359
|
+
```swift
|
|
360
|
+
@Observable @MainActor
|
|
361
|
+
final class DeepLinkRouter {
|
|
362
|
+
var pendingDestination: AppDestination?
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// In NotificationDelegate:
|
|
366
|
+
func handleNotificationTap(userInfo: [AnyHashable: Any]) async {
|
|
367
|
+
guard let id = userInfo["messageId"] as? String else { return }
|
|
368
|
+
DeepLinkRouter.shared.pendingDestination = .chat(id: id)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// In SwiftUI -- observe and consume:
|
|
372
|
+
.onChange(of: router.pendingDestination) { _, destination in
|
|
373
|
+
if let destination {
|
|
374
|
+
path.append(destination)
|
|
375
|
+
router.pendingDestination = nil
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
See `references/notification-patterns.md` for the full deep-linking handler with tab switching.
|
|
381
|
+
|
|
382
|
+
## Notification Actions and Categories
|
|
383
|
+
|
|
384
|
+
Define interactive actions that appear as buttons on the notification. Register categories at launch.
|
|
385
|
+
|
|
386
|
+
### Defining Categories and Actions
|
|
387
|
+
|
|
388
|
+
```swift
|
|
389
|
+
func registerNotificationCategories() {
|
|
390
|
+
let replyAction = UNTextInputNotificationAction(
|
|
391
|
+
identifier: "REPLY_ACTION",
|
|
392
|
+
title: "Reply",
|
|
393
|
+
options: [],
|
|
394
|
+
textInputButtonTitle: "Send",
|
|
395
|
+
textInputPlaceholder: "Type a reply..."
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
let likeAction = UNNotificationAction(
|
|
399
|
+
identifier: "LIKE_ACTION",
|
|
400
|
+
title: "Like",
|
|
401
|
+
options: []
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
let deleteAction = UNNotificationAction(
|
|
405
|
+
identifier: "DELETE_ACTION",
|
|
406
|
+
title: "Delete",
|
|
407
|
+
options: [.destructive, .authenticationRequired]
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
let messageCategory = UNNotificationCategory(
|
|
411
|
+
identifier: "MESSAGE_CATEGORY",
|
|
412
|
+
actions: [replyAction, likeAction, deleteAction],
|
|
413
|
+
intentIdentifiers: [],
|
|
414
|
+
options: [.customDismissAction] // fires didReceive on dismiss too
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
UNUserNotificationCenter.current().setNotificationCategories([messageCategory])
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Handling Action Responses
|
|
422
|
+
|
|
423
|
+
```swift
|
|
424
|
+
func handleCustomAction(_ identifier: String, userInfo: [AnyHashable: Any]) async {
|
|
425
|
+
switch identifier {
|
|
426
|
+
case "REPLY_ACTION":
|
|
427
|
+
// response is UNTextInputNotificationResponse for text input actions
|
|
428
|
+
break
|
|
429
|
+
case "LIKE_ACTION":
|
|
430
|
+
guard let messageId = userInfo["messageId"] as? String else { return }
|
|
431
|
+
await MessageService.shared.likeMessage(id: messageId)
|
|
432
|
+
case "DELETE_ACTION":
|
|
433
|
+
guard let messageId = userInfo["messageId"] as? String else { return }
|
|
434
|
+
await MessageService.shared.deleteMessage(id: messageId)
|
|
435
|
+
default:
|
|
436
|
+
break
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Action options:
|
|
442
|
+
- `.authenticationRequired` -- device must be unlocked to perform the action
|
|
443
|
+
- `.destructive` -- displayed in red; use for delete/remove actions
|
|
444
|
+
- `.foreground` -- launches the app to the foreground when tapped
|
|
445
|
+
|
|
446
|
+
## Notification Grouping
|
|
447
|
+
|
|
448
|
+
Group related notifications with `threadIdentifier` (or `thread-id` in the APNs payload). Each unique thread becomes a separate group in Notification Center.
|
|
449
|
+
|
|
450
|
+
```swift
|
|
451
|
+
content.threadIdentifier = "chat-alice" // all messages from Alice group together
|
|
452
|
+
content.summaryArgument = "Alice"
|
|
453
|
+
content.summaryArgumentCount = 3 // "3 more notifications from Alice"
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Customize the summary format string in the category:
|
|
457
|
+
|
|
458
|
+
```swift
|
|
459
|
+
let category = UNNotificationCategory(
|
|
460
|
+
identifier: "MESSAGE_CATEGORY",
|
|
461
|
+
actions: [replyAction],
|
|
462
|
+
intentIdentifiers: [],
|
|
463
|
+
categorySummaryFormat: "%u more messages from %@",
|
|
464
|
+
options: []
|
|
465
|
+
)
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Common Mistakes
|
|
469
|
+
|
|
470
|
+
**DON'T:** Register for remote notifications before requesting authorization.
|
|
471
|
+
**DO:** Call `requestAuthorization` first, then `registerForRemoteNotifications()`.
|
|
472
|
+
|
|
473
|
+
**DON'T:** Convert device token with `String(data: deviceToken, encoding: .utf8)`.
|
|
474
|
+
**DO:** Use hex: `deviceToken.map { String(format: "%02x", $0) }.joined()`.
|
|
475
|
+
|
|
476
|
+
**DON'T:** Assume notifications always arrive. APNs is best-effort.
|
|
477
|
+
**DO:** Design features that degrade gracefully; use background refresh as fallback.
|
|
478
|
+
|
|
479
|
+
**DON'T:** Put sensitive data directly in the notification payload.
|
|
480
|
+
**DO:** Use `mutable-content: 1` with a Notification Service Extension.
|
|
481
|
+
|
|
482
|
+
**DON'T:** Forget foreground handling. Without `willPresent`, notifications are silently suppressed.
|
|
483
|
+
**DO:** Implement `willPresent` and return `.banner`, `.sound`, `.badge`.
|
|
484
|
+
|
|
485
|
+
**DON'T:** Set delegate too late or register from SwiftUI views without AppDelegate adaptor.
|
|
486
|
+
**DO:** Set delegate in `App.init`; use `UIApplicationDelegateAdaptor` for APNs.
|
|
487
|
+
|
|
488
|
+
**DON'T:** Send device token only once — tokens change. Re-send on every callback.
|
|
489
|
+
|
|
490
|
+
## Review Checklist
|
|
491
|
+
|
|
492
|
+
- [ ] Authorization requested before registering; denied case handled (Settings link)
|
|
493
|
+
- [ ] Device token converted to hex string (not `String(data:encoding:)`)
|
|
494
|
+
- [ ] `UNUserNotificationCenterDelegate` set in `App.init` or `application(_:didFinishLaunching:)`
|
|
495
|
+
- [ ] Foreground (`willPresent`) and tap (`didReceive`) handling implemented
|
|
496
|
+
- [ ] Categories/actions registered at launch if interactive notifications needed
|
|
497
|
+
- [ ] Silent push configured (Background Modes enabled); `UIApplicationDelegateAdaptor` for APNs
|
|
498
|
+
|
|
499
|
+
## References
|
|
500
|
+
- `references/notification-patterns.md` — AppDelegate setup, APNs callbacks, deep-link router, silent push, debugging
|
|
501
501
|
- `references/rich-notifications.md` — Service Extension, Content Extension, attachments, communication notifications
|