@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
|
@@ -1,602 +1,602 @@
|
|
|
1
|
-
# HealthKit Extended Patterns
|
|
2
|
-
|
|
3
|
-
Overflow reference for the `healthkit` skill. Contains advanced patterns that exceed the main skill file's scope.
|
|
4
|
-
|
|
5
|
-
## Contents
|
|
6
|
-
|
|
7
|
-
- [Workout Session Lifecycle](#workout-session-lifecycle)
|
|
8
|
-
- [Anchored Object Queries](#anchored-object-queries)
|
|
9
|
-
- [Predicate-Based Filtering](#predicate-based-filtering)
|
|
10
|
-
- [Statistics Collection for Charts](#statistics-collection-for-charts)
|
|
11
|
-
- [HealthKit + SwiftUI Integration](#healthkit--swiftui-integration)
|
|
12
|
-
- [Characteristic Types](#characteristic-types)
|
|
13
|
-
|
|
14
|
-
## Workout Session Lifecycle
|
|
15
|
-
|
|
16
|
-
### Full Workout Manager
|
|
17
|
-
|
|
18
|
-
```swift
|
|
19
|
-
import HealthKit
|
|
20
|
-
|
|
21
|
-
@Observable
|
|
22
|
-
@MainActor
|
|
23
|
-
final class WorkoutManager: NSObject {
|
|
24
|
-
let healthStore = HKHealthStore()
|
|
25
|
-
|
|
26
|
-
private var session: HKWorkoutSession?
|
|
27
|
-
private var builder: HKLiveWorkoutBuilder?
|
|
28
|
-
|
|
29
|
-
var heartRate: Double = 0
|
|
30
|
-
var activeCalories: Double = 0
|
|
31
|
-
var distance: Double = 0
|
|
32
|
-
var elapsedTime: TimeInterval = 0
|
|
33
|
-
var isActive = false
|
|
34
|
-
|
|
35
|
-
func startWorkout(activityType: HKWorkoutActivityType) async throws {
|
|
36
|
-
let configuration = HKWorkoutConfiguration()
|
|
37
|
-
configuration.activityType = activityType
|
|
38
|
-
configuration.locationType = .outdoor
|
|
39
|
-
|
|
40
|
-
let session = try HKWorkoutSession(
|
|
41
|
-
healthStore: healthStore,
|
|
42
|
-
configuration: configuration
|
|
43
|
-
)
|
|
44
|
-
let builder = session.associatedWorkoutBuilder()
|
|
45
|
-
|
|
46
|
-
builder.dataSource = HKLiveWorkoutDataSource(
|
|
47
|
-
healthStore: healthStore,
|
|
48
|
-
workoutConfiguration: configuration
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
session.delegate = self
|
|
52
|
-
builder.delegate = self
|
|
53
|
-
|
|
54
|
-
self.session = session
|
|
55
|
-
self.builder = builder
|
|
56
|
-
|
|
57
|
-
session.startActivity(with: Date())
|
|
58
|
-
try await builder.beginCollection(at: Date())
|
|
59
|
-
isActive = true
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
func pause() {
|
|
63
|
-
session?.pause()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
func resume() {
|
|
67
|
-
session?.resume()
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
func end() async throws {
|
|
71
|
-
guard let session, let builder else { return }
|
|
72
|
-
session.end()
|
|
73
|
-
try await builder.endCollection(at: Date())
|
|
74
|
-
try await builder.finishWorkout()
|
|
75
|
-
isActive = false
|
|
76
|
-
self.session = nil
|
|
77
|
-
self.builder = nil
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// MARK: - HKWorkoutSessionDelegate
|
|
82
|
-
|
|
83
|
-
extension WorkoutManager: HKWorkoutSessionDelegate {
|
|
84
|
-
nonisolated func workoutSession(
|
|
85
|
-
_ workoutSession: HKWorkoutSession,
|
|
86
|
-
didChangeTo toState: HKWorkoutSessionState,
|
|
87
|
-
from fromState: HKWorkoutSessionState,
|
|
88
|
-
date: Date
|
|
89
|
-
) {
|
|
90
|
-
Task { @MainActor in
|
|
91
|
-
switch toState {
|
|
92
|
-
case .running:
|
|
93
|
-
isActive = true
|
|
94
|
-
case .paused:
|
|
95
|
-
isActive = false
|
|
96
|
-
case .ended, .stopped:
|
|
97
|
-
isActive = false
|
|
98
|
-
default:
|
|
99
|
-
break
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
nonisolated func workoutSession(
|
|
105
|
-
_ workoutSession: HKWorkoutSession,
|
|
106
|
-
didFailWithError error: Error
|
|
107
|
-
) {
|
|
108
|
-
Task { @MainActor in
|
|
109
|
-
print("Workout session failed: \(error)")
|
|
110
|
-
isActive = false
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// MARK: - HKLiveWorkoutBuilderDelegate
|
|
116
|
-
|
|
117
|
-
extension WorkoutManager: HKLiveWorkoutBuilderDelegate {
|
|
118
|
-
nonisolated func workoutBuilderDidCollectEvent(
|
|
119
|
-
_ workoutBuilder: HKLiveWorkoutBuilder
|
|
120
|
-
) {
|
|
121
|
-
// Handle workout events (pause, resume, lap, etc.)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
nonisolated func workoutBuilder(
|
|
125
|
-
_ workoutBuilder: HKLiveWorkoutBuilder,
|
|
126
|
-
didCollectDataOf collectedTypes: Set<HKSampleType>
|
|
127
|
-
) {
|
|
128
|
-
Task { @MainActor in
|
|
129
|
-
for type in collectedTypes {
|
|
130
|
-
guard let quantityType = type as? HKQuantityType else { continue }
|
|
131
|
-
|
|
132
|
-
let statistics = workoutBuilder.statistics(for: quantityType)
|
|
133
|
-
|
|
134
|
-
switch quantityType {
|
|
135
|
-
case HKQuantityType(.heartRate):
|
|
136
|
-
heartRate = statistics?.mostRecentQuantity()?
|
|
137
|
-
.doubleValue(for: HKUnit.count().unitDivided(by: .minute())) ?? 0
|
|
138
|
-
|
|
139
|
-
case HKQuantityType(.activeEnergyBurned):
|
|
140
|
-
activeCalories = statistics?.sumQuantity()?
|
|
141
|
-
.doubleValue(for: .kilocalorie()) ?? 0
|
|
142
|
-
|
|
143
|
-
case HKQuantityType(.distanceWalkingRunning):
|
|
144
|
-
distance = statistics?.sumQuantity()?
|
|
145
|
-
.doubleValue(for: .meter()) ?? 0
|
|
146
|
-
|
|
147
|
-
default:
|
|
148
|
-
break
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
elapsedTime = workoutBuilder.elapsedTime
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### Multi-Device Mirroring (watchOS + iOS)
|
|
159
|
-
|
|
160
|
-
```swift
|
|
161
|
-
// On watchOS: start mirroring to companion iPhone
|
|
162
|
-
func startMirroring() async throws {
|
|
163
|
-
try await session?.startMirroringToCompanionDevice()
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// On iOS: receive the mirrored session
|
|
167
|
-
func setupMirroredSessionHandler() {
|
|
168
|
-
healthStore.workoutSessionMirroringStartHandler = { mirroredSession in
|
|
169
|
-
// mirroredSession is an HKWorkoutSession with type == .mirrored
|
|
170
|
-
mirroredSession.delegate = self
|
|
171
|
-
let builder = mirroredSession.associatedWorkoutBuilder()
|
|
172
|
-
builder.delegate = self
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Send data between devices
|
|
177
|
-
func sendDataToRemote(_ data: Data) async throws {
|
|
178
|
-
try await session?.sendToRemoteWorkoutSession(data: data)
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
## Anchored Object Queries
|
|
183
|
-
|
|
184
|
-
Use `HKAnchoredObjectQuery` for incremental updates -- only fetches samples added or deleted since the last anchor.
|
|
185
|
-
|
|
186
|
-
```swift
|
|
187
|
-
@Observable
|
|
188
|
-
final class StepTracker {
|
|
189
|
-
private let healthStore = HKHealthStore()
|
|
190
|
-
private var anchor: HKQueryAnchor?
|
|
191
|
-
private var observerQuery: HKObserverQuery?
|
|
192
|
-
|
|
193
|
-
var totalSteps: Double = 0
|
|
194
|
-
|
|
195
|
-
func startMonitoring() {
|
|
196
|
-
let stepType = HKQuantityType(.stepCount)
|
|
197
|
-
|
|
198
|
-
// Initial fetch + ongoing updates
|
|
199
|
-
let anchoredQuery = HKAnchoredObjectQuery(
|
|
200
|
-
type: stepType,
|
|
201
|
-
predicate: nil,
|
|
202
|
-
anchor: anchor,
|
|
203
|
-
limit: HKObjectQueryNoLimit
|
|
204
|
-
) { [weak self] query, added, deleted, newAnchor, error in
|
|
205
|
-
guard let self else { return }
|
|
206
|
-
self.anchor = newAnchor
|
|
207
|
-
self.processNewSamples(added ?? [])
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Enable updates handler for real-time monitoring
|
|
211
|
-
anchoredQuery.updateHandler = { [weak self] query, added, deleted, newAnchor, error in
|
|
212
|
-
guard let self else { return }
|
|
213
|
-
self.anchor = newAnchor
|
|
214
|
-
self.processNewSamples(added ?? [])
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
healthStore.execute(anchoredQuery)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
private func processNewSamples(_ samples: [HKSample]) {
|
|
221
|
-
for sample in samples {
|
|
222
|
-
guard let quantitySample = sample as? HKQuantitySample else { continue }
|
|
223
|
-
let steps = quantitySample.quantity.doubleValue(for: .count())
|
|
224
|
-
totalSteps += steps
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
### Anchored Query with Async Descriptor
|
|
231
|
-
|
|
232
|
-
```swift
|
|
233
|
-
import HealthKit
|
|
234
|
-
|
|
235
|
-
let stepType = HKQuantityType(.stepCount)
|
|
236
|
-
let descriptor = HKAnchoredObjectQueryDescriptor(
|
|
237
|
-
predicates: [.quantitySample(type: stepType)],
|
|
238
|
-
anchor: savedAnchor
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
// One-shot
|
|
242
|
-
let result = try await descriptor.result(for: healthStore)
|
|
243
|
-
let newSamples = result.addedSamples
|
|
244
|
-
let deletedObjects = result.deletedObjects
|
|
245
|
-
let newAnchor = result.newAnchor
|
|
246
|
-
|
|
247
|
-
// Long-running with updates
|
|
248
|
-
for try await result in descriptor.results(for: healthStore) {
|
|
249
|
-
// Process result.addedSamples and result.deletedObjects
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
## Predicate-Based Filtering
|
|
254
|
-
|
|
255
|
-
### Time-Based Predicates
|
|
256
|
-
|
|
257
|
-
```swift
|
|
258
|
-
// Samples from today
|
|
259
|
-
let today = Calendar.current.startOfDay(for: Date())
|
|
260
|
-
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: today)!
|
|
261
|
-
let todayPredicate = HKQuery.predicateForSamples(
|
|
262
|
-
withStart: today, end: tomorrow
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
// Samples from the last 7 days
|
|
266
|
-
let oneWeekAgo = Calendar.current.date(byAdding: .day, value: -7, to: Date())!
|
|
267
|
-
let weekPredicate = HKQuery.predicateForSamples(
|
|
268
|
-
withStart: oneWeekAgo, end: Date()
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
// Strict: sample must be entirely within the range
|
|
272
|
-
let strictPredicate = HKQuery.predicateForSamples(
|
|
273
|
-
withStart: today, end: tomorrow,
|
|
274
|
-
options: .strictStartDate
|
|
275
|
-
)
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
### Source-Based Predicates
|
|
279
|
-
|
|
280
|
-
```swift
|
|
281
|
-
// Only samples from the current app
|
|
282
|
-
let sourcePredicate = HKQuery.predicateForObjects(
|
|
283
|
-
from: HKSource.default()
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
// Only samples from Apple Watch
|
|
287
|
-
let devicePredicate = HKQuery.predicateForObjects(
|
|
288
|
-
withDeviceProperty: HKDevicePropertyKeyModel,
|
|
289
|
-
allowedValues: ["Watch"]
|
|
290
|
-
)
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### Compound Predicates
|
|
294
|
-
|
|
295
|
-
```swift
|
|
296
|
-
let todayFromWatch = NSCompoundPredicate(
|
|
297
|
-
andPredicateWithSubpredicates: [todayPredicate, devicePredicate]
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
let descriptor = HKSampleQueryDescriptor(
|
|
301
|
-
predicates: [.quantitySample(type: stepType, predicate: todayFromWatch)],
|
|
302
|
-
sortDescriptors: [SortDescriptor(\.endDate, order: .reverse)]
|
|
303
|
-
)
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
## Statistics Collection for Charts
|
|
307
|
-
|
|
308
|
-
### Weekly Step Count Chart Data
|
|
309
|
-
|
|
310
|
-
```swift
|
|
311
|
-
struct DailyStepData: Identifiable {
|
|
312
|
-
let id = UUID()
|
|
313
|
-
let date: Date
|
|
314
|
-
let steps: Double
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
func fetchWeeklyStepData() async throws -> [DailyStepData] {
|
|
318
|
-
let calendar = Calendar.current
|
|
319
|
-
let today = calendar.startOfDay(for: Date())
|
|
320
|
-
let endDate = calendar.date(byAdding: .day, value: 1, to: today)!
|
|
321
|
-
let startDate = calendar.date(byAdding: .day, value: -7, to: endDate)!
|
|
322
|
-
|
|
323
|
-
let stepType = HKQuantityType(.stepCount)
|
|
324
|
-
let predicate = HKQuery.predicateForSamples(
|
|
325
|
-
withStart: startDate, end: endDate
|
|
326
|
-
)
|
|
327
|
-
let samplePredicate = HKSamplePredicate.quantitySample(
|
|
328
|
-
type: stepType, predicate: predicate
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
let query = HKStatisticsCollectionQueryDescriptor(
|
|
332
|
-
predicate: samplePredicate,
|
|
333
|
-
options: .cumulativeSum,
|
|
334
|
-
anchorDate: endDate,
|
|
335
|
-
intervalComponents: DateComponents(day: 1)
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
let collection = try await query.result(for: healthStore)
|
|
339
|
-
|
|
340
|
-
var data: [DailyStepData] = []
|
|
341
|
-
collection.statisticsCollection.enumerateStatistics(
|
|
342
|
-
from: startDate, to: endDate
|
|
343
|
-
) { statistics, _ in
|
|
344
|
-
let steps = statistics.sumQuantity()?
|
|
345
|
-
.doubleValue(for: .count()) ?? 0
|
|
346
|
-
data.append(DailyStepData(date: statistics.startDate, steps: steps))
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return data
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### Hourly Heart Rate Averages
|
|
354
|
-
|
|
355
|
-
```swift
|
|
356
|
-
func fetchHourlyHeartRate(for date: Date) async throws -> [(hour: Date, bpm: Double)] {
|
|
357
|
-
let calendar = Calendar.current
|
|
358
|
-
let startOfDay = calendar.startOfDay(for: date)
|
|
359
|
-
let endOfDay = calendar.date(byAdding: .day, value: 1, to: startOfDay)!
|
|
360
|
-
|
|
361
|
-
let heartRateType = HKQuantityType(.heartRate)
|
|
362
|
-
let predicate = HKQuery.predicateForSamples(
|
|
363
|
-
withStart: startOfDay, end: endOfDay
|
|
364
|
-
)
|
|
365
|
-
let samplePredicate = HKSamplePredicate.quantitySample(
|
|
366
|
-
type: heartRateType, predicate: predicate
|
|
367
|
-
)
|
|
368
|
-
|
|
369
|
-
let query = HKStatisticsCollectionQueryDescriptor(
|
|
370
|
-
predicate: samplePredicate,
|
|
371
|
-
options: .discreteAverage,
|
|
372
|
-
anchorDate: endOfDay,
|
|
373
|
-
intervalComponents: DateComponents(hour: 1)
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
let collection = try await query.result(for: healthStore)
|
|
377
|
-
let unit = HKUnit.count().unitDivided(by: .minute())
|
|
378
|
-
|
|
379
|
-
var hourlyData: [(hour: Date, bpm: Double)] = []
|
|
380
|
-
collection.statisticsCollection.enumerateStatistics(
|
|
381
|
-
from: startOfDay, to: endOfDay
|
|
382
|
-
) { statistics, _ in
|
|
383
|
-
if let avg = statistics.averageQuantity()?.doubleValue(for: unit) {
|
|
384
|
-
hourlyData.append((hour: statistics.startDate, bpm: avg))
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
return hourlyData
|
|
389
|
-
}
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
## HealthKit + SwiftUI Integration
|
|
393
|
-
|
|
394
|
-
### HealthKit Manager with @Observable
|
|
395
|
-
|
|
396
|
-
```swift
|
|
397
|
-
import HealthKit
|
|
398
|
-
import SwiftUI
|
|
399
|
-
|
|
400
|
-
@Observable
|
|
401
|
-
@MainActor
|
|
402
|
-
final class HealthManager {
|
|
403
|
-
let healthStore = HKHealthStore()
|
|
404
|
-
|
|
405
|
-
var isAuthorized = false
|
|
406
|
-
var todaySteps: Double = 0
|
|
407
|
-
var recentHeartRate: Double = 0
|
|
408
|
-
|
|
409
|
-
var isAvailable: Bool {
|
|
410
|
-
HKHealthStore.isHealthDataAvailable()
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
func requestAuthorization() async throws {
|
|
414
|
-
let typesToRead: Set<HKObjectType> = [
|
|
415
|
-
HKQuantityType(.stepCount),
|
|
416
|
-
HKQuantityType(.heartRate)
|
|
417
|
-
]
|
|
418
|
-
|
|
419
|
-
try await healthStore.requestAuthorization(
|
|
420
|
-
toShare: [],
|
|
421
|
-
read: typesToRead
|
|
422
|
-
)
|
|
423
|
-
isAuthorized = true
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
func refreshData() async {
|
|
427
|
-
async let steps = fetchTodaySteps()
|
|
428
|
-
async let heartRate = fetchLatestHeartRate()
|
|
429
|
-
|
|
430
|
-
todaySteps = (try? await steps) ?? 0
|
|
431
|
-
recentHeartRate = (try? await heartRate) ?? 0
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
private func fetchTodaySteps() async throws -> Double {
|
|
435
|
-
let calendar = Calendar.current
|
|
436
|
-
let startOfDay = calendar.startOfDay(for: Date())
|
|
437
|
-
let predicate = HKQuery.predicateForSamples(
|
|
438
|
-
withStart: startOfDay, end: Date()
|
|
439
|
-
)
|
|
440
|
-
let stepType = HKQuantityType(.stepCount)
|
|
441
|
-
let samplePredicate = HKSamplePredicate.quantitySample(
|
|
442
|
-
type: stepType, predicate: predicate
|
|
443
|
-
)
|
|
444
|
-
let query = HKStatisticsQueryDescriptor(
|
|
445
|
-
predicate: samplePredicate, options: .cumulativeSum
|
|
446
|
-
)
|
|
447
|
-
return try await query.result(for: healthStore)?
|
|
448
|
-
.sumQuantity()?.doubleValue(for: .count()) ?? 0
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
private func fetchLatestHeartRate() async throws -> Double {
|
|
452
|
-
let heartRateType = HKQuantityType(.heartRate)
|
|
453
|
-
let descriptor = HKSampleQueryDescriptor(
|
|
454
|
-
predicates: [.quantitySample(type: heartRateType)],
|
|
455
|
-
sortDescriptors: [SortDescriptor(\.endDate, order: .reverse)],
|
|
456
|
-
limit: 1
|
|
457
|
-
)
|
|
458
|
-
let results = try await descriptor.result(for: healthStore)
|
|
459
|
-
let unit = HKUnit.count().unitDivided(by: .minute())
|
|
460
|
-
return results.first?.quantity.doubleValue(for: unit) ?? 0
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
### SwiftUI View with HealthKit
|
|
466
|
-
|
|
467
|
-
```swift
|
|
468
|
-
struct HealthDashboardView: View {
|
|
469
|
-
@Environment(HealthManager.self) private var healthManager
|
|
470
|
-
|
|
471
|
-
var body: some View {
|
|
472
|
-
NavigationStack {
|
|
473
|
-
Group {
|
|
474
|
-
if !healthManager.isAvailable {
|
|
475
|
-
ContentUnavailableView(
|
|
476
|
-
"HealthKit Unavailable",
|
|
477
|
-
systemImage: "heart.slash",
|
|
478
|
-
description: Text("This device does not support HealthKit.")
|
|
479
|
-
)
|
|
480
|
-
} else if !healthManager.isAuthorized {
|
|
481
|
-
authorizationPrompt
|
|
482
|
-
} else {
|
|
483
|
-
healthDataView
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
.navigationTitle("Health")
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
private var authorizationPrompt: some View {
|
|
491
|
-
ContentUnavailableView {
|
|
492
|
-
Label("Health Access", systemImage: "heart.text.square")
|
|
493
|
-
} description: {
|
|
494
|
-
Text("Grant access to view your health data.")
|
|
495
|
-
} actions: {
|
|
496
|
-
Button("Authorize") {
|
|
497
|
-
Task {
|
|
498
|
-
try? await healthManager.requestAuthorization()
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
.buttonStyle(.borderedProminent)
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
private var healthDataView: some View {
|
|
506
|
-
List {
|
|
507
|
-
Section("Today") {
|
|
508
|
-
LabeledContent("Steps") {
|
|
509
|
-
Text(healthManager.todaySteps, format: .number.precision(.fractionLength(0)))
|
|
510
|
-
}
|
|
511
|
-
LabeledContent("Heart Rate") {
|
|
512
|
-
Text("\(Int(healthManager.recentHeartRate)) bpm")
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
.task {
|
|
517
|
-
await healthManager.refreshData()
|
|
518
|
-
}
|
|
519
|
-
.refreshable {
|
|
520
|
-
await healthManager.refreshData()
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
### App Entry Point Wiring
|
|
527
|
-
|
|
528
|
-
```swift
|
|
529
|
-
@main
|
|
530
|
-
struct MyHealthApp: App {
|
|
531
|
-
@State private var healthManager = HealthManager()
|
|
532
|
-
|
|
533
|
-
var body: some Scene {
|
|
534
|
-
WindowGroup {
|
|
535
|
-
HealthDashboardView()
|
|
536
|
-
.environment(healthManager)
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
## Characteristic Types
|
|
543
|
-
|
|
544
|
-
Characteristic types are read-only values set by the user in the Health app. They do not require sample queries.
|
|
545
|
-
|
|
546
|
-
```swift
|
|
547
|
-
func readCharacteristics() throws {
|
|
548
|
-
// Date of birth
|
|
549
|
-
let dobComponents = try healthStore.dateOfBirthComponents()
|
|
550
|
-
let calendar = Calendar.current
|
|
551
|
-
if let dob = calendar.date(from: dobComponents) {
|
|
552
|
-
let age = calendar.dateComponents([.year], from: dob, to: Date()).year ?? 0
|
|
553
|
-
print("Age: \(age)")
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// Biological sex
|
|
557
|
-
let biologicalSex = try healthStore.biologicalSex().biologicalSex
|
|
558
|
-
switch biologicalSex {
|
|
559
|
-
case .female: print("Female")
|
|
560
|
-
case .male: print("Male")
|
|
561
|
-
case .other: print("Other")
|
|
562
|
-
case .notSet: print("Not set")
|
|
563
|
-
@unknown default: break
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// Blood type
|
|
567
|
-
let bloodType = try healthStore.bloodType().bloodType
|
|
568
|
-
switch bloodType {
|
|
569
|
-
case .aPositive: print("A+")
|
|
570
|
-
case .aNegative: print("A-")
|
|
571
|
-
case .bPositive: print("B+")
|
|
572
|
-
case .bNegative: print("B-")
|
|
573
|
-
case .abPositive: print("AB+")
|
|
574
|
-
case .abNegative: print("AB-")
|
|
575
|
-
case .oPositive: print("O+")
|
|
576
|
-
case .oNegative: print("O-")
|
|
577
|
-
case .notSet: print("Not set")
|
|
578
|
-
@unknown default: break
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
// Fitzpatrick skin type
|
|
582
|
-
let skinType = try healthStore.fitzpatrickSkinType().skinType
|
|
583
|
-
print("Skin type: \(skinType.rawValue)")
|
|
584
|
-
|
|
585
|
-
// Wheelchair use
|
|
586
|
-
let wheelchair = try healthStore.wheelchairUse().wheelchairUse
|
|
587
|
-
print("Wheelchair: \(wheelchair == .yes)")
|
|
588
|
-
}
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
**Important:** These throw an error if the value is not set, so always use `try` and handle the `HKError.errorNoData` case.
|
|
592
|
-
|
|
593
|
-
```swift
|
|
594
|
-
do {
|
|
595
|
-
let dob = try healthStore.dateOfBirthComponents()
|
|
596
|
-
// Use dob
|
|
597
|
-
} catch let error as HKError where error.code == .errorNoData {
|
|
598
|
-
// User hasn't set date of birth in Health app
|
|
599
|
-
} catch {
|
|
600
|
-
// Other error
|
|
601
|
-
}
|
|
602
|
-
```
|
|
1
|
+
# HealthKit Extended Patterns
|
|
2
|
+
|
|
3
|
+
Overflow reference for the `healthkit` skill. Contains advanced patterns that exceed the main skill file's scope.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Workout Session Lifecycle](#workout-session-lifecycle)
|
|
8
|
+
- [Anchored Object Queries](#anchored-object-queries)
|
|
9
|
+
- [Predicate-Based Filtering](#predicate-based-filtering)
|
|
10
|
+
- [Statistics Collection for Charts](#statistics-collection-for-charts)
|
|
11
|
+
- [HealthKit + SwiftUI Integration](#healthkit--swiftui-integration)
|
|
12
|
+
- [Characteristic Types](#characteristic-types)
|
|
13
|
+
|
|
14
|
+
## Workout Session Lifecycle
|
|
15
|
+
|
|
16
|
+
### Full Workout Manager
|
|
17
|
+
|
|
18
|
+
```swift
|
|
19
|
+
import HealthKit
|
|
20
|
+
|
|
21
|
+
@Observable
|
|
22
|
+
@MainActor
|
|
23
|
+
final class WorkoutManager: NSObject {
|
|
24
|
+
let healthStore = HKHealthStore()
|
|
25
|
+
|
|
26
|
+
private var session: HKWorkoutSession?
|
|
27
|
+
private var builder: HKLiveWorkoutBuilder?
|
|
28
|
+
|
|
29
|
+
var heartRate: Double = 0
|
|
30
|
+
var activeCalories: Double = 0
|
|
31
|
+
var distance: Double = 0
|
|
32
|
+
var elapsedTime: TimeInterval = 0
|
|
33
|
+
var isActive = false
|
|
34
|
+
|
|
35
|
+
func startWorkout(activityType: HKWorkoutActivityType) async throws {
|
|
36
|
+
let configuration = HKWorkoutConfiguration()
|
|
37
|
+
configuration.activityType = activityType
|
|
38
|
+
configuration.locationType = .outdoor
|
|
39
|
+
|
|
40
|
+
let session = try HKWorkoutSession(
|
|
41
|
+
healthStore: healthStore,
|
|
42
|
+
configuration: configuration
|
|
43
|
+
)
|
|
44
|
+
let builder = session.associatedWorkoutBuilder()
|
|
45
|
+
|
|
46
|
+
builder.dataSource = HKLiveWorkoutDataSource(
|
|
47
|
+
healthStore: healthStore,
|
|
48
|
+
workoutConfiguration: configuration
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
session.delegate = self
|
|
52
|
+
builder.delegate = self
|
|
53
|
+
|
|
54
|
+
self.session = session
|
|
55
|
+
self.builder = builder
|
|
56
|
+
|
|
57
|
+
session.startActivity(with: Date())
|
|
58
|
+
try await builder.beginCollection(at: Date())
|
|
59
|
+
isActive = true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func pause() {
|
|
63
|
+
session?.pause()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func resume() {
|
|
67
|
+
session?.resume()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func end() async throws {
|
|
71
|
+
guard let session, let builder else { return }
|
|
72
|
+
session.end()
|
|
73
|
+
try await builder.endCollection(at: Date())
|
|
74
|
+
try await builder.finishWorkout()
|
|
75
|
+
isActive = false
|
|
76
|
+
self.session = nil
|
|
77
|
+
self.builder = nil
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// MARK: - HKWorkoutSessionDelegate
|
|
82
|
+
|
|
83
|
+
extension WorkoutManager: HKWorkoutSessionDelegate {
|
|
84
|
+
nonisolated func workoutSession(
|
|
85
|
+
_ workoutSession: HKWorkoutSession,
|
|
86
|
+
didChangeTo toState: HKWorkoutSessionState,
|
|
87
|
+
from fromState: HKWorkoutSessionState,
|
|
88
|
+
date: Date
|
|
89
|
+
) {
|
|
90
|
+
Task { @MainActor in
|
|
91
|
+
switch toState {
|
|
92
|
+
case .running:
|
|
93
|
+
isActive = true
|
|
94
|
+
case .paused:
|
|
95
|
+
isActive = false
|
|
96
|
+
case .ended, .stopped:
|
|
97
|
+
isActive = false
|
|
98
|
+
default:
|
|
99
|
+
break
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
nonisolated func workoutSession(
|
|
105
|
+
_ workoutSession: HKWorkoutSession,
|
|
106
|
+
didFailWithError error: Error
|
|
107
|
+
) {
|
|
108
|
+
Task { @MainActor in
|
|
109
|
+
print("Workout session failed: \(error)")
|
|
110
|
+
isActive = false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// MARK: - HKLiveWorkoutBuilderDelegate
|
|
116
|
+
|
|
117
|
+
extension WorkoutManager: HKLiveWorkoutBuilderDelegate {
|
|
118
|
+
nonisolated func workoutBuilderDidCollectEvent(
|
|
119
|
+
_ workoutBuilder: HKLiveWorkoutBuilder
|
|
120
|
+
) {
|
|
121
|
+
// Handle workout events (pause, resume, lap, etc.)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
nonisolated func workoutBuilder(
|
|
125
|
+
_ workoutBuilder: HKLiveWorkoutBuilder,
|
|
126
|
+
didCollectDataOf collectedTypes: Set<HKSampleType>
|
|
127
|
+
) {
|
|
128
|
+
Task { @MainActor in
|
|
129
|
+
for type in collectedTypes {
|
|
130
|
+
guard let quantityType = type as? HKQuantityType else { continue }
|
|
131
|
+
|
|
132
|
+
let statistics = workoutBuilder.statistics(for: quantityType)
|
|
133
|
+
|
|
134
|
+
switch quantityType {
|
|
135
|
+
case HKQuantityType(.heartRate):
|
|
136
|
+
heartRate = statistics?.mostRecentQuantity()?
|
|
137
|
+
.doubleValue(for: HKUnit.count().unitDivided(by: .minute())) ?? 0
|
|
138
|
+
|
|
139
|
+
case HKQuantityType(.activeEnergyBurned):
|
|
140
|
+
activeCalories = statistics?.sumQuantity()?
|
|
141
|
+
.doubleValue(for: .kilocalorie()) ?? 0
|
|
142
|
+
|
|
143
|
+
case HKQuantityType(.distanceWalkingRunning):
|
|
144
|
+
distance = statistics?.sumQuantity()?
|
|
145
|
+
.doubleValue(for: .meter()) ?? 0
|
|
146
|
+
|
|
147
|
+
default:
|
|
148
|
+
break
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
elapsedTime = workoutBuilder.elapsedTime
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Multi-Device Mirroring (watchOS + iOS)
|
|
159
|
+
|
|
160
|
+
```swift
|
|
161
|
+
// On watchOS: start mirroring to companion iPhone
|
|
162
|
+
func startMirroring() async throws {
|
|
163
|
+
try await session?.startMirroringToCompanionDevice()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// On iOS: receive the mirrored session
|
|
167
|
+
func setupMirroredSessionHandler() {
|
|
168
|
+
healthStore.workoutSessionMirroringStartHandler = { mirroredSession in
|
|
169
|
+
// mirroredSession is an HKWorkoutSession with type == .mirrored
|
|
170
|
+
mirroredSession.delegate = self
|
|
171
|
+
let builder = mirroredSession.associatedWorkoutBuilder()
|
|
172
|
+
builder.delegate = self
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Send data between devices
|
|
177
|
+
func sendDataToRemote(_ data: Data) async throws {
|
|
178
|
+
try await session?.sendToRemoteWorkoutSession(data: data)
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Anchored Object Queries
|
|
183
|
+
|
|
184
|
+
Use `HKAnchoredObjectQuery` for incremental updates -- only fetches samples added or deleted since the last anchor.
|
|
185
|
+
|
|
186
|
+
```swift
|
|
187
|
+
@Observable
|
|
188
|
+
final class StepTracker {
|
|
189
|
+
private let healthStore = HKHealthStore()
|
|
190
|
+
private var anchor: HKQueryAnchor?
|
|
191
|
+
private var observerQuery: HKObserverQuery?
|
|
192
|
+
|
|
193
|
+
var totalSteps: Double = 0
|
|
194
|
+
|
|
195
|
+
func startMonitoring() {
|
|
196
|
+
let stepType = HKQuantityType(.stepCount)
|
|
197
|
+
|
|
198
|
+
// Initial fetch + ongoing updates
|
|
199
|
+
let anchoredQuery = HKAnchoredObjectQuery(
|
|
200
|
+
type: stepType,
|
|
201
|
+
predicate: nil,
|
|
202
|
+
anchor: anchor,
|
|
203
|
+
limit: HKObjectQueryNoLimit
|
|
204
|
+
) { [weak self] query, added, deleted, newAnchor, error in
|
|
205
|
+
guard let self else { return }
|
|
206
|
+
self.anchor = newAnchor
|
|
207
|
+
self.processNewSamples(added ?? [])
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Enable updates handler for real-time monitoring
|
|
211
|
+
anchoredQuery.updateHandler = { [weak self] query, added, deleted, newAnchor, error in
|
|
212
|
+
guard let self else { return }
|
|
213
|
+
self.anchor = newAnchor
|
|
214
|
+
self.processNewSamples(added ?? [])
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
healthStore.execute(anchoredQuery)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private func processNewSamples(_ samples: [HKSample]) {
|
|
221
|
+
for sample in samples {
|
|
222
|
+
guard let quantitySample = sample as? HKQuantitySample else { continue }
|
|
223
|
+
let steps = quantitySample.quantity.doubleValue(for: .count())
|
|
224
|
+
totalSteps += steps
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Anchored Query with Async Descriptor
|
|
231
|
+
|
|
232
|
+
```swift
|
|
233
|
+
import HealthKit
|
|
234
|
+
|
|
235
|
+
let stepType = HKQuantityType(.stepCount)
|
|
236
|
+
let descriptor = HKAnchoredObjectQueryDescriptor(
|
|
237
|
+
predicates: [.quantitySample(type: stepType)],
|
|
238
|
+
anchor: savedAnchor
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
// One-shot
|
|
242
|
+
let result = try await descriptor.result(for: healthStore)
|
|
243
|
+
let newSamples = result.addedSamples
|
|
244
|
+
let deletedObjects = result.deletedObjects
|
|
245
|
+
let newAnchor = result.newAnchor
|
|
246
|
+
|
|
247
|
+
// Long-running with updates
|
|
248
|
+
for try await result in descriptor.results(for: healthStore) {
|
|
249
|
+
// Process result.addedSamples and result.deletedObjects
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Predicate-Based Filtering
|
|
254
|
+
|
|
255
|
+
### Time-Based Predicates
|
|
256
|
+
|
|
257
|
+
```swift
|
|
258
|
+
// Samples from today
|
|
259
|
+
let today = Calendar.current.startOfDay(for: Date())
|
|
260
|
+
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: today)!
|
|
261
|
+
let todayPredicate = HKQuery.predicateForSamples(
|
|
262
|
+
withStart: today, end: tomorrow
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
// Samples from the last 7 days
|
|
266
|
+
let oneWeekAgo = Calendar.current.date(byAdding: .day, value: -7, to: Date())!
|
|
267
|
+
let weekPredicate = HKQuery.predicateForSamples(
|
|
268
|
+
withStart: oneWeekAgo, end: Date()
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
// Strict: sample must be entirely within the range
|
|
272
|
+
let strictPredicate = HKQuery.predicateForSamples(
|
|
273
|
+
withStart: today, end: tomorrow,
|
|
274
|
+
options: .strictStartDate
|
|
275
|
+
)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Source-Based Predicates
|
|
279
|
+
|
|
280
|
+
```swift
|
|
281
|
+
// Only samples from the current app
|
|
282
|
+
let sourcePredicate = HKQuery.predicateForObjects(
|
|
283
|
+
from: HKSource.default()
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
// Only samples from Apple Watch
|
|
287
|
+
let devicePredicate = HKQuery.predicateForObjects(
|
|
288
|
+
withDeviceProperty: HKDevicePropertyKeyModel,
|
|
289
|
+
allowedValues: ["Watch"]
|
|
290
|
+
)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Compound Predicates
|
|
294
|
+
|
|
295
|
+
```swift
|
|
296
|
+
let todayFromWatch = NSCompoundPredicate(
|
|
297
|
+
andPredicateWithSubpredicates: [todayPredicate, devicePredicate]
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
let descriptor = HKSampleQueryDescriptor(
|
|
301
|
+
predicates: [.quantitySample(type: stepType, predicate: todayFromWatch)],
|
|
302
|
+
sortDescriptors: [SortDescriptor(\.endDate, order: .reverse)]
|
|
303
|
+
)
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Statistics Collection for Charts
|
|
307
|
+
|
|
308
|
+
### Weekly Step Count Chart Data
|
|
309
|
+
|
|
310
|
+
```swift
|
|
311
|
+
struct DailyStepData: Identifiable {
|
|
312
|
+
let id = UUID()
|
|
313
|
+
let date: Date
|
|
314
|
+
let steps: Double
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
func fetchWeeklyStepData() async throws -> [DailyStepData] {
|
|
318
|
+
let calendar = Calendar.current
|
|
319
|
+
let today = calendar.startOfDay(for: Date())
|
|
320
|
+
let endDate = calendar.date(byAdding: .day, value: 1, to: today)!
|
|
321
|
+
let startDate = calendar.date(byAdding: .day, value: -7, to: endDate)!
|
|
322
|
+
|
|
323
|
+
let stepType = HKQuantityType(.stepCount)
|
|
324
|
+
let predicate = HKQuery.predicateForSamples(
|
|
325
|
+
withStart: startDate, end: endDate
|
|
326
|
+
)
|
|
327
|
+
let samplePredicate = HKSamplePredicate.quantitySample(
|
|
328
|
+
type: stepType, predicate: predicate
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
let query = HKStatisticsCollectionQueryDescriptor(
|
|
332
|
+
predicate: samplePredicate,
|
|
333
|
+
options: .cumulativeSum,
|
|
334
|
+
anchorDate: endDate,
|
|
335
|
+
intervalComponents: DateComponents(day: 1)
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
let collection = try await query.result(for: healthStore)
|
|
339
|
+
|
|
340
|
+
var data: [DailyStepData] = []
|
|
341
|
+
collection.statisticsCollection.enumerateStatistics(
|
|
342
|
+
from: startDate, to: endDate
|
|
343
|
+
) { statistics, _ in
|
|
344
|
+
let steps = statistics.sumQuantity()?
|
|
345
|
+
.doubleValue(for: .count()) ?? 0
|
|
346
|
+
data.append(DailyStepData(date: statistics.startDate, steps: steps))
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return data
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Hourly Heart Rate Averages
|
|
354
|
+
|
|
355
|
+
```swift
|
|
356
|
+
func fetchHourlyHeartRate(for date: Date) async throws -> [(hour: Date, bpm: Double)] {
|
|
357
|
+
let calendar = Calendar.current
|
|
358
|
+
let startOfDay = calendar.startOfDay(for: date)
|
|
359
|
+
let endOfDay = calendar.date(byAdding: .day, value: 1, to: startOfDay)!
|
|
360
|
+
|
|
361
|
+
let heartRateType = HKQuantityType(.heartRate)
|
|
362
|
+
let predicate = HKQuery.predicateForSamples(
|
|
363
|
+
withStart: startOfDay, end: endOfDay
|
|
364
|
+
)
|
|
365
|
+
let samplePredicate = HKSamplePredicate.quantitySample(
|
|
366
|
+
type: heartRateType, predicate: predicate
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
let query = HKStatisticsCollectionQueryDescriptor(
|
|
370
|
+
predicate: samplePredicate,
|
|
371
|
+
options: .discreteAverage,
|
|
372
|
+
anchorDate: endOfDay,
|
|
373
|
+
intervalComponents: DateComponents(hour: 1)
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
let collection = try await query.result(for: healthStore)
|
|
377
|
+
let unit = HKUnit.count().unitDivided(by: .minute())
|
|
378
|
+
|
|
379
|
+
var hourlyData: [(hour: Date, bpm: Double)] = []
|
|
380
|
+
collection.statisticsCollection.enumerateStatistics(
|
|
381
|
+
from: startOfDay, to: endOfDay
|
|
382
|
+
) { statistics, _ in
|
|
383
|
+
if let avg = statistics.averageQuantity()?.doubleValue(for: unit) {
|
|
384
|
+
hourlyData.append((hour: statistics.startDate, bpm: avg))
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return hourlyData
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## HealthKit + SwiftUI Integration
|
|
393
|
+
|
|
394
|
+
### HealthKit Manager with @Observable
|
|
395
|
+
|
|
396
|
+
```swift
|
|
397
|
+
import HealthKit
|
|
398
|
+
import SwiftUI
|
|
399
|
+
|
|
400
|
+
@Observable
|
|
401
|
+
@MainActor
|
|
402
|
+
final class HealthManager {
|
|
403
|
+
let healthStore = HKHealthStore()
|
|
404
|
+
|
|
405
|
+
var isAuthorized = false
|
|
406
|
+
var todaySteps: Double = 0
|
|
407
|
+
var recentHeartRate: Double = 0
|
|
408
|
+
|
|
409
|
+
var isAvailable: Bool {
|
|
410
|
+
HKHealthStore.isHealthDataAvailable()
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
func requestAuthorization() async throws {
|
|
414
|
+
let typesToRead: Set<HKObjectType> = [
|
|
415
|
+
HKQuantityType(.stepCount),
|
|
416
|
+
HKQuantityType(.heartRate)
|
|
417
|
+
]
|
|
418
|
+
|
|
419
|
+
try await healthStore.requestAuthorization(
|
|
420
|
+
toShare: [],
|
|
421
|
+
read: typesToRead
|
|
422
|
+
)
|
|
423
|
+
isAuthorized = true
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
func refreshData() async {
|
|
427
|
+
async let steps = fetchTodaySteps()
|
|
428
|
+
async let heartRate = fetchLatestHeartRate()
|
|
429
|
+
|
|
430
|
+
todaySteps = (try? await steps) ?? 0
|
|
431
|
+
recentHeartRate = (try? await heartRate) ?? 0
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
private func fetchTodaySteps() async throws -> Double {
|
|
435
|
+
let calendar = Calendar.current
|
|
436
|
+
let startOfDay = calendar.startOfDay(for: Date())
|
|
437
|
+
let predicate = HKQuery.predicateForSamples(
|
|
438
|
+
withStart: startOfDay, end: Date()
|
|
439
|
+
)
|
|
440
|
+
let stepType = HKQuantityType(.stepCount)
|
|
441
|
+
let samplePredicate = HKSamplePredicate.quantitySample(
|
|
442
|
+
type: stepType, predicate: predicate
|
|
443
|
+
)
|
|
444
|
+
let query = HKStatisticsQueryDescriptor(
|
|
445
|
+
predicate: samplePredicate, options: .cumulativeSum
|
|
446
|
+
)
|
|
447
|
+
return try await query.result(for: healthStore)?
|
|
448
|
+
.sumQuantity()?.doubleValue(for: .count()) ?? 0
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
private func fetchLatestHeartRate() async throws -> Double {
|
|
452
|
+
let heartRateType = HKQuantityType(.heartRate)
|
|
453
|
+
let descriptor = HKSampleQueryDescriptor(
|
|
454
|
+
predicates: [.quantitySample(type: heartRateType)],
|
|
455
|
+
sortDescriptors: [SortDescriptor(\.endDate, order: .reverse)],
|
|
456
|
+
limit: 1
|
|
457
|
+
)
|
|
458
|
+
let results = try await descriptor.result(for: healthStore)
|
|
459
|
+
let unit = HKUnit.count().unitDivided(by: .minute())
|
|
460
|
+
return results.first?.quantity.doubleValue(for: unit) ?? 0
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### SwiftUI View with HealthKit
|
|
466
|
+
|
|
467
|
+
```swift
|
|
468
|
+
struct HealthDashboardView: View {
|
|
469
|
+
@Environment(HealthManager.self) private var healthManager
|
|
470
|
+
|
|
471
|
+
var body: some View {
|
|
472
|
+
NavigationStack {
|
|
473
|
+
Group {
|
|
474
|
+
if !healthManager.isAvailable {
|
|
475
|
+
ContentUnavailableView(
|
|
476
|
+
"HealthKit Unavailable",
|
|
477
|
+
systemImage: "heart.slash",
|
|
478
|
+
description: Text("This device does not support HealthKit.")
|
|
479
|
+
)
|
|
480
|
+
} else if !healthManager.isAuthorized {
|
|
481
|
+
authorizationPrompt
|
|
482
|
+
} else {
|
|
483
|
+
healthDataView
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
.navigationTitle("Health")
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
private var authorizationPrompt: some View {
|
|
491
|
+
ContentUnavailableView {
|
|
492
|
+
Label("Health Access", systemImage: "heart.text.square")
|
|
493
|
+
} description: {
|
|
494
|
+
Text("Grant access to view your health data.")
|
|
495
|
+
} actions: {
|
|
496
|
+
Button("Authorize") {
|
|
497
|
+
Task {
|
|
498
|
+
try? await healthManager.requestAuthorization()
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
.buttonStyle(.borderedProminent)
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
private var healthDataView: some View {
|
|
506
|
+
List {
|
|
507
|
+
Section("Today") {
|
|
508
|
+
LabeledContent("Steps") {
|
|
509
|
+
Text(healthManager.todaySteps, format: .number.precision(.fractionLength(0)))
|
|
510
|
+
}
|
|
511
|
+
LabeledContent("Heart Rate") {
|
|
512
|
+
Text("\(Int(healthManager.recentHeartRate)) bpm")
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
.task {
|
|
517
|
+
await healthManager.refreshData()
|
|
518
|
+
}
|
|
519
|
+
.refreshable {
|
|
520
|
+
await healthManager.refreshData()
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### App Entry Point Wiring
|
|
527
|
+
|
|
528
|
+
```swift
|
|
529
|
+
@main
|
|
530
|
+
struct MyHealthApp: App {
|
|
531
|
+
@State private var healthManager = HealthManager()
|
|
532
|
+
|
|
533
|
+
var body: some Scene {
|
|
534
|
+
WindowGroup {
|
|
535
|
+
HealthDashboardView()
|
|
536
|
+
.environment(healthManager)
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
## Characteristic Types
|
|
543
|
+
|
|
544
|
+
Characteristic types are read-only values set by the user in the Health app. They do not require sample queries.
|
|
545
|
+
|
|
546
|
+
```swift
|
|
547
|
+
func readCharacteristics() throws {
|
|
548
|
+
// Date of birth
|
|
549
|
+
let dobComponents = try healthStore.dateOfBirthComponents()
|
|
550
|
+
let calendar = Calendar.current
|
|
551
|
+
if let dob = calendar.date(from: dobComponents) {
|
|
552
|
+
let age = calendar.dateComponents([.year], from: dob, to: Date()).year ?? 0
|
|
553
|
+
print("Age: \(age)")
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Biological sex
|
|
557
|
+
let biologicalSex = try healthStore.biologicalSex().biologicalSex
|
|
558
|
+
switch biologicalSex {
|
|
559
|
+
case .female: print("Female")
|
|
560
|
+
case .male: print("Male")
|
|
561
|
+
case .other: print("Other")
|
|
562
|
+
case .notSet: print("Not set")
|
|
563
|
+
@unknown default: break
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Blood type
|
|
567
|
+
let bloodType = try healthStore.bloodType().bloodType
|
|
568
|
+
switch bloodType {
|
|
569
|
+
case .aPositive: print("A+")
|
|
570
|
+
case .aNegative: print("A-")
|
|
571
|
+
case .bPositive: print("B+")
|
|
572
|
+
case .bNegative: print("B-")
|
|
573
|
+
case .abPositive: print("AB+")
|
|
574
|
+
case .abNegative: print("AB-")
|
|
575
|
+
case .oPositive: print("O+")
|
|
576
|
+
case .oNegative: print("O-")
|
|
577
|
+
case .notSet: print("Not set")
|
|
578
|
+
@unknown default: break
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Fitzpatrick skin type
|
|
582
|
+
let skinType = try healthStore.fitzpatrickSkinType().skinType
|
|
583
|
+
print("Skin type: \(skinType.rawValue)")
|
|
584
|
+
|
|
585
|
+
// Wheelchair use
|
|
586
|
+
let wheelchair = try healthStore.wheelchairUse().wheelchairUse
|
|
587
|
+
print("Wheelchair: \(wheelchair == .yes)")
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
**Important:** These throw an error if the value is not set, so always use `try` and handle the `HKError.errorNoData` case.
|
|
592
|
+
|
|
593
|
+
```swift
|
|
594
|
+
do {
|
|
595
|
+
let dob = try healthStore.dateOfBirthComponents()
|
|
596
|
+
// Use dob
|
|
597
|
+
} catch let error as HKError where error.code == .errorNoData {
|
|
598
|
+
// User hasn't set date of birth in Health app
|
|
599
|
+
} catch {
|
|
600
|
+
// Other error
|
|
601
|
+
}
|
|
602
|
+
```
|