@devo-bmad-custom/agent-orchestration 1.0.2 → 1.0.4
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 +97 -47
- 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,534 +1,534 @@
|
|
|
1
|
-
# UIKit-to-SwiftUI Migration Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for incrementally migrating a UIKit app to SwiftUI. Each pattern is self-contained with rationale, implementation, and gotchas.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Contents
|
|
8
|
-
|
|
9
|
-
- [1. Screen-by-Screen Migration](#1-screen-by-screen-migration)
|
|
10
|
-
- [2. UIHostingController as Child](#2-uihostingcontroller-as-child)
|
|
11
|
-
- [3. Navigation Bridging](#3-navigation-bridging)
|
|
12
|
-
- [4. Data Sharing Between UIKit and SwiftUI](#4-data-sharing-between-uikit-and-swiftui)
|
|
13
|
-
- [5. UIHostingConfiguration (iOS 16+)](#5-uihostingconfiguration-ios-16)
|
|
14
|
-
- [6. Environment Bridging](#6-environment-bridging)
|
|
15
|
-
|
|
16
|
-
## 1. Screen-by-Screen Migration
|
|
17
|
-
|
|
18
|
-
Replace one `UIViewController` at a time with a `UIHostingController` wrapping a SwiftUI view. This is the safest migration path -- each screen is an isolated unit.
|
|
19
|
-
|
|
20
|
-
### Strategy
|
|
21
|
-
|
|
22
|
-
1. Pick a leaf screen (one that does not contain child view controllers).
|
|
23
|
-
2. Rewrite its UI in SwiftUI.
|
|
24
|
-
3. Replace the UIKit view controller with `UIHostingController` wherever it was instantiated.
|
|
25
|
-
4. Wire navigation from the parent UIKit code into the hosting controller.
|
|
26
|
-
|
|
27
|
-
### Implementation
|
|
28
|
-
|
|
29
|
-
```swift
|
|
30
|
-
// BEFORE: UIKit screen pushed onto a navigation stack
|
|
31
|
-
let detailVC = ItemDetailViewController(item: item)
|
|
32
|
-
navigationController?.pushViewController(detailVC, animated: true)
|
|
33
|
-
|
|
34
|
-
// AFTER: SwiftUI screen wrapped in UIHostingController
|
|
35
|
-
let detailView = ItemDetailView(item: item)
|
|
36
|
-
let hostingVC = UIHostingController(rootView: detailView)
|
|
37
|
-
navigationController?.pushViewController(hostingVC, animated: true)
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Passing Dismiss/Navigation Callbacks
|
|
41
|
-
|
|
42
|
-
When the SwiftUI screen needs to pop itself or trigger navigation in the UIKit stack:
|
|
43
|
-
|
|
44
|
-
```swift
|
|
45
|
-
struct ItemDetailView: View {
|
|
46
|
-
let item: Item
|
|
47
|
-
var onDelete: (() -> Void)?
|
|
48
|
-
|
|
49
|
-
@Environment(\.dismiss) private var dismiss
|
|
50
|
-
|
|
51
|
-
var body: some View {
|
|
52
|
-
VStack {
|
|
53
|
-
Text(item.title)
|
|
54
|
-
Button("Delete", role: .destructive) {
|
|
55
|
-
onDelete?()
|
|
56
|
-
dismiss()
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// In UIKit:
|
|
63
|
-
let detailView = ItemDetailView(item: item) {
|
|
64
|
-
self.dataSource.delete(item)
|
|
65
|
-
self.navigationController?.popViewController(animated: true)
|
|
66
|
-
}
|
|
67
|
-
let hostingVC = UIHostingController(rootView: detailView)
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Gotchas
|
|
71
|
-
|
|
72
|
-
- **Navigation bar.** `UIHostingController` inherits navigation bar visibility from its parent `UINavigationController`. Use `.navigationTitle()` and `.toolbar()` in the SwiftUI view -- they propagate to the UIKit navigation bar automatically.
|
|
73
|
-
- **Large titles.** Set `hostingVC.navigationItem.largeTitleDisplayMode` in UIKit code if the SwiftUI `.navigationBarTitleDisplayMode()` modifier does not apply correctly.
|
|
74
|
-
- **Tab bar insets.** `UIHostingController` respects `additionalSafeAreaInsets`. If the content overlaps the tab bar, verify safe area propagation.
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## 2. UIHostingController as Child
|
|
79
|
-
|
|
80
|
-
Embed SwiftUI sections within an existing UIKit screen. Use when migrating part of a screen (a header, a card, a section) before rewriting the entire controller.
|
|
81
|
-
|
|
82
|
-
### Implementation
|
|
83
|
-
|
|
84
|
-
```swift
|
|
85
|
-
final class DashboardViewController: UIViewController {
|
|
86
|
-
private var statsHostingController: UIHostingController<StatsCardView>?
|
|
87
|
-
|
|
88
|
-
override func viewDidLoad() {
|
|
89
|
-
super.viewDidLoad()
|
|
90
|
-
|
|
91
|
-
let statsView = StatsCardView(stats: currentStats)
|
|
92
|
-
let hostingVC = UIHostingController(rootView: statsView)
|
|
93
|
-
|
|
94
|
-
// Enable intrinsic sizing so Auto Layout can size the hosted view
|
|
95
|
-
if #available(iOS 16.0, *) {
|
|
96
|
-
hostingVC.sizingOptions = [.intrinsicContentSize]
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
addChild(hostingVC)
|
|
100
|
-
hostingVC.view.translatesAutoresizingMaskIntoConstraints = false
|
|
101
|
-
containerView.addSubview(hostingVC.view)
|
|
102
|
-
|
|
103
|
-
NSLayoutConstraint.activate([
|
|
104
|
-
hostingVC.view.topAnchor.constraint(equalTo: containerView.topAnchor),
|
|
105
|
-
hostingVC.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
|
106
|
-
hostingVC.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
|
107
|
-
hostingVC.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
|
108
|
-
])
|
|
109
|
-
|
|
110
|
-
hostingVC.didMove(toParent: self)
|
|
111
|
-
statsHostingController = hostingVC
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
func updateStats(_ stats: Stats) {
|
|
115
|
-
statsHostingController?.rootView = StatsCardView(stats: stats)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### With @Observable Model
|
|
121
|
-
|
|
122
|
-
Pass an `@Observable` model to avoid reassigning `rootView` manually. SwiftUI tracks changes automatically:
|
|
123
|
-
|
|
124
|
-
```swift
|
|
125
|
-
@Observable
|
|
126
|
-
final class DashboardModel {
|
|
127
|
-
var stats: Stats = .empty
|
|
128
|
-
var isLoading = false
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
struct StatsCardView: View {
|
|
132
|
-
let model: DashboardModel
|
|
133
|
-
|
|
134
|
-
var body: some View {
|
|
135
|
-
// Automatically re-renders when model.stats changes
|
|
136
|
-
if model.isLoading {
|
|
137
|
-
ProgressView()
|
|
138
|
-
} else {
|
|
139
|
-
StatsGrid(stats: model.stats)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// In UIKit:
|
|
145
|
-
let model = DashboardModel()
|
|
146
|
-
let hostingVC = UIHostingController(rootView: StatsCardView(model: model))
|
|
147
|
-
|
|
148
|
-
// Later -- just mutate the model, no rootView reassignment needed
|
|
149
|
-
model.stats = newStats
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Gotchas
|
|
153
|
-
|
|
154
|
-
- **Background color.** `UIHostingController`'s view has an opaque system background by default. Set `hostingVC.view.backgroundColor = .clear` if embedding over existing content.
|
|
155
|
-
- **sizingOptions on iOS 16+.** Without `.intrinsicContentSize`, the hosted view may report zero size in Auto Layout, causing the container to collapse.
|
|
156
|
-
- **Memory.** Store the hosting controller in a property. If it is only held as a child, removing it from the parent deallocates it and the SwiftUI view disappears.
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## 3. Navigation Bridging
|
|
161
|
-
|
|
162
|
-
Mix UIKit and SwiftUI screens in the same `UINavigationController` stack.
|
|
163
|
-
|
|
164
|
-
### UIKit Pushing SwiftUI
|
|
165
|
-
|
|
166
|
-
```swift
|
|
167
|
-
// From a UIKit view controller, push a SwiftUI screen
|
|
168
|
-
func showProfile(for user: User) {
|
|
169
|
-
let profileView = ProfileView(user: user)
|
|
170
|
-
let hostingVC = UIHostingController(rootView: profileView)
|
|
171
|
-
hostingVC.title = user.name
|
|
172
|
-
navigationController?.pushViewController(hostingVC, animated: true)
|
|
173
|
-
}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### SwiftUI Pushing UIKit
|
|
177
|
-
|
|
178
|
-
Use a coordinator or `UIViewControllerRepresentable` bridge:
|
|
179
|
-
|
|
180
|
-
```swift
|
|
181
|
-
struct ProfileView: View {
|
|
182
|
-
let user: User
|
|
183
|
-
@State private var showLegacyEditor = false
|
|
184
|
-
|
|
185
|
-
var body: some View {
|
|
186
|
-
List {
|
|
187
|
-
// ... profile content
|
|
188
|
-
Button("Edit (Legacy)") { showLegacyEditor = true }
|
|
189
|
-
}
|
|
190
|
-
.sheet(isPresented: $showLegacyEditor) {
|
|
191
|
-
LegacyEditorWrapper(user: user)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
struct LegacyEditorWrapper: UIViewControllerRepresentable {
|
|
197
|
-
let user: User
|
|
198
|
-
|
|
199
|
-
func makeUIViewController(context: Context) -> UINavigationController {
|
|
200
|
-
let editor = ProfileEditorViewController(user: user)
|
|
201
|
-
return UINavigationController(rootViewController: editor)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
### Passing NavigationController Reference
|
|
209
|
-
|
|
210
|
-
For deep integration where SwiftUI needs to push onto the UIKit navigation stack:
|
|
211
|
-
|
|
212
|
-
```swift
|
|
213
|
-
struct NavigationBridge {
|
|
214
|
-
weak var navigationController: UINavigationController?
|
|
215
|
-
|
|
216
|
-
func push(_ viewController: UIViewController, animated: Bool = true) {
|
|
217
|
-
navigationController?.pushViewController(viewController, animated: animated)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
func push<V: View>(_ view: V, title: String? = nil, animated: Bool = true) {
|
|
221
|
-
let hostingVC = UIHostingController(rootView: view)
|
|
222
|
-
hostingVC.title = title
|
|
223
|
-
navigationController?.pushViewController(hostingVC, animated: animated)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Inject via environment
|
|
228
|
-
private struct NavigationBridgeKey: EnvironmentKey {
|
|
229
|
-
static let defaultValue = NavigationBridge()
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
extension EnvironmentValues {
|
|
233
|
-
var navigationBridge: NavigationBridge {
|
|
234
|
-
get { self[NavigationBridgeKey.self] }
|
|
235
|
-
set { self[NavigationBridgeKey.self] = newValue }
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Gotchas
|
|
241
|
-
|
|
242
|
-
- **Back button.** When pushing `UIHostingController` onto a `UINavigationController`, the back button works automatically. Do not add a manual back button in the SwiftUI view.
|
|
243
|
-
- **Double navigation bars.** If the SwiftUI view uses `NavigationStack`, it creates its own navigation bar inside the UIKit one. Remove `NavigationStack` from SwiftUI views presented inside `UINavigationController`.
|
|
244
|
-
- **Toolbar items.** SwiftUI `.toolbar` items propagate to the UIKit navigation bar when hosted in `UIHostingController`. This works reliably on iOS 16+.
|
|
245
|
-
|
|
246
|
-
---
|
|
247
|
-
|
|
248
|
-
## 4. Data Sharing Between UIKit and SwiftUI
|
|
249
|
-
|
|
250
|
-
### Using @Observable (iOS 17+)
|
|
251
|
-
|
|
252
|
-
The cleanest approach. Create an `@Observable` model, pass it to both UIKit and SwiftUI code:
|
|
253
|
-
|
|
254
|
-
```swift
|
|
255
|
-
@Observable
|
|
256
|
-
final class AppState {
|
|
257
|
-
var currentUser: User?
|
|
258
|
-
var unreadCount: Int = 0
|
|
259
|
-
var theme: AppTheme = .system
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// UIKit side -- read properties directly
|
|
263
|
-
let state = AppState()
|
|
264
|
-
func viewDidLoad() {
|
|
265
|
-
titleLabel.text = state.currentUser?.name
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// SwiftUI side -- observation is automatic
|
|
269
|
-
struct HeaderView: View {
|
|
270
|
-
let state: AppState
|
|
271
|
-
|
|
272
|
-
var body: some View {
|
|
273
|
-
HStack {
|
|
274
|
-
Text(state.currentUser?.name ?? "Guest")
|
|
275
|
-
if state.unreadCount > 0 {
|
|
276
|
-
Badge(count: state.unreadCount)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
### Reactive Updates in UIKit with Combine
|
|
284
|
-
|
|
285
|
-
If UIKit code needs to react to `@Observable` changes, bridge with a `withObservationTracking` loop or use `Combine`:
|
|
286
|
-
|
|
287
|
-
```swift
|
|
288
|
-
import Combine
|
|
289
|
-
import Observation
|
|
290
|
-
|
|
291
|
-
final class DashboardViewController: UIViewController {
|
|
292
|
-
let state: AppState
|
|
293
|
-
private var observationTask: Task<Void, Never>?
|
|
294
|
-
|
|
295
|
-
override func viewDidLoad() {
|
|
296
|
-
super.viewDidLoad()
|
|
297
|
-
startObserving()
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
private func startObserving() {
|
|
301
|
-
observationTask = Task { @MainActor [weak self] in
|
|
302
|
-
while !Task.isCancelled {
|
|
303
|
-
guard let self else { return }
|
|
304
|
-
withObservationTracking {
|
|
305
|
-
self.updateUI(unreadCount: self.state.unreadCount)
|
|
306
|
-
} onChange: {
|
|
307
|
-
// Triggers next iteration
|
|
308
|
-
}
|
|
309
|
-
try? await Task.sleep(for: .zero) // Yield to allow onChange to fire
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
private func updateUI(unreadCount: Int) {
|
|
315
|
-
badgeLabel.text = "\(unreadCount)"
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
deinit { observationTask?.cancel() }
|
|
319
|
-
}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
### Legacy: ObservableObject with Combine
|
|
323
|
-
|
|
324
|
-
For iOS 15-16 or existing `ObservableObject` models, subscribe to `objectWillChange`:
|
|
325
|
-
|
|
326
|
-
```swift
|
|
327
|
-
final class SettingsViewController: UIViewController {
|
|
328
|
-
let settings: SettingsModel // ObservableObject
|
|
329
|
-
private var cancellable: AnyCancellable?
|
|
330
|
-
|
|
331
|
-
override func viewDidLoad() {
|
|
332
|
-
super.viewDidLoad()
|
|
333
|
-
cancellable = settings.objectWillChange
|
|
334
|
-
.receive(on: RunLoop.main)
|
|
335
|
-
.sink { [weak self] _ in
|
|
336
|
-
self?.updateUI()
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
### Gotchas
|
|
343
|
-
|
|
344
|
-
- **`@Observable` does not trigger UIKit updates automatically.** Unlike SwiftUI views, UIKit code must manually observe changes via `withObservationTracking` or `Combine`.
|
|
345
|
-
- **Thread safety.** Mutate `@Observable` properties on `@MainActor` when they drive UI in both UIKit and SwiftUI.
|
|
346
|
-
- **Retain cycles.** Use `[weak self]` in Combine sinks and task closures. Store cancellables and tasks, then cancel in `deinit`.
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
## 5. UIHostingConfiguration (iOS 16+)
|
|
351
|
-
|
|
352
|
-
Render SwiftUI content inside `UICollectionViewCell` and `UITableViewCell` without managing a child `UIHostingController`. This is the preferred approach for cells in a UIKit collection or table view.
|
|
353
|
-
|
|
354
|
-
### UICollectionView with SwiftUI Cells
|
|
355
|
-
|
|
356
|
-
```swift
|
|
357
|
-
@available(iOS 16.0, *)
|
|
358
|
-
func collectionView(
|
|
359
|
-
_ collectionView: UICollectionView,
|
|
360
|
-
cellForItemAt indexPath: IndexPath
|
|
361
|
-
) -> UICollectionViewCell {
|
|
362
|
-
let cell = collectionView.dequeueReusableCell(
|
|
363
|
-
withReuseIdentifier: "cell",
|
|
364
|
-
for: indexPath
|
|
365
|
-
)
|
|
366
|
-
let item = dataSource[indexPath.item]
|
|
367
|
-
|
|
368
|
-
cell.contentConfiguration = UIHostingConfiguration {
|
|
369
|
-
HStack {
|
|
370
|
-
AsyncImage(url: item.imageURL) { image in
|
|
371
|
-
image.resizable().scaledToFill()
|
|
372
|
-
} placeholder: {
|
|
373
|
-
ProgressView()
|
|
374
|
-
}
|
|
375
|
-
.frame(width: 60, height: 60)
|
|
376
|
-
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
377
|
-
|
|
378
|
-
VStack(alignment: .leading) {
|
|
379
|
-
Text(item.title).font(.headline)
|
|
380
|
-
Text(item.subtitle).font(.subheadline).foregroundStyle(.secondary)
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
.margins(.all, 12)
|
|
385
|
-
|
|
386
|
-
return cell
|
|
387
|
-
}
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
### UITableView with SwiftUI Cells
|
|
391
|
-
|
|
392
|
-
```swift
|
|
393
|
-
@available(iOS 16.0, *)
|
|
394
|
-
func tableView(
|
|
395
|
-
_ tableView: UITableView,
|
|
396
|
-
cellForRowAt indexPath: IndexPath
|
|
397
|
-
) -> UITableViewCell {
|
|
398
|
-
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
|
|
399
|
-
let item = items[indexPath.row]
|
|
400
|
-
|
|
401
|
-
cell.contentConfiguration = UIHostingConfiguration {
|
|
402
|
-
ItemRowView(item: item)
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return cell
|
|
406
|
-
}
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### Self-Sizing
|
|
410
|
-
|
|
411
|
-
`UIHostingConfiguration` cells self-size automatically. Ensure:
|
|
412
|
-
- The table/collection view uses `UICollectionViewCompositionalLayout` with estimated dimensions, or `tableView.rowHeight = UITableView.automaticDimension`.
|
|
413
|
-
- The SwiftUI content has defined height (via content or explicit `.frame`).
|
|
414
|
-
|
|
415
|
-
### Background Customization
|
|
416
|
-
|
|
417
|
-
```swift
|
|
418
|
-
cell.contentConfiguration = UIHostingConfiguration {
|
|
419
|
-
ItemRowView(item: item)
|
|
420
|
-
}
|
|
421
|
-
.background {
|
|
422
|
-
RoundedRectangle(cornerRadius: 12)
|
|
423
|
-
.fill(.background)
|
|
424
|
-
}
|
|
425
|
-
.margins(.horizontal, 16)
|
|
426
|
-
.minSize(height: 60)
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
### Gotchas
|
|
430
|
-
|
|
431
|
-
- **Performance.** Each `UIHostingConfiguration` creates a lightweight hosting controller. For very large lists (10,000+ items), profile with Instruments to ensure smooth scrolling.
|
|
432
|
-
- **State management.** The SwiftUI content inside `UIHostingConfiguration` is recreated on each cell reuse. Do not store `@State` that needs to persist across reuse -- use the data model instead.
|
|
433
|
-
- **Swipe actions.** Configure swipe actions in UIKit (`leadingSwipeActionsConfigurationForRowAt`), not inside the SwiftUI content.
|
|
434
|
-
- **No `@Environment` propagation by default.** Environment values from the UIKit context are not automatically available. Inject them explicitly in the `UIHostingConfiguration` closure.
|
|
435
|
-
|
|
436
|
-
---
|
|
437
|
-
|
|
438
|
-
## 6. Environment Bridging
|
|
439
|
-
|
|
440
|
-
Pass SwiftUI environment values into hosted SwiftUI views from UIKit, and access UIKit traits from SwiftUI.
|
|
441
|
-
|
|
442
|
-
### Injecting Environment into UIHostingController
|
|
443
|
-
|
|
444
|
-
```swift
|
|
445
|
-
let model = AppState()
|
|
446
|
-
let settingsView = SettingsView()
|
|
447
|
-
.environment(model)
|
|
448
|
-
.environment(\.locale, Locale(identifier: "en_US"))
|
|
449
|
-
|
|
450
|
-
let hostingVC = UIHostingController(rootView: settingsView)
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
Apply environment modifiers to the root view before passing it to the hosting controller. The hosting controller does not support adding environment values after creation (you would need to reassign `rootView`).
|
|
454
|
-
|
|
455
|
-
### Trait Collection to SwiftUI Environment
|
|
456
|
-
|
|
457
|
-
`UIHostingController` automatically bridges these UIKit trait collections to SwiftUI environment values:
|
|
458
|
-
|
|
459
|
-
| UIKit Trait | SwiftUI Environment |
|
|
460
|
-
|------------|-------------------|
|
|
461
|
-
| `userInterfaceStyle` | `\.colorScheme` |
|
|
462
|
-
| `horizontalSizeClass` | `\.horizontalSizeClass` |
|
|
463
|
-
| `verticalSizeClass` | `\.verticalSizeClass` |
|
|
464
|
-
| `preferredContentSizeCategory` | `\.dynamicTypeSize` |
|
|
465
|
-
| `layoutDirection` | `\.layoutDirection` |
|
|
466
|
-
| `legibilityWeight` | `\.legibilityWeight` |
|
|
467
|
-
|
|
468
|
-
These update automatically when the UIKit trait environment changes (device rotation, split view resize, accessibility settings change).
|
|
469
|
-
|
|
470
|
-
### Custom Environment Values Across the Bridge
|
|
471
|
-
|
|
472
|
-
Define a custom environment key and set it from UIKit:
|
|
473
|
-
|
|
474
|
-
```swift
|
|
475
|
-
private struct UserRoleKey: EnvironmentKey {
|
|
476
|
-
static let defaultValue: UserRole = .guest
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
extension EnvironmentValues {
|
|
480
|
-
var userRole: UserRole {
|
|
481
|
-
get { self[UserRoleKey.self] }
|
|
482
|
-
set { self[UserRoleKey.self] = newValue }
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// UIKit side:
|
|
487
|
-
let role = authManager.currentRole
|
|
488
|
-
let profileView = ProfileView().environment(\.userRole, role)
|
|
489
|
-
let hostingVC = UIHostingController(rootView: profileView)
|
|
490
|
-
|
|
491
|
-
// SwiftUI side:
|
|
492
|
-
struct ProfileView: View {
|
|
493
|
-
@Environment(\.userRole) private var role
|
|
494
|
-
|
|
495
|
-
var body: some View {
|
|
496
|
-
if role == .admin {
|
|
497
|
-
AdminDashboard()
|
|
498
|
-
} else {
|
|
499
|
-
UserDashboard()
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
### Updating Environment After Creation
|
|
506
|
-
|
|
507
|
-
To change environment values after the hosting controller is created, wrap the root view in a container that takes a binding or observable:
|
|
508
|
-
|
|
509
|
-
```swift
|
|
510
|
-
struct EnvironmentBridge<Content: View>: View {
|
|
511
|
-
let state: AppState // @Observable
|
|
512
|
-
let content: Content
|
|
513
|
-
|
|
514
|
-
var body: some View {
|
|
515
|
-
content
|
|
516
|
-
.environment(state)
|
|
517
|
-
.environment(\.userRole, state.currentRole)
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// UIKit:
|
|
522
|
-
let state = AppState()
|
|
523
|
-
let bridge = EnvironmentBridge(state: state, content: SettingsView())
|
|
524
|
-
let hostingVC = UIHostingController(rootView: bridge)
|
|
525
|
-
|
|
526
|
-
// Later: mutating state.currentRole updates the environment automatically
|
|
527
|
-
state.currentRole = .admin
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
### Gotchas
|
|
531
|
-
|
|
532
|
-
- **`@Environment(\.dismiss)` in hosted views.** This works when the `UIHostingController` is presented modally (via `present(_:animated:)`). It does NOT work when the hosting controller is pushed onto a `UINavigationController` -- use the navigation controller's `popViewController` instead.
|
|
533
|
-
- **Missing environment.** If a SwiftUI view expects an `@Environment` object and it is not provided, the app crashes at runtime. Always set required environment values before creating the hosting controller.
|
|
534
|
-
- **Overriding traits.** Use `hostingVC.overrideUserInterfaceStyle` to force light/dark mode for a hosted SwiftUI view. This propagates to `\.colorScheme` automatically.
|
|
1
|
+
# UIKit-to-SwiftUI Migration Patterns
|
|
2
|
+
|
|
3
|
+
Patterns for incrementally migrating a UIKit app to SwiftUI. Each pattern is self-contained with rationale, implementation, and gotchas.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- [1. Screen-by-Screen Migration](#1-screen-by-screen-migration)
|
|
10
|
+
- [2. UIHostingController as Child](#2-uihostingcontroller-as-child)
|
|
11
|
+
- [3. Navigation Bridging](#3-navigation-bridging)
|
|
12
|
+
- [4. Data Sharing Between UIKit and SwiftUI](#4-data-sharing-between-uikit-and-swiftui)
|
|
13
|
+
- [5. UIHostingConfiguration (iOS 16+)](#5-uihostingconfiguration-ios-16)
|
|
14
|
+
- [6. Environment Bridging](#6-environment-bridging)
|
|
15
|
+
|
|
16
|
+
## 1. Screen-by-Screen Migration
|
|
17
|
+
|
|
18
|
+
Replace one `UIViewController` at a time with a `UIHostingController` wrapping a SwiftUI view. This is the safest migration path -- each screen is an isolated unit.
|
|
19
|
+
|
|
20
|
+
### Strategy
|
|
21
|
+
|
|
22
|
+
1. Pick a leaf screen (one that does not contain child view controllers).
|
|
23
|
+
2. Rewrite its UI in SwiftUI.
|
|
24
|
+
3. Replace the UIKit view controller with `UIHostingController` wherever it was instantiated.
|
|
25
|
+
4. Wire navigation from the parent UIKit code into the hosting controller.
|
|
26
|
+
|
|
27
|
+
### Implementation
|
|
28
|
+
|
|
29
|
+
```swift
|
|
30
|
+
// BEFORE: UIKit screen pushed onto a navigation stack
|
|
31
|
+
let detailVC = ItemDetailViewController(item: item)
|
|
32
|
+
navigationController?.pushViewController(detailVC, animated: true)
|
|
33
|
+
|
|
34
|
+
// AFTER: SwiftUI screen wrapped in UIHostingController
|
|
35
|
+
let detailView = ItemDetailView(item: item)
|
|
36
|
+
let hostingVC = UIHostingController(rootView: detailView)
|
|
37
|
+
navigationController?.pushViewController(hostingVC, animated: true)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Passing Dismiss/Navigation Callbacks
|
|
41
|
+
|
|
42
|
+
When the SwiftUI screen needs to pop itself or trigger navigation in the UIKit stack:
|
|
43
|
+
|
|
44
|
+
```swift
|
|
45
|
+
struct ItemDetailView: View {
|
|
46
|
+
let item: Item
|
|
47
|
+
var onDelete: (() -> Void)?
|
|
48
|
+
|
|
49
|
+
@Environment(\.dismiss) private var dismiss
|
|
50
|
+
|
|
51
|
+
var body: some View {
|
|
52
|
+
VStack {
|
|
53
|
+
Text(item.title)
|
|
54
|
+
Button("Delete", role: .destructive) {
|
|
55
|
+
onDelete?()
|
|
56
|
+
dismiss()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// In UIKit:
|
|
63
|
+
let detailView = ItemDetailView(item: item) {
|
|
64
|
+
self.dataSource.delete(item)
|
|
65
|
+
self.navigationController?.popViewController(animated: true)
|
|
66
|
+
}
|
|
67
|
+
let hostingVC = UIHostingController(rootView: detailView)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Gotchas
|
|
71
|
+
|
|
72
|
+
- **Navigation bar.** `UIHostingController` inherits navigation bar visibility from its parent `UINavigationController`. Use `.navigationTitle()` and `.toolbar()` in the SwiftUI view -- they propagate to the UIKit navigation bar automatically.
|
|
73
|
+
- **Large titles.** Set `hostingVC.navigationItem.largeTitleDisplayMode` in UIKit code if the SwiftUI `.navigationBarTitleDisplayMode()` modifier does not apply correctly.
|
|
74
|
+
- **Tab bar insets.** `UIHostingController` respects `additionalSafeAreaInsets`. If the content overlaps the tab bar, verify safe area propagation.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 2. UIHostingController as Child
|
|
79
|
+
|
|
80
|
+
Embed SwiftUI sections within an existing UIKit screen. Use when migrating part of a screen (a header, a card, a section) before rewriting the entire controller.
|
|
81
|
+
|
|
82
|
+
### Implementation
|
|
83
|
+
|
|
84
|
+
```swift
|
|
85
|
+
final class DashboardViewController: UIViewController {
|
|
86
|
+
private var statsHostingController: UIHostingController<StatsCardView>?
|
|
87
|
+
|
|
88
|
+
override func viewDidLoad() {
|
|
89
|
+
super.viewDidLoad()
|
|
90
|
+
|
|
91
|
+
let statsView = StatsCardView(stats: currentStats)
|
|
92
|
+
let hostingVC = UIHostingController(rootView: statsView)
|
|
93
|
+
|
|
94
|
+
// Enable intrinsic sizing so Auto Layout can size the hosted view
|
|
95
|
+
if #available(iOS 16.0, *) {
|
|
96
|
+
hostingVC.sizingOptions = [.intrinsicContentSize]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
addChild(hostingVC)
|
|
100
|
+
hostingVC.view.translatesAutoresizingMaskIntoConstraints = false
|
|
101
|
+
containerView.addSubview(hostingVC.view)
|
|
102
|
+
|
|
103
|
+
NSLayoutConstraint.activate([
|
|
104
|
+
hostingVC.view.topAnchor.constraint(equalTo: containerView.topAnchor),
|
|
105
|
+
hostingVC.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
|
106
|
+
hostingVC.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
|
107
|
+
hostingVC.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
|
108
|
+
])
|
|
109
|
+
|
|
110
|
+
hostingVC.didMove(toParent: self)
|
|
111
|
+
statsHostingController = hostingVC
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
func updateStats(_ stats: Stats) {
|
|
115
|
+
statsHostingController?.rootView = StatsCardView(stats: stats)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### With @Observable Model
|
|
121
|
+
|
|
122
|
+
Pass an `@Observable` model to avoid reassigning `rootView` manually. SwiftUI tracks changes automatically:
|
|
123
|
+
|
|
124
|
+
```swift
|
|
125
|
+
@Observable
|
|
126
|
+
final class DashboardModel {
|
|
127
|
+
var stats: Stats = .empty
|
|
128
|
+
var isLoading = false
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
struct StatsCardView: View {
|
|
132
|
+
let model: DashboardModel
|
|
133
|
+
|
|
134
|
+
var body: some View {
|
|
135
|
+
// Automatically re-renders when model.stats changes
|
|
136
|
+
if model.isLoading {
|
|
137
|
+
ProgressView()
|
|
138
|
+
} else {
|
|
139
|
+
StatsGrid(stats: model.stats)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// In UIKit:
|
|
145
|
+
let model = DashboardModel()
|
|
146
|
+
let hostingVC = UIHostingController(rootView: StatsCardView(model: model))
|
|
147
|
+
|
|
148
|
+
// Later -- just mutate the model, no rootView reassignment needed
|
|
149
|
+
model.stats = newStats
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Gotchas
|
|
153
|
+
|
|
154
|
+
- **Background color.** `UIHostingController`'s view has an opaque system background by default. Set `hostingVC.view.backgroundColor = .clear` if embedding over existing content.
|
|
155
|
+
- **sizingOptions on iOS 16+.** Without `.intrinsicContentSize`, the hosted view may report zero size in Auto Layout, causing the container to collapse.
|
|
156
|
+
- **Memory.** Store the hosting controller in a property. If it is only held as a child, removing it from the parent deallocates it and the SwiftUI view disappears.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 3. Navigation Bridging
|
|
161
|
+
|
|
162
|
+
Mix UIKit and SwiftUI screens in the same `UINavigationController` stack.
|
|
163
|
+
|
|
164
|
+
### UIKit Pushing SwiftUI
|
|
165
|
+
|
|
166
|
+
```swift
|
|
167
|
+
// From a UIKit view controller, push a SwiftUI screen
|
|
168
|
+
func showProfile(for user: User) {
|
|
169
|
+
let profileView = ProfileView(user: user)
|
|
170
|
+
let hostingVC = UIHostingController(rootView: profileView)
|
|
171
|
+
hostingVC.title = user.name
|
|
172
|
+
navigationController?.pushViewController(hostingVC, animated: true)
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### SwiftUI Pushing UIKit
|
|
177
|
+
|
|
178
|
+
Use a coordinator or `UIViewControllerRepresentable` bridge:
|
|
179
|
+
|
|
180
|
+
```swift
|
|
181
|
+
struct ProfileView: View {
|
|
182
|
+
let user: User
|
|
183
|
+
@State private var showLegacyEditor = false
|
|
184
|
+
|
|
185
|
+
var body: some View {
|
|
186
|
+
List {
|
|
187
|
+
// ... profile content
|
|
188
|
+
Button("Edit (Legacy)") { showLegacyEditor = true }
|
|
189
|
+
}
|
|
190
|
+
.sheet(isPresented: $showLegacyEditor) {
|
|
191
|
+
LegacyEditorWrapper(user: user)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
struct LegacyEditorWrapper: UIViewControllerRepresentable {
|
|
197
|
+
let user: User
|
|
198
|
+
|
|
199
|
+
func makeUIViewController(context: Context) -> UINavigationController {
|
|
200
|
+
let editor = ProfileEditorViewController(user: user)
|
|
201
|
+
return UINavigationController(rootViewController: editor)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Passing NavigationController Reference
|
|
209
|
+
|
|
210
|
+
For deep integration where SwiftUI needs to push onto the UIKit navigation stack:
|
|
211
|
+
|
|
212
|
+
```swift
|
|
213
|
+
struct NavigationBridge {
|
|
214
|
+
weak var navigationController: UINavigationController?
|
|
215
|
+
|
|
216
|
+
func push(_ viewController: UIViewController, animated: Bool = true) {
|
|
217
|
+
navigationController?.pushViewController(viewController, animated: animated)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
func push<V: View>(_ view: V, title: String? = nil, animated: Bool = true) {
|
|
221
|
+
let hostingVC = UIHostingController(rootView: view)
|
|
222
|
+
hostingVC.title = title
|
|
223
|
+
navigationController?.pushViewController(hostingVC, animated: animated)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Inject via environment
|
|
228
|
+
private struct NavigationBridgeKey: EnvironmentKey {
|
|
229
|
+
static let defaultValue = NavigationBridge()
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
extension EnvironmentValues {
|
|
233
|
+
var navigationBridge: NavigationBridge {
|
|
234
|
+
get { self[NavigationBridgeKey.self] }
|
|
235
|
+
set { self[NavigationBridgeKey.self] = newValue }
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Gotchas
|
|
241
|
+
|
|
242
|
+
- **Back button.** When pushing `UIHostingController` onto a `UINavigationController`, the back button works automatically. Do not add a manual back button in the SwiftUI view.
|
|
243
|
+
- **Double navigation bars.** If the SwiftUI view uses `NavigationStack`, it creates its own navigation bar inside the UIKit one. Remove `NavigationStack` from SwiftUI views presented inside `UINavigationController`.
|
|
244
|
+
- **Toolbar items.** SwiftUI `.toolbar` items propagate to the UIKit navigation bar when hosted in `UIHostingController`. This works reliably on iOS 16+.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 4. Data Sharing Between UIKit and SwiftUI
|
|
249
|
+
|
|
250
|
+
### Using @Observable (iOS 17+)
|
|
251
|
+
|
|
252
|
+
The cleanest approach. Create an `@Observable` model, pass it to both UIKit and SwiftUI code:
|
|
253
|
+
|
|
254
|
+
```swift
|
|
255
|
+
@Observable
|
|
256
|
+
final class AppState {
|
|
257
|
+
var currentUser: User?
|
|
258
|
+
var unreadCount: Int = 0
|
|
259
|
+
var theme: AppTheme = .system
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// UIKit side -- read properties directly
|
|
263
|
+
let state = AppState()
|
|
264
|
+
func viewDidLoad() {
|
|
265
|
+
titleLabel.text = state.currentUser?.name
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// SwiftUI side -- observation is automatic
|
|
269
|
+
struct HeaderView: View {
|
|
270
|
+
let state: AppState
|
|
271
|
+
|
|
272
|
+
var body: some View {
|
|
273
|
+
HStack {
|
|
274
|
+
Text(state.currentUser?.name ?? "Guest")
|
|
275
|
+
if state.unreadCount > 0 {
|
|
276
|
+
Badge(count: state.unreadCount)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Reactive Updates in UIKit with Combine
|
|
284
|
+
|
|
285
|
+
If UIKit code needs to react to `@Observable` changes, bridge with a `withObservationTracking` loop or use `Combine`:
|
|
286
|
+
|
|
287
|
+
```swift
|
|
288
|
+
import Combine
|
|
289
|
+
import Observation
|
|
290
|
+
|
|
291
|
+
final class DashboardViewController: UIViewController {
|
|
292
|
+
let state: AppState
|
|
293
|
+
private var observationTask: Task<Void, Never>?
|
|
294
|
+
|
|
295
|
+
override func viewDidLoad() {
|
|
296
|
+
super.viewDidLoad()
|
|
297
|
+
startObserving()
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private func startObserving() {
|
|
301
|
+
observationTask = Task { @MainActor [weak self] in
|
|
302
|
+
while !Task.isCancelled {
|
|
303
|
+
guard let self else { return }
|
|
304
|
+
withObservationTracking {
|
|
305
|
+
self.updateUI(unreadCount: self.state.unreadCount)
|
|
306
|
+
} onChange: {
|
|
307
|
+
// Triggers next iteration
|
|
308
|
+
}
|
|
309
|
+
try? await Task.sleep(for: .zero) // Yield to allow onChange to fire
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private func updateUI(unreadCount: Int) {
|
|
315
|
+
badgeLabel.text = "\(unreadCount)"
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
deinit { observationTask?.cancel() }
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Legacy: ObservableObject with Combine
|
|
323
|
+
|
|
324
|
+
For iOS 15-16 or existing `ObservableObject` models, subscribe to `objectWillChange`:
|
|
325
|
+
|
|
326
|
+
```swift
|
|
327
|
+
final class SettingsViewController: UIViewController {
|
|
328
|
+
let settings: SettingsModel // ObservableObject
|
|
329
|
+
private var cancellable: AnyCancellable?
|
|
330
|
+
|
|
331
|
+
override func viewDidLoad() {
|
|
332
|
+
super.viewDidLoad()
|
|
333
|
+
cancellable = settings.objectWillChange
|
|
334
|
+
.receive(on: RunLoop.main)
|
|
335
|
+
.sink { [weak self] _ in
|
|
336
|
+
self?.updateUI()
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Gotchas
|
|
343
|
+
|
|
344
|
+
- **`@Observable` does not trigger UIKit updates automatically.** Unlike SwiftUI views, UIKit code must manually observe changes via `withObservationTracking` or `Combine`.
|
|
345
|
+
- **Thread safety.** Mutate `@Observable` properties on `@MainActor` when they drive UI in both UIKit and SwiftUI.
|
|
346
|
+
- **Retain cycles.** Use `[weak self]` in Combine sinks and task closures. Store cancellables and tasks, then cancel in `deinit`.
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## 5. UIHostingConfiguration (iOS 16+)
|
|
351
|
+
|
|
352
|
+
Render SwiftUI content inside `UICollectionViewCell` and `UITableViewCell` without managing a child `UIHostingController`. This is the preferred approach for cells in a UIKit collection or table view.
|
|
353
|
+
|
|
354
|
+
### UICollectionView with SwiftUI Cells
|
|
355
|
+
|
|
356
|
+
```swift
|
|
357
|
+
@available(iOS 16.0, *)
|
|
358
|
+
func collectionView(
|
|
359
|
+
_ collectionView: UICollectionView,
|
|
360
|
+
cellForItemAt indexPath: IndexPath
|
|
361
|
+
) -> UICollectionViewCell {
|
|
362
|
+
let cell = collectionView.dequeueReusableCell(
|
|
363
|
+
withReuseIdentifier: "cell",
|
|
364
|
+
for: indexPath
|
|
365
|
+
)
|
|
366
|
+
let item = dataSource[indexPath.item]
|
|
367
|
+
|
|
368
|
+
cell.contentConfiguration = UIHostingConfiguration {
|
|
369
|
+
HStack {
|
|
370
|
+
AsyncImage(url: item.imageURL) { image in
|
|
371
|
+
image.resizable().scaledToFill()
|
|
372
|
+
} placeholder: {
|
|
373
|
+
ProgressView()
|
|
374
|
+
}
|
|
375
|
+
.frame(width: 60, height: 60)
|
|
376
|
+
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
377
|
+
|
|
378
|
+
VStack(alignment: .leading) {
|
|
379
|
+
Text(item.title).font(.headline)
|
|
380
|
+
Text(item.subtitle).font(.subheadline).foregroundStyle(.secondary)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
.margins(.all, 12)
|
|
385
|
+
|
|
386
|
+
return cell
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### UITableView with SwiftUI Cells
|
|
391
|
+
|
|
392
|
+
```swift
|
|
393
|
+
@available(iOS 16.0, *)
|
|
394
|
+
func tableView(
|
|
395
|
+
_ tableView: UITableView,
|
|
396
|
+
cellForRowAt indexPath: IndexPath
|
|
397
|
+
) -> UITableViewCell {
|
|
398
|
+
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
|
|
399
|
+
let item = items[indexPath.row]
|
|
400
|
+
|
|
401
|
+
cell.contentConfiguration = UIHostingConfiguration {
|
|
402
|
+
ItemRowView(item: item)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return cell
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Self-Sizing
|
|
410
|
+
|
|
411
|
+
`UIHostingConfiguration` cells self-size automatically. Ensure:
|
|
412
|
+
- The table/collection view uses `UICollectionViewCompositionalLayout` with estimated dimensions, or `tableView.rowHeight = UITableView.automaticDimension`.
|
|
413
|
+
- The SwiftUI content has defined height (via content or explicit `.frame`).
|
|
414
|
+
|
|
415
|
+
### Background Customization
|
|
416
|
+
|
|
417
|
+
```swift
|
|
418
|
+
cell.contentConfiguration = UIHostingConfiguration {
|
|
419
|
+
ItemRowView(item: item)
|
|
420
|
+
}
|
|
421
|
+
.background {
|
|
422
|
+
RoundedRectangle(cornerRadius: 12)
|
|
423
|
+
.fill(.background)
|
|
424
|
+
}
|
|
425
|
+
.margins(.horizontal, 16)
|
|
426
|
+
.minSize(height: 60)
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Gotchas
|
|
430
|
+
|
|
431
|
+
- **Performance.** Each `UIHostingConfiguration` creates a lightweight hosting controller. For very large lists (10,000+ items), profile with Instruments to ensure smooth scrolling.
|
|
432
|
+
- **State management.** The SwiftUI content inside `UIHostingConfiguration` is recreated on each cell reuse. Do not store `@State` that needs to persist across reuse -- use the data model instead.
|
|
433
|
+
- **Swipe actions.** Configure swipe actions in UIKit (`leadingSwipeActionsConfigurationForRowAt`), not inside the SwiftUI content.
|
|
434
|
+
- **No `@Environment` propagation by default.** Environment values from the UIKit context are not automatically available. Inject them explicitly in the `UIHostingConfiguration` closure.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## 6. Environment Bridging
|
|
439
|
+
|
|
440
|
+
Pass SwiftUI environment values into hosted SwiftUI views from UIKit, and access UIKit traits from SwiftUI.
|
|
441
|
+
|
|
442
|
+
### Injecting Environment into UIHostingController
|
|
443
|
+
|
|
444
|
+
```swift
|
|
445
|
+
let model = AppState()
|
|
446
|
+
let settingsView = SettingsView()
|
|
447
|
+
.environment(model)
|
|
448
|
+
.environment(\.locale, Locale(identifier: "en_US"))
|
|
449
|
+
|
|
450
|
+
let hostingVC = UIHostingController(rootView: settingsView)
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Apply environment modifiers to the root view before passing it to the hosting controller. The hosting controller does not support adding environment values after creation (you would need to reassign `rootView`).
|
|
454
|
+
|
|
455
|
+
### Trait Collection to SwiftUI Environment
|
|
456
|
+
|
|
457
|
+
`UIHostingController` automatically bridges these UIKit trait collections to SwiftUI environment values:
|
|
458
|
+
|
|
459
|
+
| UIKit Trait | SwiftUI Environment |
|
|
460
|
+
|------------|-------------------|
|
|
461
|
+
| `userInterfaceStyle` | `\.colorScheme` |
|
|
462
|
+
| `horizontalSizeClass` | `\.horizontalSizeClass` |
|
|
463
|
+
| `verticalSizeClass` | `\.verticalSizeClass` |
|
|
464
|
+
| `preferredContentSizeCategory` | `\.dynamicTypeSize` |
|
|
465
|
+
| `layoutDirection` | `\.layoutDirection` |
|
|
466
|
+
| `legibilityWeight` | `\.legibilityWeight` |
|
|
467
|
+
|
|
468
|
+
These update automatically when the UIKit trait environment changes (device rotation, split view resize, accessibility settings change).
|
|
469
|
+
|
|
470
|
+
### Custom Environment Values Across the Bridge
|
|
471
|
+
|
|
472
|
+
Define a custom environment key and set it from UIKit:
|
|
473
|
+
|
|
474
|
+
```swift
|
|
475
|
+
private struct UserRoleKey: EnvironmentKey {
|
|
476
|
+
static let defaultValue: UserRole = .guest
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
extension EnvironmentValues {
|
|
480
|
+
var userRole: UserRole {
|
|
481
|
+
get { self[UserRoleKey.self] }
|
|
482
|
+
set { self[UserRoleKey.self] = newValue }
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// UIKit side:
|
|
487
|
+
let role = authManager.currentRole
|
|
488
|
+
let profileView = ProfileView().environment(\.userRole, role)
|
|
489
|
+
let hostingVC = UIHostingController(rootView: profileView)
|
|
490
|
+
|
|
491
|
+
// SwiftUI side:
|
|
492
|
+
struct ProfileView: View {
|
|
493
|
+
@Environment(\.userRole) private var role
|
|
494
|
+
|
|
495
|
+
var body: some View {
|
|
496
|
+
if role == .admin {
|
|
497
|
+
AdminDashboard()
|
|
498
|
+
} else {
|
|
499
|
+
UserDashboard()
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Updating Environment After Creation
|
|
506
|
+
|
|
507
|
+
To change environment values after the hosting controller is created, wrap the root view in a container that takes a binding or observable:
|
|
508
|
+
|
|
509
|
+
```swift
|
|
510
|
+
struct EnvironmentBridge<Content: View>: View {
|
|
511
|
+
let state: AppState // @Observable
|
|
512
|
+
let content: Content
|
|
513
|
+
|
|
514
|
+
var body: some View {
|
|
515
|
+
content
|
|
516
|
+
.environment(state)
|
|
517
|
+
.environment(\.userRole, state.currentRole)
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// UIKit:
|
|
522
|
+
let state = AppState()
|
|
523
|
+
let bridge = EnvironmentBridge(state: state, content: SettingsView())
|
|
524
|
+
let hostingVC = UIHostingController(rootView: bridge)
|
|
525
|
+
|
|
526
|
+
// Later: mutating state.currentRole updates the environment automatically
|
|
527
|
+
state.currentRole = .admin
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Gotchas
|
|
531
|
+
|
|
532
|
+
- **`@Environment(\.dismiss)` in hosted views.** This works when the `UIHostingController` is presented modally (via `present(_:animated:)`). It does NOT work when the hosting controller is pushed onto a `UINavigationController` -- use the navigation controller's `popViewController` instead.
|
|
533
|
+
- **Missing environment.** If a SwiftUI view expects an `@Environment` object and it is not provided, the app crashes at runtime. Always set required environment values before creating the hosting controller.
|
|
534
|
+
- **Overriding traits.** Use `hostingVC.overrideUserInterfaceStyle` to force light/dark mode for a hosted SwiftUI view. This propagates to `\.colorScheme` automatically.
|