@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,765 +1,765 @@
|
|
|
1
|
-
# Core ML Swift Integration Reference
|
|
2
|
-
|
|
3
|
-
Complete implementation patterns for loading, configuring, and running Core ML
|
|
4
|
-
models in Swift. All patterns target iOS 26+ with Swift 6.2, backward-compatible
|
|
5
|
-
to iOS 14 unless noted.
|
|
6
|
-
|
|
7
|
-
## Contents
|
|
8
|
-
- Actor-Based Model Loading and Caching
|
|
9
|
-
- Auto-Generated Class Usage
|
|
10
|
-
- Manual MLFeatureProvider
|
|
11
|
-
- Async Prediction Patterns
|
|
12
|
-
- MLBatchProvider for Batch Inference
|
|
13
|
-
- Stateful Predictions with MLState (iOS 18+)
|
|
14
|
-
- Image Preprocessing
|
|
15
|
-
- MLMultiArray Creation and Manipulation
|
|
16
|
-
- MLTensor Advanced Operations
|
|
17
|
-
- Vision + Core ML Pipelines
|
|
18
|
-
- NaturalLanguage Integration
|
|
19
|
-
- MLComputePlan Detailed Usage (iOS 17.4+)
|
|
20
|
-
- Background Loading and Memory Management
|
|
21
|
-
- Error Handling Patterns
|
|
22
|
-
- Testing Patterns
|
|
23
|
-
|
|
24
|
-
## Actor-Based Model Loading and Caching
|
|
25
|
-
|
|
26
|
-
Use an actor to manage model lifecycle, prevent concurrent loading, and cache
|
|
27
|
-
compiled models persistently.
|
|
28
|
-
|
|
29
|
-
```swift
|
|
30
|
-
import CoreML
|
|
31
|
-
|
|
32
|
-
actor ModelManager {
|
|
33
|
-
private var loadedModels: [String: MLModel] = [:]
|
|
34
|
-
private let cacheDirectory: URL
|
|
35
|
-
|
|
36
|
-
init() {
|
|
37
|
-
let appSupport = FileManager.default.urls(
|
|
38
|
-
for: .applicationSupportDirectory, in: .userDomainMask
|
|
39
|
-
).first!
|
|
40
|
-
cacheDirectory = appSupport.appendingPathComponent("CompiledModels", isDirectory: true)
|
|
41
|
-
try? FileManager.default.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
func model(named name: String, configuration: MLModelConfiguration = .init()) async throws -> MLModel {
|
|
45
|
-
if let cached = loadedModels[name] {
|
|
46
|
-
return cached
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let compiledURL = try await compiledModelURL(for: name)
|
|
50
|
-
let model = try await MLModel.load(contentsOf: compiledURL, configuration: configuration)
|
|
51
|
-
loadedModels[name] = model
|
|
52
|
-
return model
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
func unloadModel(named name: String) {
|
|
56
|
-
loadedModels.removeValue(forKey: name)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
func unloadAll() {
|
|
60
|
-
loadedModels.removeAll()
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private func compiledModelURL(for name: String) async throws -> URL {
|
|
64
|
-
// Check for pre-compiled model in cache
|
|
65
|
-
let cachedURL = cacheDirectory.appendingPathComponent("\(name).mlmodelc")
|
|
66
|
-
if FileManager.default.fileExists(atPath: cachedURL.path) {
|
|
67
|
-
return cachedURL
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Check for pre-compiled model in bundle
|
|
71
|
-
if let bundledCompiledURL = Bundle.main.url(forResource: name, withExtension: "mlmodelc") {
|
|
72
|
-
return bundledCompiledURL
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Compile from .mlpackage and cache
|
|
76
|
-
guard let packageURL = Bundle.main.url(forResource: name, withExtension: "mlpackage") else {
|
|
77
|
-
throw ModelManagerError.modelNotFound(name)
|
|
78
|
-
}
|
|
79
|
-
let tempCompiledURL = try await MLModel.compileModel(at: packageURL)
|
|
80
|
-
|
|
81
|
-
// Move compiled model to persistent cache
|
|
82
|
-
if FileManager.default.fileExists(atPath: cachedURL.path) {
|
|
83
|
-
try FileManager.default.removeItem(at: cachedURL)
|
|
84
|
-
}
|
|
85
|
-
try FileManager.default.moveItem(at: tempCompiledURL, to: cachedURL)
|
|
86
|
-
return cachedURL
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
enum ModelManagerError: Error {
|
|
91
|
-
case modelNotFound(String)
|
|
92
|
-
case predictionFailed(String)
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Usage with SwiftUI
|
|
97
|
-
|
|
98
|
-
```swift
|
|
99
|
-
@MainActor
|
|
100
|
-
@Observable
|
|
101
|
-
final class ClassifierViewModel {
|
|
102
|
-
var classLabel: String = ""
|
|
103
|
-
var confidence: Double = 0
|
|
104
|
-
var isLoading = false
|
|
105
|
-
var errorMessage: String?
|
|
106
|
-
|
|
107
|
-
private let modelManager = ModelManager()
|
|
108
|
-
|
|
109
|
-
func classify(image: CGImage) async {
|
|
110
|
-
isLoading = true
|
|
111
|
-
defer { isLoading = false }
|
|
112
|
-
|
|
113
|
-
do {
|
|
114
|
-
let config = MLModelConfiguration()
|
|
115
|
-
config.computeUnits = .all
|
|
116
|
-
|
|
117
|
-
let model = try await modelManager.model(named: "ImageClassifier", configuration: config)
|
|
118
|
-
|
|
119
|
-
let pixelBuffer = try image.toPixelBuffer(width: 224, height: 224)
|
|
120
|
-
let input = try MLDictionaryFeatureProvider(dictionary: [
|
|
121
|
-
"image": MLFeatureValue(pixelBuffer: pixelBuffer),
|
|
122
|
-
])
|
|
123
|
-
let output = try await model.prediction(from: input)
|
|
124
|
-
|
|
125
|
-
classLabel = output.featureValue(for: "classLabel")?.stringValue ?? "Unknown"
|
|
126
|
-
confidence = output.featureValue(for: "classLabelProbs")?
|
|
127
|
-
.dictionaryValue[classLabel]?
|
|
128
|
-
.doubleValue ?? 0
|
|
129
|
-
} catch {
|
|
130
|
-
errorMessage = "Classification failed: \(error.localizedDescription)"
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Auto-Generated Class Usage
|
|
137
|
-
|
|
138
|
-
When you add a `.mlpackage` or `.mlmodelc` to your Xcode project, Xcode
|
|
139
|
-
generates a Swift class with typed inputs and outputs.
|
|
140
|
-
|
|
141
|
-
```swift
|
|
142
|
-
import CoreML
|
|
143
|
-
|
|
144
|
-
// Xcode generates: MyImageClassifier, MyImageClassifierInput, MyImageClassifierOutput
|
|
145
|
-
|
|
146
|
-
// Synchronous prediction with generated types
|
|
147
|
-
func classifyWithGeneratedClass(pixelBuffer: CVPixelBuffer) throws -> (label: String, confidence: Double) {
|
|
148
|
-
let config = MLModelConfiguration()
|
|
149
|
-
config.computeUnits = .all
|
|
150
|
-
|
|
151
|
-
let classifier = try MyImageClassifier(configuration: config)
|
|
152
|
-
let input = MyImageClassifierInput(image: pixelBuffer)
|
|
153
|
-
let output = try classifier.prediction(input: input)
|
|
154
|
-
|
|
155
|
-
let topLabel = output.classLabel
|
|
156
|
-
let topConfidence = output.classLabelProbs[topLabel] ?? 0
|
|
157
|
-
return (topLabel, topConfidence)
|
|
158
|
-
}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### Accessing the Underlying MLModel
|
|
162
|
-
|
|
163
|
-
```swift
|
|
164
|
-
// Get the underlying MLModel from a generated class
|
|
165
|
-
let classifier = try MyImageClassifier(configuration: config)
|
|
166
|
-
let mlModel = classifier.model
|
|
167
|
-
|
|
168
|
-
// Useful for Vision integration
|
|
169
|
-
let vnModel = try VNCoreMLModel(for: mlModel)
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
## Manual MLFeatureProvider
|
|
173
|
-
|
|
174
|
-
Implement `MLFeatureProvider` when you need custom input construction.
|
|
175
|
-
|
|
176
|
-
```swift
|
|
177
|
-
import CoreML
|
|
178
|
-
|
|
179
|
-
final class CustomImageInput: MLFeatureProvider {
|
|
180
|
-
let image: CVPixelBuffer
|
|
181
|
-
let confidenceThreshold: Double
|
|
182
|
-
|
|
183
|
-
var featureNames: Set<String> {
|
|
184
|
-
["image", "confidence_threshold"]
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
func featureValue(for featureName: String) -> MLFeatureValue? {
|
|
188
|
-
switch featureName {
|
|
189
|
-
case "image":
|
|
190
|
-
return MLFeatureValue(pixelBuffer: image)
|
|
191
|
-
case "confidence_threshold":
|
|
192
|
-
return MLFeatureValue(double: confidenceThreshold)
|
|
193
|
-
default:
|
|
194
|
-
return nil
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
init(image: CVPixelBuffer, confidenceThreshold: Double = 0.5) {
|
|
199
|
-
self.image = image
|
|
200
|
-
self.confidenceThreshold = confidenceThreshold
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Usage
|
|
205
|
-
let input = CustomImageInput(image: pixelBuffer, confidenceThreshold: 0.7)
|
|
206
|
-
let output = try model.prediction(from: input)
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
## Async Prediction Patterns
|
|
210
|
-
|
|
211
|
-
### Single Prediction with Swift Concurrency (iOS 17+)
|
|
212
|
-
|
|
213
|
-
```swift
|
|
214
|
-
actor PredictionService {
|
|
215
|
-
private let model: MLModel
|
|
216
|
-
|
|
217
|
-
init(model: MLModel) {
|
|
218
|
-
self.model = model
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
func predict(input: MLFeatureProvider) async throws -> MLFeatureProvider {
|
|
222
|
-
try await model.prediction(from: input)
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### Streaming Predictions
|
|
228
|
-
|
|
229
|
-
```swift
|
|
230
|
-
func classifyFrames(_ frames: AsyncStream<CVPixelBuffer>) async throws -> AsyncStream<String> {
|
|
231
|
-
let model = try await ModelManager().model(named: "Classifier")
|
|
232
|
-
|
|
233
|
-
return AsyncStream { continuation in
|
|
234
|
-
Task {
|
|
235
|
-
for await frame in frames {
|
|
236
|
-
let input = try MLDictionaryFeatureProvider(dictionary: [
|
|
237
|
-
"image": MLFeatureValue(pixelBuffer: frame),
|
|
238
|
-
])
|
|
239
|
-
let output = try await model.prediction(from: input)
|
|
240
|
-
let label = output.featureValue(for: "classLabel")?.stringValue ?? "unknown"
|
|
241
|
-
continuation.yield(label)
|
|
242
|
-
}
|
|
243
|
-
continuation.finish()
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
## MLBatchProvider for Batch Inference
|
|
250
|
-
|
|
251
|
-
```swift
|
|
252
|
-
import CoreML
|
|
253
|
-
|
|
254
|
-
func batchClassify(images: [CVPixelBuffer], model: MLModel) throws -> [(label: String, confidence: Double)] {
|
|
255
|
-
let batchInputs = try MLArrayBatchProvider(array: images.map { buffer in
|
|
256
|
-
try MLDictionaryFeatureProvider(dictionary: [
|
|
257
|
-
"image": MLFeatureValue(pixelBuffer: buffer),
|
|
258
|
-
])
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
let batchOutput = try model.predictions(from: batchInputs)
|
|
262
|
-
|
|
263
|
-
var results: [(String, Double)] = []
|
|
264
|
-
for i in 0..<batchOutput.count {
|
|
265
|
-
let features = batchOutput.features(at: i)
|
|
266
|
-
let label = features.featureValue(for: "classLabel")?.stringValue ?? "unknown"
|
|
267
|
-
let probs = features.featureValue(for: "classLabelProbs")?.dictionaryValue ?? [:]
|
|
268
|
-
let confidence = (probs[label] as? NSNumber)?.doubleValue ?? 0
|
|
269
|
-
results.append((label, confidence))
|
|
270
|
-
}
|
|
271
|
-
return results
|
|
272
|
-
}
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
## Stateful Predictions with MLState (iOS 18+)
|
|
276
|
-
|
|
277
|
-
`MLState` enables models that maintain internal state across predictions. This is
|
|
278
|
-
essential for sequence models (text generation, audio classification, time-series)
|
|
279
|
-
where each prediction depends on previous context.
|
|
280
|
-
|
|
281
|
-
```swift
|
|
282
|
-
import CoreML
|
|
283
|
-
|
|
284
|
-
/// Audio classification that accumulates context over time
|
|
285
|
-
actor AudioClassifier {
|
|
286
|
-
private let model: MLModel
|
|
287
|
-
private var state: MLState?
|
|
288
|
-
|
|
289
|
-
init(model: MLModel) {
|
|
290
|
-
self.model = model
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/// Start a new classification session
|
|
294
|
-
func beginSession() {
|
|
295
|
-
state = model.makeState()
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/// Classify the next audio frame using accumulated state
|
|
299
|
-
func classify(audioFeatures: MLMultiArray) async throws -> String {
|
|
300
|
-
guard let state else {
|
|
301
|
-
throw ClassifierError.noActiveSession
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
let input = try MLDictionaryFeatureProvider(dictionary: [
|
|
305
|
-
"audio_features": MLFeatureValue(multiArray: audioFeatures)
|
|
306
|
-
])
|
|
307
|
-
|
|
308
|
-
let output = try await model.prediction(from: input, using: state)
|
|
309
|
-
return output.featureValue(for: "label")?.stringValue ?? "unknown"
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/// End the session and release state
|
|
313
|
-
func endSession() {
|
|
314
|
-
state = nil
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
enum ClassifierError: Error {
|
|
319
|
-
case noActiveSession
|
|
320
|
-
}
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
### Key Rules for MLState
|
|
324
|
-
|
|
325
|
-
- **Not Sendable:** Use `MLState` from a single actor or task. Do not share
|
|
326
|
-
across concurrency domains.
|
|
327
|
-
- **Independent streams:** Call `model.makeState()` per stream when processing
|
|
328
|
-
multiple concurrent sequences (e.g., multiple audio channels).
|
|
329
|
-
- **Resettable:** Create a new state to reset accumulated context. There is no
|
|
330
|
-
explicit reset method -- just discard the old state and create fresh.
|
|
331
|
-
- **Memory:** State holds model-specific internal buffers. Release it when the
|
|
332
|
-
session ends to free memory.
|
|
333
|
-
|
|
334
|
-
## Image Preprocessing
|
|
335
|
-
|
|
336
|
-
### CVPixelBuffer from CGImage
|
|
337
|
-
|
|
338
|
-
```swift
|
|
339
|
-
import CoreVideo
|
|
340
|
-
import CoreGraphics
|
|
341
|
-
|
|
342
|
-
func createPixelBuffer(from cgImage: CGImage, width: Int, height: Int) throws -> CVPixelBuffer {
|
|
343
|
-
let attrs: [CFString: Any] = [
|
|
344
|
-
kCVPixelBufferCGImageCompatibilityKey: true,
|
|
345
|
-
kCVPixelBufferCGBitmapContextCompatibilityKey: true,
|
|
346
|
-
]
|
|
347
|
-
|
|
348
|
-
var pixelBuffer: CVPixelBuffer?
|
|
349
|
-
let status = CVPixelBufferCreate(
|
|
350
|
-
kCFAllocatorDefault,
|
|
351
|
-
width, height,
|
|
352
|
-
kCVPixelFormatType_32ARGB,
|
|
353
|
-
attrs as CFDictionary,
|
|
354
|
-
&pixelBuffer
|
|
355
|
-
)
|
|
356
|
-
guard status == kCVReturnSuccess, let buffer = pixelBuffer else {
|
|
357
|
-
throw ImageError.pixelBufferCreationFailed
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
CVPixelBufferLockBaseAddress(buffer, [])
|
|
361
|
-
defer { CVPixelBufferUnlockBaseAddress(buffer, []) }
|
|
362
|
-
|
|
363
|
-
guard let context = CGContext(
|
|
364
|
-
data: CVPixelBufferGetBaseAddress(buffer),
|
|
365
|
-
width: width,
|
|
366
|
-
height: height,
|
|
367
|
-
bitsPerComponent: 8,
|
|
368
|
-
bytesPerRow: CVPixelBufferGetBytesPerRow(buffer),
|
|
369
|
-
space: CGColorSpaceCreateDeviceRGB(),
|
|
370
|
-
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
|
|
371
|
-
) else {
|
|
372
|
-
throw ImageError.contextCreationFailed
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
|
|
376
|
-
return buffer
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
enum ImageError: Error {
|
|
380
|
-
case pixelBufferCreationFailed
|
|
381
|
-
case contextCreationFailed
|
|
382
|
-
}
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
For `CIImage` sources, use `CIContext.render(_:to:)` into a `CVPixelBuffer`
|
|
386
|
-
created with `kCVPixelFormatType_32BGRA`.
|
|
387
|
-
|
|
388
|
-
## MLMultiArray Creation and Manipulation
|
|
389
|
-
|
|
390
|
-
```swift
|
|
391
|
-
import CoreML
|
|
392
|
-
|
|
393
|
-
let array = try MLMultiArray(shape: [1, 3, 224, 224], dataType: .float32)
|
|
394
|
-
for i in 0..<array.count { array[i] = NSNumber(value: Float.random(in: 0...1)) }
|
|
395
|
-
|
|
396
|
-
let values: [Float] = [1.0, 2.0, 3.0, 4.0, 5.0]
|
|
397
|
-
let mlArray = try MLMultiArray(values)
|
|
398
|
-
|
|
399
|
-
// Access elements
|
|
400
|
-
let element = array[[0, 0, 112, 112] as [NSNumber]].floatValue
|
|
401
|
-
|
|
402
|
-
// Convert to Swift array
|
|
403
|
-
func toFloatArray(_ multiArray: MLMultiArray) -> [Float] {
|
|
404
|
-
let pointer = multiArray.dataPointer.assumingMemoryBound(to: Float.self)
|
|
405
|
-
return Array(UnsafeBufferPointer(start: pointer, count: multiArray.count))
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
let featureValue = MLFeatureValue(multiArray: array)
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
## MLTensor Advanced Operations (iOS 18+)
|
|
412
|
-
|
|
413
|
-
```swift
|
|
414
|
-
import CoreML
|
|
415
|
-
|
|
416
|
-
// Creation patterns
|
|
417
|
-
let tensor1D = MLTensor([1.0, 2.0, 3.0, 4.0])
|
|
418
|
-
let zeros = MLTensor(zeros: [3, 224, 224], scalarType: Float.self)
|
|
419
|
-
let ones = MLTensor(ones: [2, 2], scalarType: Float.self)
|
|
420
|
-
|
|
421
|
-
// Reshaping
|
|
422
|
-
let reshaped = tensor1D.reshaped(to: [2, 2])
|
|
423
|
-
let expanded = tensor1D.expandingShape(at: 0) // [1, 4]
|
|
424
|
-
|
|
425
|
-
// Arithmetic and reduction
|
|
426
|
-
let sum = tensor1D + ones.reshaped(to: [4])
|
|
427
|
-
let mean = tensor1D.mean()
|
|
428
|
-
let argmax = tensor1D.argmax()
|
|
429
|
-
|
|
430
|
-
// Activation functions
|
|
431
|
-
let softmaxed = tensor1D.softmax()
|
|
432
|
-
|
|
433
|
-
// Interop with MLMultiArray
|
|
434
|
-
let multiArray = try MLMultiArray([1.0, 2.0, 3.0])
|
|
435
|
-
let fromMultiArray = MLTensor(multiArray)
|
|
436
|
-
let shaped = tensor1D.shapedArray(of: Float.self) // MLShapedArray
|
|
437
|
-
|
|
438
|
-
// Concatenation
|
|
439
|
-
let a = MLTensor([1.0, 2.0])
|
|
440
|
-
let b = MLTensor([3.0, 4.0])
|
|
441
|
-
let concatenated = MLTensor(concatenating: [a, b], alongAxis: 0)
|
|
442
|
-
|
|
443
|
-
// Normalization pattern (e.g., ImageNet preprocessing)
|
|
444
|
-
func normalize(_ tensor: MLTensor, mean: [Float], std: [Float]) -> MLTensor {
|
|
445
|
-
let meanTensor = MLTensor(mean)
|
|
446
|
-
let stdTensor = MLTensor(std)
|
|
447
|
-
return (tensor - meanTensor) / stdTensor
|
|
448
|
-
}
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
## Vision + Core ML Pipelines
|
|
452
|
-
|
|
453
|
-
### Modern API (iOS 18+)
|
|
454
|
-
|
|
455
|
-
```swift
|
|
456
|
-
import Vision
|
|
457
|
-
import CoreML
|
|
458
|
-
|
|
459
|
-
@MainActor
|
|
460
|
-
@Observable
|
|
461
|
-
final class ObjectDetectionViewModel {
|
|
462
|
-
var detections: [Detection] = []
|
|
463
|
-
var isProcessing = false
|
|
464
|
-
|
|
465
|
-
struct Detection: Identifiable {
|
|
466
|
-
let id = UUID()
|
|
467
|
-
let label: String
|
|
468
|
-
let confidence: Float
|
|
469
|
-
let boundingBox: CGRect
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
func detect(in image: CGImage) async {
|
|
473
|
-
isProcessing = true
|
|
474
|
-
defer { isProcessing = false }
|
|
475
|
-
|
|
476
|
-
do {
|
|
477
|
-
let config = MLModelConfiguration()
|
|
478
|
-
config.computeUnits = .all
|
|
479
|
-
|
|
480
|
-
let detector = try MyObjectDetector(configuration: config)
|
|
481
|
-
let request = CoreMLRequest(model: .init(detector.model))
|
|
482
|
-
|
|
483
|
-
let results = try await request.perform(on: image)
|
|
484
|
-
detections = results.compactMap { observation in
|
|
485
|
-
guard let object = observation as? RecognizedObjectObservation,
|
|
486
|
-
let topLabel = object.labels.first else { return nil }
|
|
487
|
-
return Detection(
|
|
488
|
-
label: topLabel.identifier,
|
|
489
|
-
confidence: topLabel.confidence,
|
|
490
|
-
boundingBox: object.boundingBox
|
|
491
|
-
)
|
|
492
|
-
}
|
|
493
|
-
} catch {
|
|
494
|
-
detections = []
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
```swift
|
|
501
|
-
func classifyImage(_ image: CGImage) async throws -> [(label: String, confidence: Float)] {
|
|
502
|
-
let classifier = try MyImageClassifier(configuration: .init())
|
|
503
|
-
let request = CoreMLRequest(model: .init(classifier.model))
|
|
504
|
-
|
|
505
|
-
let results = try await request.perform(on: image)
|
|
506
|
-
return results.compactMap { observation in
|
|
507
|
-
guard let classification = observation as? ClassificationObservation else { return nil }
|
|
508
|
-
return (classification.identifier, classification.confidence)
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
### Legacy API (Pre-iOS 18)
|
|
514
|
-
|
|
515
|
-
```swift
|
|
516
|
-
import Vision
|
|
517
|
-
import CoreML
|
|
518
|
-
|
|
519
|
-
func detectLegacy(in image: CGImage) async throws -> [VNRecognizedObjectObservation] {
|
|
520
|
-
let config = MLModelConfiguration()
|
|
521
|
-
config.computeUnits = .all
|
|
522
|
-
|
|
523
|
-
let detector = try MyObjectDetector(configuration: config)
|
|
524
|
-
let vnModel = try VNCoreMLModel(for: detector.model)
|
|
525
|
-
|
|
526
|
-
let request = VNCoreMLRequest(model: vnModel)
|
|
527
|
-
request.imageCropAndScaleOption = .scaleFill
|
|
528
|
-
|
|
529
|
-
let handler = VNImageRequestHandler(cgImage: image)
|
|
530
|
-
return try await Task.detached {
|
|
531
|
-
try handler.perform([request])
|
|
532
|
-
return request.results as? [VNRecognizedObjectObservation] ?? []
|
|
533
|
-
}.value
|
|
534
|
-
}
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
```swift
|
|
538
|
-
func classifyImageLegacy(_ image: CGImage) async throws -> [(label: String, confidence: Float)] {
|
|
539
|
-
let classifier = try MyImageClassifier(configuration: .init())
|
|
540
|
-
let vnModel = try VNCoreMLModel(for: classifier.model)
|
|
541
|
-
let request = VNCoreMLRequest(model: vnModel)
|
|
542
|
-
request.imageCropAndScaleOption = .centerCrop
|
|
543
|
-
|
|
544
|
-
let handler = VNImageRequestHandler(cgImage: image)
|
|
545
|
-
return try await Task.detached {
|
|
546
|
-
try handler.perform([request])
|
|
547
|
-
guard let results = request.results as? [VNClassificationObservation] else { return [] }
|
|
548
|
-
return results.prefix(5).map { ($0.identifier, $0.confidence) }
|
|
549
|
-
}.value
|
|
550
|
-
}
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
## NaturalLanguage Integration
|
|
554
|
-
|
|
555
|
-
Use `NLModel` to load Core ML models trained for NLP tasks.
|
|
556
|
-
|
|
557
|
-
```swift
|
|
558
|
-
import NaturalLanguage
|
|
559
|
-
|
|
560
|
-
func analyzeSentiment(text: String) throws -> (label: String, confidence: Double)? {
|
|
561
|
-
let modelURL = Bundle.main.url(forResource: "SentimentClassifier", withExtension: "mlmodelc")!
|
|
562
|
-
let nlModel = try NLModel(contentsOf: modelURL)
|
|
563
|
-
|
|
564
|
-
guard let label = nlModel.predictedLabel(for: text) else { return nil }
|
|
565
|
-
let hypotheses = nlModel.predictedLabelHypotheses(for: text, maximumCount: 1)
|
|
566
|
-
let confidence = hypotheses[label] ?? 0
|
|
567
|
-
return (label, confidence)
|
|
568
|
-
}
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
## MLComputePlan Detailed Usage (iOS 17+)
|
|
572
|
-
|
|
573
|
-
```swift
|
|
574
|
-
import CoreML
|
|
575
|
-
|
|
576
|
-
func profileModel(at url: URL) async throws {
|
|
577
|
-
let config = MLModelConfiguration()
|
|
578
|
-
config.computeUnits = .all
|
|
579
|
-
let computePlan = try await MLComputePlan.load(contentsOf: url, configuration: config)
|
|
580
|
-
|
|
581
|
-
guard case let .program(program) = computePlan.modelStructure,
|
|
582
|
-
let mainFunction = program.functions["main"] else {
|
|
583
|
-
print("Model is not an ML program or has no main function")
|
|
584
|
-
return
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
for operation in mainFunction.block.operations {
|
|
588
|
-
let opName = operation.operatorName
|
|
589
|
-
if let deviceUsage = computePlan.deviceUsage(for: operation) {
|
|
590
|
-
print(" \(opName): \(deviceUsage.preferredComputeDevice)")
|
|
591
|
-
}
|
|
592
|
-
if let cost = computePlan.estimatedCost(of: operation) {
|
|
593
|
-
print(" Estimated weight: \(cost.weight)")
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
### Interpreting MLComputePlan Results
|
|
600
|
-
|
|
601
|
-
| Device | Meaning | Action |
|
|
602
|
-
|---|---|---|
|
|
603
|
-
| Neural Engine | Best efficiency and speed for supported ops | Ideal -- no changes needed |
|
|
604
|
-
| GPU | Runs on Metal GPU | Good for large matrix ops |
|
|
605
|
-
| CPU | Fallback for unsupported operations | Investigate if many ops fall here |
|
|
606
|
-
|
|
607
|
-
If many critical operations fall back to CPU, try `.cpuAndGPU` compute units,
|
|
608
|
-
check for unsupported ANE operations, or re-convert with a different deployment target.
|
|
609
|
-
|
|
610
|
-
## Background Model Loading and App Lifecycle
|
|
611
|
-
|
|
612
|
-
Manage model loading and memory across app lifecycle transitions.
|
|
613
|
-
|
|
614
|
-
```swift
|
|
615
|
-
@MainActor
|
|
616
|
-
@Observable
|
|
617
|
-
final class AppModelState {
|
|
618
|
-
var isModelReady = false
|
|
619
|
-
private let modelManager = ModelManager()
|
|
620
|
-
|
|
621
|
-
func warmup() async {
|
|
622
|
-
do {
|
|
623
|
-
let config = MLModelConfiguration()
|
|
624
|
-
config.computeUnits = .all
|
|
625
|
-
_ = try await modelManager.model(named: "MainClassifier", configuration: config)
|
|
626
|
-
isModelReady = true
|
|
627
|
-
} catch {
|
|
628
|
-
isModelReady = false
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
func handleBackground() async {
|
|
633
|
-
await modelManager.unloadAll()
|
|
634
|
-
isModelReady = false
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// SwiftUI integration
|
|
639
|
-
@main
|
|
640
|
-
struct MyApp: App {
|
|
641
|
-
@State private var modelState = AppModelState()
|
|
642
|
-
@Environment(\.scenePhase) private var scenePhase
|
|
643
|
-
|
|
644
|
-
var body: some Scene {
|
|
645
|
-
WindowGroup {
|
|
646
|
-
ContentView()
|
|
647
|
-
.environment(modelState)
|
|
648
|
-
.task { await modelState.warmup() }
|
|
649
|
-
.onChange(of: scenePhase) { _, newPhase in
|
|
650
|
-
Task {
|
|
651
|
-
if newPhase == .background {
|
|
652
|
-
await modelState.handleBackground()
|
|
653
|
-
} else if newPhase == .active {
|
|
654
|
-
await modelState.warmup()
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
## Error Handling
|
|
664
|
-
|
|
665
|
-
```swift
|
|
666
|
-
import CoreML
|
|
667
|
-
|
|
668
|
-
func loadAndPredict(modelName: String, input: MLFeatureProvider) async -> MLFeatureProvider? {
|
|
669
|
-
let config = MLModelConfiguration()
|
|
670
|
-
config.computeUnits = .all
|
|
671
|
-
|
|
672
|
-
do {
|
|
673
|
-
guard let url = Bundle.main.url(forResource: modelName, withExtension: "mlmodelc") else {
|
|
674
|
-
print("Model \(modelName) not found in bundle")
|
|
675
|
-
return nil
|
|
676
|
-
}
|
|
677
|
-
let model = try await MLModel.load(contentsOf: url, configuration: config)
|
|
678
|
-
return try await model.prediction(from: input)
|
|
679
|
-
} catch {
|
|
680
|
-
print("Model error: \(error)")
|
|
681
|
-
return nil
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
```
|
|
685
|
-
|
|
686
|
-
### Common Error Types
|
|
687
|
-
|
|
688
|
-
| Error | Cause | Fix |
|
|
689
|
-
|---|---|---|
|
|
690
|
-
| `MLModel` file not found | Wrong bundle path or missing target membership | Verify file is in correct target |
|
|
691
|
-
| Compilation failure | Corrupted `.mlpackage` or unsupported ops | Re-export from coremltools |
|
|
692
|
-
| Input shape mismatch | Wrong image dimensions or tensor shape | Match model's expected input shape |
|
|
693
|
-
| Out of memory | Model too large for device | Use smaller model or `.cpuOnly` compute |
|
|
694
|
-
| Compute unit fallback | Ops unsupported on requested device | Use `.all` or check `MLComputePlan` |
|
|
695
|
-
|
|
696
|
-
## Testing Patterns
|
|
697
|
-
|
|
698
|
-
```swift
|
|
699
|
-
import Testing
|
|
700
|
-
import CoreML
|
|
701
|
-
|
|
702
|
-
struct ModelLoadingTests {
|
|
703
|
-
@Test func loadModelSucceeds() async throws {
|
|
704
|
-
let config = MLModelConfiguration()
|
|
705
|
-
config.computeUnits = .cpuOnly // CPU for test stability
|
|
706
|
-
let model = try MyImageClassifier(configuration: config)
|
|
707
|
-
#expect(model.model.modelDescription.inputDescriptionsByName.count > 0)
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
@Test func predictionReturnsValidOutput() async throws {
|
|
711
|
-
let config = MLModelConfiguration()
|
|
712
|
-
config.computeUnits = .cpuOnly
|
|
713
|
-
|
|
714
|
-
let model = try MyImageClassifier(configuration: config)
|
|
715
|
-
let input = try createTestInput(width: 224, height: 224)
|
|
716
|
-
let output = try model.prediction(input: input)
|
|
717
|
-
|
|
718
|
-
#expect(!output.classLabel.isEmpty)
|
|
719
|
-
#expect(output.classLabelProbs.values.allSatisfy { $0 >= 0 && $0 <= 1 })
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
@Test func predictionLatencyUnderThreshold() async throws {
|
|
723
|
-
let config = MLModelConfiguration()
|
|
724
|
-
config.computeUnits = .all
|
|
725
|
-
|
|
726
|
-
let model = try MyImageClassifier(configuration: config)
|
|
727
|
-
let input = try createTestInput(width: 224, height: 224)
|
|
728
|
-
|
|
729
|
-
_ = try model.prediction(input: input) // Warm up
|
|
730
|
-
|
|
731
|
-
let start = ContinuousClock.now
|
|
732
|
-
for _ in 0..<10 {
|
|
733
|
-
_ = try model.prediction(input: input)
|
|
734
|
-
}
|
|
735
|
-
let avgMs = (ContinuousClock.now - start) / 10
|
|
736
|
-
|
|
737
|
-
#expect(avgMs < .milliseconds(50), "Average prediction time \(avgMs) exceeds 50ms")
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
private func createTestPixelBuffer(width: Int, height: Int) throws -> CVPixelBuffer {
|
|
742
|
-
var pixelBuffer: CVPixelBuffer?
|
|
743
|
-
let status = CVPixelBufferCreate(
|
|
744
|
-
kCFAllocatorDefault, width, height,
|
|
745
|
-
kCVPixelFormatType_32ARGB, nil, &pixelBuffer
|
|
746
|
-
)
|
|
747
|
-
guard status == kCVReturnSuccess, let buffer = pixelBuffer else {
|
|
748
|
-
throw ImageError.pixelBufferCreationFailed
|
|
749
|
-
}
|
|
750
|
-
return buffer
|
|
751
|
-
}
|
|
752
|
-
```
|
|
753
|
-
|
|
754
|
-
## Memory Management Best Practices
|
|
755
|
-
|
|
756
|
-
1. **Unload on background.** Unload models when `scenePhase == .background`
|
|
757
|
-
and reload on return to foreground. iOS reclaims memory aggressively.
|
|
758
|
-
2. **Use `.cpuOnly` for background processing.** GPU and Neural Engine may not
|
|
759
|
-
be available in background execution contexts.
|
|
760
|
-
3. **Prefer compiled models.** `.mlmodelc` loads faster and uses less transient
|
|
761
|
-
memory than compiling `.mlpackage` at runtime.
|
|
762
|
-
4. **Share model instances.** Use an actor (like `ModelManager` above) to
|
|
763
|
-
ensure only one instance of each model exists.
|
|
764
|
-
5. **Release batch providers promptly.** Large `MLArrayBatchProvider` instances
|
|
765
|
-
hold references to all input data.
|
|
1
|
+
# Core ML Swift Integration Reference
|
|
2
|
+
|
|
3
|
+
Complete implementation patterns for loading, configuring, and running Core ML
|
|
4
|
+
models in Swift. All patterns target iOS 26+ with Swift 6.2, backward-compatible
|
|
5
|
+
to iOS 14 unless noted.
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
- Actor-Based Model Loading and Caching
|
|
9
|
+
- Auto-Generated Class Usage
|
|
10
|
+
- Manual MLFeatureProvider
|
|
11
|
+
- Async Prediction Patterns
|
|
12
|
+
- MLBatchProvider for Batch Inference
|
|
13
|
+
- Stateful Predictions with MLState (iOS 18+)
|
|
14
|
+
- Image Preprocessing
|
|
15
|
+
- MLMultiArray Creation and Manipulation
|
|
16
|
+
- MLTensor Advanced Operations
|
|
17
|
+
- Vision + Core ML Pipelines
|
|
18
|
+
- NaturalLanguage Integration
|
|
19
|
+
- MLComputePlan Detailed Usage (iOS 17.4+)
|
|
20
|
+
- Background Loading and Memory Management
|
|
21
|
+
- Error Handling Patterns
|
|
22
|
+
- Testing Patterns
|
|
23
|
+
|
|
24
|
+
## Actor-Based Model Loading and Caching
|
|
25
|
+
|
|
26
|
+
Use an actor to manage model lifecycle, prevent concurrent loading, and cache
|
|
27
|
+
compiled models persistently.
|
|
28
|
+
|
|
29
|
+
```swift
|
|
30
|
+
import CoreML
|
|
31
|
+
|
|
32
|
+
actor ModelManager {
|
|
33
|
+
private var loadedModels: [String: MLModel] = [:]
|
|
34
|
+
private let cacheDirectory: URL
|
|
35
|
+
|
|
36
|
+
init() {
|
|
37
|
+
let appSupport = FileManager.default.urls(
|
|
38
|
+
for: .applicationSupportDirectory, in: .userDomainMask
|
|
39
|
+
).first!
|
|
40
|
+
cacheDirectory = appSupport.appendingPathComponent("CompiledModels", isDirectory: true)
|
|
41
|
+
try? FileManager.default.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func model(named name: String, configuration: MLModelConfiguration = .init()) async throws -> MLModel {
|
|
45
|
+
if let cached = loadedModels[name] {
|
|
46
|
+
return cached
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let compiledURL = try await compiledModelURL(for: name)
|
|
50
|
+
let model = try await MLModel.load(contentsOf: compiledURL, configuration: configuration)
|
|
51
|
+
loadedModels[name] = model
|
|
52
|
+
return model
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
func unloadModel(named name: String) {
|
|
56
|
+
loadedModels.removeValue(forKey: name)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func unloadAll() {
|
|
60
|
+
loadedModels.removeAll()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private func compiledModelURL(for name: String) async throws -> URL {
|
|
64
|
+
// Check for pre-compiled model in cache
|
|
65
|
+
let cachedURL = cacheDirectory.appendingPathComponent("\(name).mlmodelc")
|
|
66
|
+
if FileManager.default.fileExists(atPath: cachedURL.path) {
|
|
67
|
+
return cachedURL
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Check for pre-compiled model in bundle
|
|
71
|
+
if let bundledCompiledURL = Bundle.main.url(forResource: name, withExtension: "mlmodelc") {
|
|
72
|
+
return bundledCompiledURL
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Compile from .mlpackage and cache
|
|
76
|
+
guard let packageURL = Bundle.main.url(forResource: name, withExtension: "mlpackage") else {
|
|
77
|
+
throw ModelManagerError.modelNotFound(name)
|
|
78
|
+
}
|
|
79
|
+
let tempCompiledURL = try await MLModel.compileModel(at: packageURL)
|
|
80
|
+
|
|
81
|
+
// Move compiled model to persistent cache
|
|
82
|
+
if FileManager.default.fileExists(atPath: cachedURL.path) {
|
|
83
|
+
try FileManager.default.removeItem(at: cachedURL)
|
|
84
|
+
}
|
|
85
|
+
try FileManager.default.moveItem(at: tempCompiledURL, to: cachedURL)
|
|
86
|
+
return cachedURL
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
enum ModelManagerError: Error {
|
|
91
|
+
case modelNotFound(String)
|
|
92
|
+
case predictionFailed(String)
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Usage with SwiftUI
|
|
97
|
+
|
|
98
|
+
```swift
|
|
99
|
+
@MainActor
|
|
100
|
+
@Observable
|
|
101
|
+
final class ClassifierViewModel {
|
|
102
|
+
var classLabel: String = ""
|
|
103
|
+
var confidence: Double = 0
|
|
104
|
+
var isLoading = false
|
|
105
|
+
var errorMessage: String?
|
|
106
|
+
|
|
107
|
+
private let modelManager = ModelManager()
|
|
108
|
+
|
|
109
|
+
func classify(image: CGImage) async {
|
|
110
|
+
isLoading = true
|
|
111
|
+
defer { isLoading = false }
|
|
112
|
+
|
|
113
|
+
do {
|
|
114
|
+
let config = MLModelConfiguration()
|
|
115
|
+
config.computeUnits = .all
|
|
116
|
+
|
|
117
|
+
let model = try await modelManager.model(named: "ImageClassifier", configuration: config)
|
|
118
|
+
|
|
119
|
+
let pixelBuffer = try image.toPixelBuffer(width: 224, height: 224)
|
|
120
|
+
let input = try MLDictionaryFeatureProvider(dictionary: [
|
|
121
|
+
"image": MLFeatureValue(pixelBuffer: pixelBuffer),
|
|
122
|
+
])
|
|
123
|
+
let output = try await model.prediction(from: input)
|
|
124
|
+
|
|
125
|
+
classLabel = output.featureValue(for: "classLabel")?.stringValue ?? "Unknown"
|
|
126
|
+
confidence = output.featureValue(for: "classLabelProbs")?
|
|
127
|
+
.dictionaryValue[classLabel]?
|
|
128
|
+
.doubleValue ?? 0
|
|
129
|
+
} catch {
|
|
130
|
+
errorMessage = "Classification failed: \(error.localizedDescription)"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Auto-Generated Class Usage
|
|
137
|
+
|
|
138
|
+
When you add a `.mlpackage` or `.mlmodelc` to your Xcode project, Xcode
|
|
139
|
+
generates a Swift class with typed inputs and outputs.
|
|
140
|
+
|
|
141
|
+
```swift
|
|
142
|
+
import CoreML
|
|
143
|
+
|
|
144
|
+
// Xcode generates: MyImageClassifier, MyImageClassifierInput, MyImageClassifierOutput
|
|
145
|
+
|
|
146
|
+
// Synchronous prediction with generated types
|
|
147
|
+
func classifyWithGeneratedClass(pixelBuffer: CVPixelBuffer) throws -> (label: String, confidence: Double) {
|
|
148
|
+
let config = MLModelConfiguration()
|
|
149
|
+
config.computeUnits = .all
|
|
150
|
+
|
|
151
|
+
let classifier = try MyImageClassifier(configuration: config)
|
|
152
|
+
let input = MyImageClassifierInput(image: pixelBuffer)
|
|
153
|
+
let output = try classifier.prediction(input: input)
|
|
154
|
+
|
|
155
|
+
let topLabel = output.classLabel
|
|
156
|
+
let topConfidence = output.classLabelProbs[topLabel] ?? 0
|
|
157
|
+
return (topLabel, topConfidence)
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Accessing the Underlying MLModel
|
|
162
|
+
|
|
163
|
+
```swift
|
|
164
|
+
// Get the underlying MLModel from a generated class
|
|
165
|
+
let classifier = try MyImageClassifier(configuration: config)
|
|
166
|
+
let mlModel = classifier.model
|
|
167
|
+
|
|
168
|
+
// Useful for Vision integration
|
|
169
|
+
let vnModel = try VNCoreMLModel(for: mlModel)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Manual MLFeatureProvider
|
|
173
|
+
|
|
174
|
+
Implement `MLFeatureProvider` when you need custom input construction.
|
|
175
|
+
|
|
176
|
+
```swift
|
|
177
|
+
import CoreML
|
|
178
|
+
|
|
179
|
+
final class CustomImageInput: MLFeatureProvider {
|
|
180
|
+
let image: CVPixelBuffer
|
|
181
|
+
let confidenceThreshold: Double
|
|
182
|
+
|
|
183
|
+
var featureNames: Set<String> {
|
|
184
|
+
["image", "confidence_threshold"]
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
func featureValue(for featureName: String) -> MLFeatureValue? {
|
|
188
|
+
switch featureName {
|
|
189
|
+
case "image":
|
|
190
|
+
return MLFeatureValue(pixelBuffer: image)
|
|
191
|
+
case "confidence_threshold":
|
|
192
|
+
return MLFeatureValue(double: confidenceThreshold)
|
|
193
|
+
default:
|
|
194
|
+
return nil
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
init(image: CVPixelBuffer, confidenceThreshold: Double = 0.5) {
|
|
199
|
+
self.image = image
|
|
200
|
+
self.confidenceThreshold = confidenceThreshold
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Usage
|
|
205
|
+
let input = CustomImageInput(image: pixelBuffer, confidenceThreshold: 0.7)
|
|
206
|
+
let output = try model.prediction(from: input)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Async Prediction Patterns
|
|
210
|
+
|
|
211
|
+
### Single Prediction with Swift Concurrency (iOS 17+)
|
|
212
|
+
|
|
213
|
+
```swift
|
|
214
|
+
actor PredictionService {
|
|
215
|
+
private let model: MLModel
|
|
216
|
+
|
|
217
|
+
init(model: MLModel) {
|
|
218
|
+
self.model = model
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
func predict(input: MLFeatureProvider) async throws -> MLFeatureProvider {
|
|
222
|
+
try await model.prediction(from: input)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Streaming Predictions
|
|
228
|
+
|
|
229
|
+
```swift
|
|
230
|
+
func classifyFrames(_ frames: AsyncStream<CVPixelBuffer>) async throws -> AsyncStream<String> {
|
|
231
|
+
let model = try await ModelManager().model(named: "Classifier")
|
|
232
|
+
|
|
233
|
+
return AsyncStream { continuation in
|
|
234
|
+
Task {
|
|
235
|
+
for await frame in frames {
|
|
236
|
+
let input = try MLDictionaryFeatureProvider(dictionary: [
|
|
237
|
+
"image": MLFeatureValue(pixelBuffer: frame),
|
|
238
|
+
])
|
|
239
|
+
let output = try await model.prediction(from: input)
|
|
240
|
+
let label = output.featureValue(for: "classLabel")?.stringValue ?? "unknown"
|
|
241
|
+
continuation.yield(label)
|
|
242
|
+
}
|
|
243
|
+
continuation.finish()
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## MLBatchProvider for Batch Inference
|
|
250
|
+
|
|
251
|
+
```swift
|
|
252
|
+
import CoreML
|
|
253
|
+
|
|
254
|
+
func batchClassify(images: [CVPixelBuffer], model: MLModel) throws -> [(label: String, confidence: Double)] {
|
|
255
|
+
let batchInputs = try MLArrayBatchProvider(array: images.map { buffer in
|
|
256
|
+
try MLDictionaryFeatureProvider(dictionary: [
|
|
257
|
+
"image": MLFeatureValue(pixelBuffer: buffer),
|
|
258
|
+
])
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
let batchOutput = try model.predictions(from: batchInputs)
|
|
262
|
+
|
|
263
|
+
var results: [(String, Double)] = []
|
|
264
|
+
for i in 0..<batchOutput.count {
|
|
265
|
+
let features = batchOutput.features(at: i)
|
|
266
|
+
let label = features.featureValue(for: "classLabel")?.stringValue ?? "unknown"
|
|
267
|
+
let probs = features.featureValue(for: "classLabelProbs")?.dictionaryValue ?? [:]
|
|
268
|
+
let confidence = (probs[label] as? NSNumber)?.doubleValue ?? 0
|
|
269
|
+
results.append((label, confidence))
|
|
270
|
+
}
|
|
271
|
+
return results
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Stateful Predictions with MLState (iOS 18+)
|
|
276
|
+
|
|
277
|
+
`MLState` enables models that maintain internal state across predictions. This is
|
|
278
|
+
essential for sequence models (text generation, audio classification, time-series)
|
|
279
|
+
where each prediction depends on previous context.
|
|
280
|
+
|
|
281
|
+
```swift
|
|
282
|
+
import CoreML
|
|
283
|
+
|
|
284
|
+
/// Audio classification that accumulates context over time
|
|
285
|
+
actor AudioClassifier {
|
|
286
|
+
private let model: MLModel
|
|
287
|
+
private var state: MLState?
|
|
288
|
+
|
|
289
|
+
init(model: MLModel) {
|
|
290
|
+
self.model = model
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/// Start a new classification session
|
|
294
|
+
func beginSession() {
|
|
295
|
+
state = model.makeState()
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/// Classify the next audio frame using accumulated state
|
|
299
|
+
func classify(audioFeatures: MLMultiArray) async throws -> String {
|
|
300
|
+
guard let state else {
|
|
301
|
+
throw ClassifierError.noActiveSession
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let input = try MLDictionaryFeatureProvider(dictionary: [
|
|
305
|
+
"audio_features": MLFeatureValue(multiArray: audioFeatures)
|
|
306
|
+
])
|
|
307
|
+
|
|
308
|
+
let output = try await model.prediction(from: input, using: state)
|
|
309
|
+
return output.featureValue(for: "label")?.stringValue ?? "unknown"
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/// End the session and release state
|
|
313
|
+
func endSession() {
|
|
314
|
+
state = nil
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
enum ClassifierError: Error {
|
|
319
|
+
case noActiveSession
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Key Rules for MLState
|
|
324
|
+
|
|
325
|
+
- **Not Sendable:** Use `MLState` from a single actor or task. Do not share
|
|
326
|
+
across concurrency domains.
|
|
327
|
+
- **Independent streams:** Call `model.makeState()` per stream when processing
|
|
328
|
+
multiple concurrent sequences (e.g., multiple audio channels).
|
|
329
|
+
- **Resettable:** Create a new state to reset accumulated context. There is no
|
|
330
|
+
explicit reset method -- just discard the old state and create fresh.
|
|
331
|
+
- **Memory:** State holds model-specific internal buffers. Release it when the
|
|
332
|
+
session ends to free memory.
|
|
333
|
+
|
|
334
|
+
## Image Preprocessing
|
|
335
|
+
|
|
336
|
+
### CVPixelBuffer from CGImage
|
|
337
|
+
|
|
338
|
+
```swift
|
|
339
|
+
import CoreVideo
|
|
340
|
+
import CoreGraphics
|
|
341
|
+
|
|
342
|
+
func createPixelBuffer(from cgImage: CGImage, width: Int, height: Int) throws -> CVPixelBuffer {
|
|
343
|
+
let attrs: [CFString: Any] = [
|
|
344
|
+
kCVPixelBufferCGImageCompatibilityKey: true,
|
|
345
|
+
kCVPixelBufferCGBitmapContextCompatibilityKey: true,
|
|
346
|
+
]
|
|
347
|
+
|
|
348
|
+
var pixelBuffer: CVPixelBuffer?
|
|
349
|
+
let status = CVPixelBufferCreate(
|
|
350
|
+
kCFAllocatorDefault,
|
|
351
|
+
width, height,
|
|
352
|
+
kCVPixelFormatType_32ARGB,
|
|
353
|
+
attrs as CFDictionary,
|
|
354
|
+
&pixelBuffer
|
|
355
|
+
)
|
|
356
|
+
guard status == kCVReturnSuccess, let buffer = pixelBuffer else {
|
|
357
|
+
throw ImageError.pixelBufferCreationFailed
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
CVPixelBufferLockBaseAddress(buffer, [])
|
|
361
|
+
defer { CVPixelBufferUnlockBaseAddress(buffer, []) }
|
|
362
|
+
|
|
363
|
+
guard let context = CGContext(
|
|
364
|
+
data: CVPixelBufferGetBaseAddress(buffer),
|
|
365
|
+
width: width,
|
|
366
|
+
height: height,
|
|
367
|
+
bitsPerComponent: 8,
|
|
368
|
+
bytesPerRow: CVPixelBufferGetBytesPerRow(buffer),
|
|
369
|
+
space: CGColorSpaceCreateDeviceRGB(),
|
|
370
|
+
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
|
|
371
|
+
) else {
|
|
372
|
+
throw ImageError.contextCreationFailed
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
|
|
376
|
+
return buffer
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
enum ImageError: Error {
|
|
380
|
+
case pixelBufferCreationFailed
|
|
381
|
+
case contextCreationFailed
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
For `CIImage` sources, use `CIContext.render(_:to:)` into a `CVPixelBuffer`
|
|
386
|
+
created with `kCVPixelFormatType_32BGRA`.
|
|
387
|
+
|
|
388
|
+
## MLMultiArray Creation and Manipulation
|
|
389
|
+
|
|
390
|
+
```swift
|
|
391
|
+
import CoreML
|
|
392
|
+
|
|
393
|
+
let array = try MLMultiArray(shape: [1, 3, 224, 224], dataType: .float32)
|
|
394
|
+
for i in 0..<array.count { array[i] = NSNumber(value: Float.random(in: 0...1)) }
|
|
395
|
+
|
|
396
|
+
let values: [Float] = [1.0, 2.0, 3.0, 4.0, 5.0]
|
|
397
|
+
let mlArray = try MLMultiArray(values)
|
|
398
|
+
|
|
399
|
+
// Access elements
|
|
400
|
+
let element = array[[0, 0, 112, 112] as [NSNumber]].floatValue
|
|
401
|
+
|
|
402
|
+
// Convert to Swift array
|
|
403
|
+
func toFloatArray(_ multiArray: MLMultiArray) -> [Float] {
|
|
404
|
+
let pointer = multiArray.dataPointer.assumingMemoryBound(to: Float.self)
|
|
405
|
+
return Array(UnsafeBufferPointer(start: pointer, count: multiArray.count))
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
let featureValue = MLFeatureValue(multiArray: array)
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## MLTensor Advanced Operations (iOS 18+)
|
|
412
|
+
|
|
413
|
+
```swift
|
|
414
|
+
import CoreML
|
|
415
|
+
|
|
416
|
+
// Creation patterns
|
|
417
|
+
let tensor1D = MLTensor([1.0, 2.0, 3.0, 4.0])
|
|
418
|
+
let zeros = MLTensor(zeros: [3, 224, 224], scalarType: Float.self)
|
|
419
|
+
let ones = MLTensor(ones: [2, 2], scalarType: Float.self)
|
|
420
|
+
|
|
421
|
+
// Reshaping
|
|
422
|
+
let reshaped = tensor1D.reshaped(to: [2, 2])
|
|
423
|
+
let expanded = tensor1D.expandingShape(at: 0) // [1, 4]
|
|
424
|
+
|
|
425
|
+
// Arithmetic and reduction
|
|
426
|
+
let sum = tensor1D + ones.reshaped(to: [4])
|
|
427
|
+
let mean = tensor1D.mean()
|
|
428
|
+
let argmax = tensor1D.argmax()
|
|
429
|
+
|
|
430
|
+
// Activation functions
|
|
431
|
+
let softmaxed = tensor1D.softmax()
|
|
432
|
+
|
|
433
|
+
// Interop with MLMultiArray
|
|
434
|
+
let multiArray = try MLMultiArray([1.0, 2.0, 3.0])
|
|
435
|
+
let fromMultiArray = MLTensor(multiArray)
|
|
436
|
+
let shaped = tensor1D.shapedArray(of: Float.self) // MLShapedArray
|
|
437
|
+
|
|
438
|
+
// Concatenation
|
|
439
|
+
let a = MLTensor([1.0, 2.0])
|
|
440
|
+
let b = MLTensor([3.0, 4.0])
|
|
441
|
+
let concatenated = MLTensor(concatenating: [a, b], alongAxis: 0)
|
|
442
|
+
|
|
443
|
+
// Normalization pattern (e.g., ImageNet preprocessing)
|
|
444
|
+
func normalize(_ tensor: MLTensor, mean: [Float], std: [Float]) -> MLTensor {
|
|
445
|
+
let meanTensor = MLTensor(mean)
|
|
446
|
+
let stdTensor = MLTensor(std)
|
|
447
|
+
return (tensor - meanTensor) / stdTensor
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Vision + Core ML Pipelines
|
|
452
|
+
|
|
453
|
+
### Modern API (iOS 18+)
|
|
454
|
+
|
|
455
|
+
```swift
|
|
456
|
+
import Vision
|
|
457
|
+
import CoreML
|
|
458
|
+
|
|
459
|
+
@MainActor
|
|
460
|
+
@Observable
|
|
461
|
+
final class ObjectDetectionViewModel {
|
|
462
|
+
var detections: [Detection] = []
|
|
463
|
+
var isProcessing = false
|
|
464
|
+
|
|
465
|
+
struct Detection: Identifiable {
|
|
466
|
+
let id = UUID()
|
|
467
|
+
let label: String
|
|
468
|
+
let confidence: Float
|
|
469
|
+
let boundingBox: CGRect
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
func detect(in image: CGImage) async {
|
|
473
|
+
isProcessing = true
|
|
474
|
+
defer { isProcessing = false }
|
|
475
|
+
|
|
476
|
+
do {
|
|
477
|
+
let config = MLModelConfiguration()
|
|
478
|
+
config.computeUnits = .all
|
|
479
|
+
|
|
480
|
+
let detector = try MyObjectDetector(configuration: config)
|
|
481
|
+
let request = CoreMLRequest(model: .init(detector.model))
|
|
482
|
+
|
|
483
|
+
let results = try await request.perform(on: image)
|
|
484
|
+
detections = results.compactMap { observation in
|
|
485
|
+
guard let object = observation as? RecognizedObjectObservation,
|
|
486
|
+
let topLabel = object.labels.first else { return nil }
|
|
487
|
+
return Detection(
|
|
488
|
+
label: topLabel.identifier,
|
|
489
|
+
confidence: topLabel.confidence,
|
|
490
|
+
boundingBox: object.boundingBox
|
|
491
|
+
)
|
|
492
|
+
}
|
|
493
|
+
} catch {
|
|
494
|
+
detections = []
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
```swift
|
|
501
|
+
func classifyImage(_ image: CGImage) async throws -> [(label: String, confidence: Float)] {
|
|
502
|
+
let classifier = try MyImageClassifier(configuration: .init())
|
|
503
|
+
let request = CoreMLRequest(model: .init(classifier.model))
|
|
504
|
+
|
|
505
|
+
let results = try await request.perform(on: image)
|
|
506
|
+
return results.compactMap { observation in
|
|
507
|
+
guard let classification = observation as? ClassificationObservation else { return nil }
|
|
508
|
+
return (classification.identifier, classification.confidence)
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Legacy API (Pre-iOS 18)
|
|
514
|
+
|
|
515
|
+
```swift
|
|
516
|
+
import Vision
|
|
517
|
+
import CoreML
|
|
518
|
+
|
|
519
|
+
func detectLegacy(in image: CGImage) async throws -> [VNRecognizedObjectObservation] {
|
|
520
|
+
let config = MLModelConfiguration()
|
|
521
|
+
config.computeUnits = .all
|
|
522
|
+
|
|
523
|
+
let detector = try MyObjectDetector(configuration: config)
|
|
524
|
+
let vnModel = try VNCoreMLModel(for: detector.model)
|
|
525
|
+
|
|
526
|
+
let request = VNCoreMLRequest(model: vnModel)
|
|
527
|
+
request.imageCropAndScaleOption = .scaleFill
|
|
528
|
+
|
|
529
|
+
let handler = VNImageRequestHandler(cgImage: image)
|
|
530
|
+
return try await Task.detached {
|
|
531
|
+
try handler.perform([request])
|
|
532
|
+
return request.results as? [VNRecognizedObjectObservation] ?? []
|
|
533
|
+
}.value
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
```swift
|
|
538
|
+
func classifyImageLegacy(_ image: CGImage) async throws -> [(label: String, confidence: Float)] {
|
|
539
|
+
let classifier = try MyImageClassifier(configuration: .init())
|
|
540
|
+
let vnModel = try VNCoreMLModel(for: classifier.model)
|
|
541
|
+
let request = VNCoreMLRequest(model: vnModel)
|
|
542
|
+
request.imageCropAndScaleOption = .centerCrop
|
|
543
|
+
|
|
544
|
+
let handler = VNImageRequestHandler(cgImage: image)
|
|
545
|
+
return try await Task.detached {
|
|
546
|
+
try handler.perform([request])
|
|
547
|
+
guard let results = request.results as? [VNClassificationObservation] else { return [] }
|
|
548
|
+
return results.prefix(5).map { ($0.identifier, $0.confidence) }
|
|
549
|
+
}.value
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
## NaturalLanguage Integration
|
|
554
|
+
|
|
555
|
+
Use `NLModel` to load Core ML models trained for NLP tasks.
|
|
556
|
+
|
|
557
|
+
```swift
|
|
558
|
+
import NaturalLanguage
|
|
559
|
+
|
|
560
|
+
func analyzeSentiment(text: String) throws -> (label: String, confidence: Double)? {
|
|
561
|
+
let modelURL = Bundle.main.url(forResource: "SentimentClassifier", withExtension: "mlmodelc")!
|
|
562
|
+
let nlModel = try NLModel(contentsOf: modelURL)
|
|
563
|
+
|
|
564
|
+
guard let label = nlModel.predictedLabel(for: text) else { return nil }
|
|
565
|
+
let hypotheses = nlModel.predictedLabelHypotheses(for: text, maximumCount: 1)
|
|
566
|
+
let confidence = hypotheses[label] ?? 0
|
|
567
|
+
return (label, confidence)
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
## MLComputePlan Detailed Usage (iOS 17+)
|
|
572
|
+
|
|
573
|
+
```swift
|
|
574
|
+
import CoreML
|
|
575
|
+
|
|
576
|
+
func profileModel(at url: URL) async throws {
|
|
577
|
+
let config = MLModelConfiguration()
|
|
578
|
+
config.computeUnits = .all
|
|
579
|
+
let computePlan = try await MLComputePlan.load(contentsOf: url, configuration: config)
|
|
580
|
+
|
|
581
|
+
guard case let .program(program) = computePlan.modelStructure,
|
|
582
|
+
let mainFunction = program.functions["main"] else {
|
|
583
|
+
print("Model is not an ML program or has no main function")
|
|
584
|
+
return
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
for operation in mainFunction.block.operations {
|
|
588
|
+
let opName = operation.operatorName
|
|
589
|
+
if let deviceUsage = computePlan.deviceUsage(for: operation) {
|
|
590
|
+
print(" \(opName): \(deviceUsage.preferredComputeDevice)")
|
|
591
|
+
}
|
|
592
|
+
if let cost = computePlan.estimatedCost(of: operation) {
|
|
593
|
+
print(" Estimated weight: \(cost.weight)")
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### Interpreting MLComputePlan Results
|
|
600
|
+
|
|
601
|
+
| Device | Meaning | Action |
|
|
602
|
+
|---|---|---|
|
|
603
|
+
| Neural Engine | Best efficiency and speed for supported ops | Ideal -- no changes needed |
|
|
604
|
+
| GPU | Runs on Metal GPU | Good for large matrix ops |
|
|
605
|
+
| CPU | Fallback for unsupported operations | Investigate if many ops fall here |
|
|
606
|
+
|
|
607
|
+
If many critical operations fall back to CPU, try `.cpuAndGPU` compute units,
|
|
608
|
+
check for unsupported ANE operations, or re-convert with a different deployment target.
|
|
609
|
+
|
|
610
|
+
## Background Model Loading and App Lifecycle
|
|
611
|
+
|
|
612
|
+
Manage model loading and memory across app lifecycle transitions.
|
|
613
|
+
|
|
614
|
+
```swift
|
|
615
|
+
@MainActor
|
|
616
|
+
@Observable
|
|
617
|
+
final class AppModelState {
|
|
618
|
+
var isModelReady = false
|
|
619
|
+
private let modelManager = ModelManager()
|
|
620
|
+
|
|
621
|
+
func warmup() async {
|
|
622
|
+
do {
|
|
623
|
+
let config = MLModelConfiguration()
|
|
624
|
+
config.computeUnits = .all
|
|
625
|
+
_ = try await modelManager.model(named: "MainClassifier", configuration: config)
|
|
626
|
+
isModelReady = true
|
|
627
|
+
} catch {
|
|
628
|
+
isModelReady = false
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
func handleBackground() async {
|
|
633
|
+
await modelManager.unloadAll()
|
|
634
|
+
isModelReady = false
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// SwiftUI integration
|
|
639
|
+
@main
|
|
640
|
+
struct MyApp: App {
|
|
641
|
+
@State private var modelState = AppModelState()
|
|
642
|
+
@Environment(\.scenePhase) private var scenePhase
|
|
643
|
+
|
|
644
|
+
var body: some Scene {
|
|
645
|
+
WindowGroup {
|
|
646
|
+
ContentView()
|
|
647
|
+
.environment(modelState)
|
|
648
|
+
.task { await modelState.warmup() }
|
|
649
|
+
.onChange(of: scenePhase) { _, newPhase in
|
|
650
|
+
Task {
|
|
651
|
+
if newPhase == .background {
|
|
652
|
+
await modelState.handleBackground()
|
|
653
|
+
} else if newPhase == .active {
|
|
654
|
+
await modelState.warmup()
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
## Error Handling
|
|
664
|
+
|
|
665
|
+
```swift
|
|
666
|
+
import CoreML
|
|
667
|
+
|
|
668
|
+
func loadAndPredict(modelName: String, input: MLFeatureProvider) async -> MLFeatureProvider? {
|
|
669
|
+
let config = MLModelConfiguration()
|
|
670
|
+
config.computeUnits = .all
|
|
671
|
+
|
|
672
|
+
do {
|
|
673
|
+
guard let url = Bundle.main.url(forResource: modelName, withExtension: "mlmodelc") else {
|
|
674
|
+
print("Model \(modelName) not found in bundle")
|
|
675
|
+
return nil
|
|
676
|
+
}
|
|
677
|
+
let model = try await MLModel.load(contentsOf: url, configuration: config)
|
|
678
|
+
return try await model.prediction(from: input)
|
|
679
|
+
} catch {
|
|
680
|
+
print("Model error: \(error)")
|
|
681
|
+
return nil
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
### Common Error Types
|
|
687
|
+
|
|
688
|
+
| Error | Cause | Fix |
|
|
689
|
+
|---|---|---|
|
|
690
|
+
| `MLModel` file not found | Wrong bundle path or missing target membership | Verify file is in correct target |
|
|
691
|
+
| Compilation failure | Corrupted `.mlpackage` or unsupported ops | Re-export from coremltools |
|
|
692
|
+
| Input shape mismatch | Wrong image dimensions or tensor shape | Match model's expected input shape |
|
|
693
|
+
| Out of memory | Model too large for device | Use smaller model or `.cpuOnly` compute |
|
|
694
|
+
| Compute unit fallback | Ops unsupported on requested device | Use `.all` or check `MLComputePlan` |
|
|
695
|
+
|
|
696
|
+
## Testing Patterns
|
|
697
|
+
|
|
698
|
+
```swift
|
|
699
|
+
import Testing
|
|
700
|
+
import CoreML
|
|
701
|
+
|
|
702
|
+
struct ModelLoadingTests {
|
|
703
|
+
@Test func loadModelSucceeds() async throws {
|
|
704
|
+
let config = MLModelConfiguration()
|
|
705
|
+
config.computeUnits = .cpuOnly // CPU for test stability
|
|
706
|
+
let model = try MyImageClassifier(configuration: config)
|
|
707
|
+
#expect(model.model.modelDescription.inputDescriptionsByName.count > 0)
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
@Test func predictionReturnsValidOutput() async throws {
|
|
711
|
+
let config = MLModelConfiguration()
|
|
712
|
+
config.computeUnits = .cpuOnly
|
|
713
|
+
|
|
714
|
+
let model = try MyImageClassifier(configuration: config)
|
|
715
|
+
let input = try createTestInput(width: 224, height: 224)
|
|
716
|
+
let output = try model.prediction(input: input)
|
|
717
|
+
|
|
718
|
+
#expect(!output.classLabel.isEmpty)
|
|
719
|
+
#expect(output.classLabelProbs.values.allSatisfy { $0 >= 0 && $0 <= 1 })
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
@Test func predictionLatencyUnderThreshold() async throws {
|
|
723
|
+
let config = MLModelConfiguration()
|
|
724
|
+
config.computeUnits = .all
|
|
725
|
+
|
|
726
|
+
let model = try MyImageClassifier(configuration: config)
|
|
727
|
+
let input = try createTestInput(width: 224, height: 224)
|
|
728
|
+
|
|
729
|
+
_ = try model.prediction(input: input) // Warm up
|
|
730
|
+
|
|
731
|
+
let start = ContinuousClock.now
|
|
732
|
+
for _ in 0..<10 {
|
|
733
|
+
_ = try model.prediction(input: input)
|
|
734
|
+
}
|
|
735
|
+
let avgMs = (ContinuousClock.now - start) / 10
|
|
736
|
+
|
|
737
|
+
#expect(avgMs < .milliseconds(50), "Average prediction time \(avgMs) exceeds 50ms")
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
private func createTestPixelBuffer(width: Int, height: Int) throws -> CVPixelBuffer {
|
|
742
|
+
var pixelBuffer: CVPixelBuffer?
|
|
743
|
+
let status = CVPixelBufferCreate(
|
|
744
|
+
kCFAllocatorDefault, width, height,
|
|
745
|
+
kCVPixelFormatType_32ARGB, nil, &pixelBuffer
|
|
746
|
+
)
|
|
747
|
+
guard status == kCVReturnSuccess, let buffer = pixelBuffer else {
|
|
748
|
+
throw ImageError.pixelBufferCreationFailed
|
|
749
|
+
}
|
|
750
|
+
return buffer
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
## Memory Management Best Practices
|
|
755
|
+
|
|
756
|
+
1. **Unload on background.** Unload models when `scenePhase == .background`
|
|
757
|
+
and reload on return to foreground. iOS reclaims memory aggressively.
|
|
758
|
+
2. **Use `.cpuOnly` for background processing.** GPU and Neural Engine may not
|
|
759
|
+
be available in background execution contexts.
|
|
760
|
+
3. **Prefer compiled models.** `.mlmodelc` loads faster and uses less transient
|
|
761
|
+
memory than compiling `.mlpackage` at runtime.
|
|
762
|
+
4. **Share model instances.** Use an actor (like `ModelManager` above) to
|
|
763
|
+
ensure only one instance of each model exists.
|
|
764
|
+
5. **Release batch providers promptly.** Large `MLArrayBatchProvider` instances
|
|
765
|
+
hold references to all input data.
|