@devo-bmad-custom/agent-orchestration 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/installer.js +33 -0
- package/package.json +1 -1
- package/src/.agents/skills/audit-website/README.md +20 -20
- package/src/.agents/skills/audit-website/SKILL.md +470 -470
- package/src/.agents/skills/audit-website/agents/openai.yaml +6 -6
- package/src/.agents/skills/audit-website/assets/icon-small.svg +41 -41
- package/src/.agents/skills/audit-website/references/OUTPUT-FORMAT.md +250 -250
- package/src/.agents/skills/clean-code-standards/SKILL.md +104 -104
- package/src/.agents/skills/excalidraw-dark-standard/SKILL.md +281 -281
- package/src/.agents/skills/frontend-responsive-design-standards/SKILL.md +434 -434
- package/src/.agents/skills/java-fundamentals/SKILL.md +116 -116
- package/src/.agents/skills/java-performance/SKILL.md +119 -119
- package/src/.agents/skills/next-best-practices/SKILL.md +153 -153
- package/src/.agents/skills/next-best-practices/async-patterns.md +87 -87
- package/src/.agents/skills/next-best-practices/bundling.md +180 -180
- package/src/.agents/skills/next-best-practices/data-patterns.md +297 -297
- package/src/.agents/skills/next-best-practices/debug-tricks.md +105 -105
- package/src/.agents/skills/next-best-practices/directives.md +73 -73
- package/src/.agents/skills/next-best-practices/error-handling.md +227 -227
- package/src/.agents/skills/next-best-practices/file-conventions.md +140 -140
- package/src/.agents/skills/next-best-practices/font.md +245 -245
- package/src/.agents/skills/next-best-practices/functions.md +108 -108
- package/src/.agents/skills/next-best-practices/hydration-error.md +91 -91
- package/src/.agents/skills/next-best-practices/image.md +173 -173
- package/src/.agents/skills/next-best-practices/metadata.md +301 -301
- package/src/.agents/skills/next-best-practices/parallel-routes.md +287 -287
- package/src/.agents/skills/next-best-practices/route-handlers.md +146 -146
- package/src/.agents/skills/next-best-practices/rsc-boundaries.md +159 -159
- package/src/.agents/skills/next-best-practices/runtime-selection.md +39 -39
- package/src/.agents/skills/next-best-practices/scripts.md +141 -141
- package/src/.agents/skills/next-best-practices/self-hosting.md +371 -371
- package/src/.agents/skills/next-best-practices/suspense-boundaries.md +67 -67
- package/src/.agents/skills/nextjs-app-router-patterns/SKILL.md +537 -537
- package/src/.agents/skills/postgresql-optimization/SKILL.md +404 -404
- package/src/.agents/skills/python-backend/SKILL.md +153 -153
- package/src/.agents/skills/python-fundamentals/SKILL.md +234 -234
- package/src/.agents/skills/python-performance/SKILL.md +404 -404
- package/src/.agents/skills/react-expert/SKILL.md +335 -335
- package/src/.agents/skills/redis-best-practices/SKILL.md +438 -438
- package/src/.agents/skills/security-best-practices/SKILL.md +288 -288
- package/src/.agents/skills/security-review/LICENSE +22 -22
- package/src/.agents/skills/security-review/SKILL.md +312 -312
- package/src/.agents/skills/security-review/infrastructure/docker.md +432 -432
- package/src/.agents/skills/security-review/languages/javascript.md +388 -388
- package/src/.agents/skills/security-review/languages/python.md +363 -363
- package/src/.agents/skills/security-review/references/api-security.md +519 -519
- package/src/.agents/skills/security-review/references/authentication.md +353 -353
- package/src/.agents/skills/security-review/references/authorization.md +372 -372
- package/src/.agents/skills/security-review/references/business-logic.md +443 -443
- package/src/.agents/skills/security-review/references/cryptography.md +329 -329
- package/src/.agents/skills/security-review/references/csrf.md +398 -398
- package/src/.agents/skills/security-review/references/data-protection.md +378 -378
- package/src/.agents/skills/security-review/references/deserialization.md +410 -410
- package/src/.agents/skills/security-review/references/error-handling.md +436 -436
- package/src/.agents/skills/security-review/references/file-security.md +457 -457
- package/src/.agents/skills/security-review/references/injection.md +259 -259
- package/src/.agents/skills/security-review/references/logging.md +433 -433
- package/src/.agents/skills/security-review/references/misconfiguration.md +435 -435
- package/src/.agents/skills/security-review/references/modern-threats.md +475 -475
- package/src/.agents/skills/security-review/references/ssrf.md +415 -415
- package/src/.agents/skills/security-review/references/supply-chain.md +405 -405
- package/src/.agents/skills/security-review/references/xss.md +336 -336
- package/src/.agents/skills/subagent-driven-development/SKILL.md +275 -275
- package/src/.agents/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -26
- package/src/.agents/skills/subagent-driven-development/implementer-prompt.md +113 -113
- package/src/.agents/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -61
- package/src/.agents/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/src/.agents/skills/systematic-debugging/SKILL.md +296 -296
- package/src/.agents/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/src/.agents/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/src/.agents/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/src/.agents/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/src/.agents/skills/systematic-debugging/test-academic.md +14 -14
- package/src/.agents/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/src/.agents/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/src/.agents/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/src/.agents/skills/typescript-best-practices/SKILL.md +373 -373
- package/src/.agents/skills/ui-ux-pro-custom/SKILL.md +348 -348
- package/src/.agents/skills/ui-ux-pro-custom/data/charts.csv +26 -26
- package/src/.agents/skills/ui-ux-pro-custom/data/colors.csv +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/icons.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/SKILL.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/accessibility.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/animation.md +466 -466
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/composition-locals.md +231 -231
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/deprecated-patterns.md +323 -323
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/lists-scrolling.md +400 -400
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/modifiers.md +331 -331
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/navigation.md +416 -416
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/performance.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/side-effects.md +516 -516
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/foundation-source.md +13327 -13327
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/material3-source.md +19097 -19097
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/navigation-source.md +2947 -2947
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/runtime-source.md +11316 -11316
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/ui-source.md +7896 -7896
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/state-management.md +377 -377
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/styles-experimental.md +470 -470
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/theming-material3.md +349 -349
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/view-composition.md +595 -595
- package/src/.agents/skills/ui-ux-pro-custom/data/landing.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/data/mobile-ui-layout.md +654 -654
- package/src/.agents/skills/ui-ux-pro-custom/data/products.csv +96 -96
- package/src/.agents/skills/ui-ux-pro-custom/data/react-performance.csv +45 -45
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/astro.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/flutter.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/html-tailwind.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/jetpack-compose.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nextjs.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxt-ui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxtjs.csv +59 -59
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react-native.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/shadcn.csv +61 -61
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/svelte.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/swiftui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/vue.csv +50 -50
- package/src/.agents/skills/ui-ux-pro-custom/data/styles.csv +68 -68
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/SKILL.md +438 -438
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/references/alarmkit-patterns.md +584 -584
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-clips/SKILL.md +436 -436
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/SKILL.md +489 -489
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/references/appintents-advanced.md +1076 -1076
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/SKILL.md +340 -340
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/privacy-manifest.md +90 -90
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/review-checklists.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-conversion.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-optimization.md +344 -344
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/foundation-models.md +508 -508
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/mlx-swift.md +285 -285
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/references/keychain-biometric.md +211 -211
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/SKILL.md +499 -499
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/references/background-task-patterns.md +390 -390
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/SKILL.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/references/callkit-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/SKILL.md +492 -492
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/references/cloudkit-patterns.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/codable-patterns/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/SKILL.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/references/contacts-patterns.md +409 -409
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/references/ble-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/SKILL.md +388 -388
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/references/motion-patterns.md +405 -405
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/SKILL.md +495 -495
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/references/nfc-patterns.md +420 -420
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/SKILL.md +459 -459
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/references/coreml-swift-integration.md +765 -765
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/SKILL.md +422 -422
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/instruments-guide.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/lldb-patterns.md +298 -298
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/device-integrity/SKILL.md +477 -477
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/SKILL.md +460 -460
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/references/energykit-patterns.md +541 -541
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/references/eventkit-patterns.md +326 -326
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/references/healthkit-patterns.md +602 -602
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/references/matter-commissioning.md +455 -455
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/SKILL.md +301 -301
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/references/a11y-patterns.md +140 -140
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/SKILL.md +418 -418
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/formatstyle-locale.md +627 -627
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/string-catalogs.md +462 -462
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/SKILL.md +441 -441
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/background-websocket.md +862 -862
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/lightweight-clients.md +93 -93
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/network-framework.md +563 -563
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/urlsession-patterns.md +1116 -1116
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/app-review-guidelines.md +174 -174
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/cryptokit-advanced.md +296 -296
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/file-storage-patterns.md +354 -354
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/privacy-manifest.md +117 -117
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/references/live-activity-patterns.md +868 -868
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/corelocation-patterns.md +730 -730
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/mapkit-patterns.md +748 -748
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/metrickit-diagnostics/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/SKILL.md +395 -395
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/references/musickit-patterns.md +363 -363
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/SKILL.md +412 -412
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/references/translation-patterns.md +311 -311
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/SKILL.md +398 -398
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/references/wallet-passes.md +254 -254
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/SKILL.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/paperkit-integration.md +376 -376
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/pencilkit-patterns.md +302 -302
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/SKILL.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/references/permissionkit-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/av-playback.md +701 -701
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/camera-capture.md +774 -774
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/image-loading-caching.md +869 -869
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/photospicker-patterns.md +597 -597
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/notification-patterns.md +677 -677
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/rich-notifications.md +745 -745
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/references/realitykit-patterns.md +480 -480
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/references/shareplay-patterns.md +544 -544
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/speech-recognition/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/SKILL.md +478 -478
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/app-review-guidelines.md +58 -58
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/storekit-advanced.md +755 -755
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/SKILL.md +487 -487
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/references/charts-patterns.md +895 -895
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/SKILL.md +408 -408
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/approachable-concurrency.md +80 -80
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swift-6-2-concurrency.md +233 -233
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swiftui-concurrency.md +187 -187
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/synchronization-primitives.md +341 -341
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/references/swift-patterns-extended.md +505 -505
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/references/testing-patterns.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/SKILL.md +334 -334
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/core-data-coexistence.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-advanced.md +975 -975
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-queries.md +675 -675
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/SKILL.md +481 -481
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/animation-advanced.md +804 -804
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/core-animation-bridge.md +553 -553
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/SKILL.md +450 -450
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/references/gesture-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/SKILL.md +336 -336
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/form.md +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/grids.md +69 -69
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/list.md +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/scrollview.md +147 -147
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/SKILL.md +325 -325
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/references/liquid-glass.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/SKILL.md +262 -262
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/deeplinks.md +207 -207
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/navigationstack.md +177 -177
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/sheets.md +169 -169
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/tabview.md +178 -178
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/SKILL.md +381 -381
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/architecture-patterns.md +486 -486
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/deprecated-migration.md +1097 -1097
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/design-polish.md +780 -780
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/platform-and-sharing.md +696 -696
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +46 -46
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +29 -29
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-hangs-in-your-app.md +33 -33
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-improving-swiftui-performance.md +52 -52
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/SKILL.md +428 -428
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/hosting-migration.md +534 -534
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/representable-recipes.md +1133 -1133
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/SKILL.md +494 -494
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/references/tipkit-patterns.md +782 -782
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/SKILL.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/vision-requests.md +736 -736
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/visionkit-scanner.md +738 -738
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/SKILL.md +410 -410
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/references/weatherkit-patterns.md +567 -567
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/SKILL.md +497 -497
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/references/widgetkit-advanced.md +871 -871
- package/src/.agents/skills/ui-ux-pro-custom/data/typography.csv +57 -57
- package/src/.agents/skills/ui-ux-pro-custom/data/ui-reasoning.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/ux-guidelines.csv +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/web-interface.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/scripts/core.py +253 -253
- package/src/.agents/skills/ui-ux-pro-custom/scripts/design_system.py +1067 -1067
- package/src/.agents/skills/ui-ux-pro-custom/scripts/search.py +114 -114
- package/src/.agents/skills/ux-audit/SKILL.md +150 -150
- package/src/.agents/skills/websocket-engineer/SKILL.md +168 -168
- package/src/.agents/skills/websocket-engineer/references/alternatives.md +391 -391
- package/src/.agents/skills/websocket-engineer/references/patterns.md +400 -400
- package/src/.agents/skills/websocket-engineer/references/protocol.md +195 -195
- package/src/.agents/skills/websocket-engineer/references/scaling.md +333 -333
- package/src/.agents/skills/websocket-engineer/references/security.md +474 -474
- package/src/.agents/skills/writing-skills/SKILL.md +655 -655
- package/src/.agents/skills/writing-skills/anthropic-best-practices.md +1150 -1150
- package/src/.agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -189
- package/src/.agents/skills/writing-skills/graphviz-conventions.dot +171 -171
- package/src/.agents/skills/writing-skills/persuasion-principles.md +187 -187
- package/src/.agents/skills/writing-skills/render-graphs.js +168 -168
- package/src/.agents/skills/writing-skills/testing-skills-with-subagents.md +384 -384
- package/src/.claude/commands/master-orchestrator.md +15 -0
- package/src/_memory/config.yaml +11 -11
- package/src/_memory/master-orchestrator-sidecar/instructions.md +85 -32
- package/src/_memory/skills/nimbalyst-tracking/SKILL.md +103 -103
- package/src/_memory/skills/writing-skills/SKILL.md +655 -655
- package/src/bmb/agents/agent-builder.md +59 -59
- package/src/bmb/agents/module-builder.md +60 -60
- package/src/bmb/agents/workflow-builder.md +61 -61
- package/src/bmb/config.yaml +12 -12
- package/src/bmb/module-help.csv +13 -13
- package/src/bmb/workflows/agent/data/agent-architecture.md +258 -258
- package/src/bmb/workflows/agent/data/agent-compilation.md +185 -185
- package/src/bmb/workflows/agent/data/agent-menu-patterns.md +189 -189
- package/src/bmb/workflows/agent/data/agent-metadata.md +133 -133
- package/src/bmb/workflows/agent/data/agent-validation.md +111 -111
- package/src/bmb/workflows/agent/data/brainstorm-context.md +96 -96
- package/src/bmb/workflows/agent/data/communication-presets.csv +61 -61
- package/src/bmb/workflows/agent/data/critical-actions.md +75 -75
- package/src/bmb/workflows/agent/data/persona-properties.md +252 -252
- package/src/bmb/workflows/agent/data/principles-crafting.md +142 -142
- package/src/bmb/workflows/agent/data/reference/module-examples/architect.md +68 -68
- package/src/bmb/workflows/agent/data/reference/with-sidecar/journal-keeper/journal-keeper-sidecar/entries/yy-mm-dd-entry-template.md +16 -16
- package/src/bmb/workflows/agent/data/understanding-agent-types.md +126 -126
- package/src/bmb/workflows/agent/steps-c/step-01-brainstorm.md +129 -129
- package/src/bmb/workflows/agent/steps-c/step-02-discovery.md +170 -170
- package/src/bmb/workflows/agent/steps-c/step-03-sidecar-metadata.md +309 -309
- package/src/bmb/workflows/agent/steps-c/step-04-persona.md +213 -213
- package/src/bmb/workflows/agent/steps-c/step-05-commands-menu.md +179 -179
- package/src/bmb/workflows/agent/steps-c/step-06-activation.md +278 -278
- package/src/bmb/workflows/agent/steps-c/step-07-build-agent.md +316 -316
- package/src/bmb/workflows/agent/steps-c/step-08-celebrate.md +247 -247
- package/src/bmb/workflows/agent/steps-e/e-01-load-existing.md +221 -221
- package/src/bmb/workflows/agent/steps-e/e-02-discover-edits.md +195 -195
- package/src/bmb/workflows/agent/steps-e/e-04-sidecar-metadata.md +126 -126
- package/src/bmb/workflows/agent/steps-e/e-05-persona.md +135 -135
- package/src/bmb/workflows/agent/steps-e/e-06-commands-menu.md +123 -123
- package/src/bmb/workflows/agent/steps-e/e-07-activation.md +124 -124
- package/src/bmb/workflows/agent/steps-e/e-08-edit-agent.md +197 -197
- package/src/bmb/workflows/agent/steps-e/e-09-celebrate.md +155 -155
- package/src/bmb/workflows/agent/steps-v/v-01-load-review.md +137 -137
- package/src/bmb/workflows/agent/steps-v/v-02a-validate-metadata.md +116 -116
- package/src/bmb/workflows/agent/steps-v/v-02b-validate-persona.md +124 -124
- package/src/bmb/workflows/agent/steps-v/v-02c-validate-menu.md +127 -127
- package/src/bmb/workflows/agent/steps-v/v-02d-validate-structure.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-02e-validate-sidecar.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-03-summary.md +104 -104
- package/src/bmb/workflows/agent/templates/agent-plan.template.md +5 -5
- package/src/bmb/workflows/agent/templates/agent-template.md +89 -89
- package/src/bmb/workflows/agent/workflow-create-agent.md +72 -72
- package/src/bmb/workflows/agent/workflow-edit-agent.md +75 -75
- package/src/bmb/workflows/agent/workflow-validate-agent.md +73 -73
- package/src/bmb/workflows/module/data/agent-architecture.md +179 -179
- package/src/bmb/workflows/module/data/agent-spec-template.md +79 -79
- package/src/bmb/workflows/module/data/module-standards.md +263 -263
- package/src/bmb/workflows/module/data/module-yaml-conventions.md +392 -392
- package/src/bmb/workflows/module/module-help-generate.md +254 -254
- package/src/bmb/workflows/module/steps-b/step-01-welcome.md +148 -148
- package/src/bmb/workflows/module/steps-b/step-02-spark.md +141 -141
- package/src/bmb/workflows/module/steps-b/step-03-module-type.md +149 -149
- package/src/bmb/workflows/module/steps-b/step-04-vision.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-05-identity.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-06-users.md +86 -86
- package/src/bmb/workflows/module/steps-b/step-07-value.md +76 -76
- package/src/bmb/workflows/module/steps-b/step-08-agents.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-09-workflows.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-10-tools.md +91 -91
- package/src/bmb/workflows/module/steps-b/step-11-scenarios.md +84 -84
- package/src/bmb/workflows/module/steps-b/step-12-creative.md +95 -95
- package/src/bmb/workflows/module/steps-b/step-13-review.md +105 -105
- package/src/bmb/workflows/module/steps-b/step-14-finalize.md +117 -117
- package/src/bmb/workflows/module/steps-c/step-01-load-brief.md +179 -179
- package/src/bmb/workflows/module/steps-c/step-01b-continue.md +82 -82
- package/src/bmb/workflows/module/steps-c/step-02-structure.md +105 -105
- package/src/bmb/workflows/module/steps-c/step-03-config.md +119 -119
- package/src/bmb/workflows/module/steps-c/step-04-agents.md +168 -168
- package/src/bmb/workflows/module/steps-c/step-05-workflows.md +184 -184
- package/src/bmb/workflows/module/steps-c/step-06-docs.md +401 -401
- package/src/bmb/workflows/module/steps-c/step-07-complete.md +152 -152
- package/src/bmb/workflows/module/steps-e/step-01-load-target.md +81 -81
- package/src/bmb/workflows/module/steps-e/step-02-select-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-03-apply-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-04-review.md +80 -80
- package/src/bmb/workflows/module/steps-e/step-05-confirm.md +75 -75
- package/src/bmb/workflows/module/steps-v/step-01-load-target.md +96 -96
- package/src/bmb/workflows/module/steps-v/step-02-file-structure.md +93 -93
- package/src/bmb/workflows/module/steps-v/step-03-module-yaml.md +99 -99
- package/src/bmb/workflows/module/steps-v/step-04-agent-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-05-workflow-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-06-documentation.md +143 -143
- package/src/bmb/workflows/module/steps-v/step-07-installation.md +102 -102
- package/src/bmb/workflows/module/steps-v/step-08-report.md +197 -197
- package/src/bmb/workflows/module/templates/brief-template.md +154 -154
- package/src/bmb/workflows/module/templates/workflow-spec-template.md +96 -96
- package/src/bmb/workflows/module/workflow-create-module-brief.md +71 -71
- package/src/bmb/workflows/module/workflow-create-module.md +86 -86
- package/src/bmb/workflows/module/workflow-edit-module.md +66 -66
- package/src/bmb/workflows/module/workflow-validate-module.md +66 -66
- package/src/bmb/workflows/workflow/data/architecture.md +150 -150
- package/src/bmb/workflows/workflow/data/common-workflow-tools.csv +19 -19
- package/src/bmb/workflows/workflow/data/csv-data-file-standards.md +53 -53
- package/src/bmb/workflows/workflow/data/frontmatter-standards.md +184 -184
- package/src/bmb/workflows/workflow/data/input-discovery-standards.md +191 -191
- package/src/bmb/workflows/workflow/data/intent-vs-prescriptive-spectrum.md +44 -44
- package/src/bmb/workflows/workflow/data/menu-handling-standards.md +133 -133
- package/src/bmb/workflows/workflow/data/output-format-standards.md +135 -135
- package/src/bmb/workflows/workflow/data/step-file-rules.md +235 -235
- package/src/bmb/workflows/workflow/data/step-type-patterns.md +257 -257
- package/src/bmb/workflows/workflow/data/subprocess-optimization-patterns.md +188 -188
- package/src/bmb/workflows/workflow/data/trimodal-workflow-structure.md +164 -164
- package/src/bmb/workflows/workflow/data/workflow-chaining-standards.md +222 -222
- package/src/bmb/workflows/workflow/data/workflow-examples.md +232 -232
- package/src/bmb/workflows/workflow/data/workflow-type-criteria.md +134 -134
- package/src/bmb/workflows/workflow/steps-c/step-00-conversion.md +263 -263
- package/src/bmb/workflows/workflow/steps-c/step-01-discovery.md +194 -194
- package/src/bmb/workflows/workflow/steps-c/step-01b-continuation.md +3 -3
- package/src/bmb/workflows/workflow/steps-c/step-02-classification.md +270 -270
- package/src/bmb/workflows/workflow/steps-c/step-03-requirements.md +283 -283
- package/src/bmb/workflows/workflow/steps-c/step-04-tools.md +282 -282
- package/src/bmb/workflows/workflow/steps-c/step-05-plan-review.md +243 -243
- package/src/bmb/workflows/workflow/steps-c/step-06-design.md +330 -330
- package/src/bmb/workflows/workflow/steps-c/step-07-foundation.md +239 -239
- package/src/bmb/workflows/workflow/steps-c/step-08-build-step-01.md +379 -379
- package/src/bmb/workflows/workflow/steps-c/step-09-build-next-step.md +350 -350
- package/src/bmb/workflows/workflow/steps-c/step-10-confirmation.md +322 -322
- package/src/bmb/workflows/workflow/steps-c/step-11-completion.md +191 -191
- package/src/bmb/workflows/workflow/steps-e/step-e-01-assess-workflow.md +237 -237
- package/src/bmb/workflows/workflow/steps-e/step-e-02-discover-edits.md +251 -251
- package/src/bmb/workflows/workflow/steps-e/step-e-03-fix-validation.md +254 -254
- package/src/bmb/workflows/workflow/steps-e/step-e-04-direct-edit.md +277 -277
- package/src/bmb/workflows/workflow/steps-e/step-e-05-apply-edit.md +154 -154
- package/src/bmb/workflows/workflow/steps-e/step-e-06-validate-after.md +190 -190
- package/src/bmb/workflows/workflow/steps-e/step-e-07-complete.md +206 -206
- package/src/bmb/workflows/workflow/steps-v/step-01-validate-max-mode.md +109 -109
- package/src/bmb/workflows/workflow/steps-v/step-01-validate.md +221 -221
- package/src/bmb/workflows/workflow/steps-v/step-01b-structure.md +152 -152
- package/src/bmb/workflows/workflow/steps-v/step-02-frontmatter-validation.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-02b-path-violations.md +265 -265
- package/src/bmb/workflows/workflow/steps-v/step-03-menu-validation.md +164 -164
- package/src/bmb/workflows/workflow/steps-v/step-04-step-type-validation.md +211 -211
- package/src/bmb/workflows/workflow/steps-v/step-05-output-format-validation.md +200 -200
- package/src/bmb/workflows/workflow/steps-v/step-06-validation-design-check.md +195 -195
- package/src/bmb/workflows/workflow/steps-v/step-07-instruction-style-check.md +209 -209
- package/src/bmb/workflows/workflow/steps-v/step-08-collaborative-experience-check.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-08b-subprocess-optimization.md +179 -179
- package/src/bmb/workflows/workflow/steps-v/step-09-cohesive-review.md +186 -186
- package/src/bmb/workflows/workflow/steps-v/step-10-report-complete.md +154 -154
- package/src/bmb/workflows/workflow/steps-v/step-11-plan-validation.md +237 -237
- package/src/bmb/workflows/workflow/templates/minimal-output-template.md +11 -11
- package/src/bmb/workflows/workflow/templates/step-01-init-continuable-template.md +241 -241
- package/src/bmb/workflows/workflow/templates/step-1b-template.md +224 -224
- package/src/bmb/workflows/workflow/templates/step-template.md +294 -294
- package/src/bmb/workflows/workflow/templates/workflow-template.md +102 -102
- package/src/bmb/workflows/workflow/workflow-create-workflow.md +79 -79
- package/src/bmb/workflows/workflow/workflow-edit-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-rework-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-validate-max-parallel-workflow.md +66 -66
- package/src/bmb/workflows/workflow/workflow-validate-workflow.md +65 -65
- package/src/bmm/agents/analyst.md +104 -104
- package/src/bmm/agents/dev.md +100 -100
- package/src/bmm/agents/qa.md +100 -90
- package/src/bmm/agents/tech-writer/tech-writer.md +94 -94
- package/src/bmm/module-help.csv +31 -31
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +115 -115
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +107 -107
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +141 -141
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +144 -144
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +147 -147
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +161 -161
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +99 -99
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +57 -57
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +156 -156
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +165 -165
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +140 -140
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +152 -152
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +345 -345
- package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +92 -92
- package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +164 -164
- package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +174 -174
- package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +184 -184
- package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +105 -105
- package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +360 -360
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +165 -165
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +174 -174
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +141 -141
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +159 -159
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +387 -387
- package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -54
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +100 -100
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +160 -160
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +88 -88
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +99 -99
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +169 -169
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +156 -156
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +176 -176
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +184 -184
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +174 -174
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +175 -175
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +189 -189
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +162 -162
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +79 -79
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +183 -183
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +149 -149
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +187 -187
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +108 -108
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +166 -166
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +131 -131
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +150 -150
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +155 -155
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +170 -170
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +158 -158
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +147 -147
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +182 -182
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +202 -202
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +148 -148
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +201 -201
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +179 -179
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +164 -164
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +106 -106
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +111 -111
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +115 -115
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +127 -127
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +167 -167
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +143 -143
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +154 -154
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +165 -165
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +135 -135
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +101 -101
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +45 -45
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +185 -185
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +130 -130
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +93 -93
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +196 -196
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +54 -54
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +82 -82
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +106 -106
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +138 -138
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +129 -129
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +166 -166
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +186 -186
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +163 -163
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +38 -38
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +49 -49
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +124 -124
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +122 -122
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +84 -84
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +58 -58
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +43 -43
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +53 -53
- package/src/bmm/workflows/4-implementation/create-story/checklist.md +159 -159
- package/src/bmm/workflows/4-implementation/create-story/template.md +79 -79
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +20 -20
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +25 -25
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +158 -158
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +122 -122
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +87 -87
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +146 -146
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -50
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +152 -152
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +123 -123
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +201 -201
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -79
- package/src/bmm/workflows/document-project/workflow.yaml +22 -22
- package/src/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -184
- package/src/bmm/workflows/generate-project-context/steps/step-02-generate.md +322 -322
- package/src/bmm/workflows/generate-project-context/steps/step-03-complete.md +235 -235
- package/src/bmm/workflows/generate-project-context/workflow.md +49 -49
- package/src/bmm/workflows/qa/automate/workflow.yaml +233 -233
- package/src/bmm/workflows/qa-generate-e2e-tests/workflow.yaml +42 -42
- package/src/core/config.yaml +9 -9
- package/src/core/module-help.csv +10 -10
- package/src/core/scripts/generate-loop-report.py +72 -72
- package/src/core/tasks/editorial-review-prose.xml +101 -101
- package/src/core/tasks/editorial-review-structure.xml +207 -207
- package/src/core/tasks/help.md +86 -86
- package/src/core/tasks/index-docs.xml +64 -64
- package/src/core/tasks/review-adversarial-general.xml +66 -66
- package/src/core/tasks/review-adversarial-loop.xml +46 -46
- package/src/core/tasks/review-edge-case-hunter.xml +63 -63
- package/src/core/tasks/review-party-loop.xml +46 -46
- package/src/core/tasks/shard-doc.xml +107 -107
- package/src/core/tasks/workflow.xml +235 -235
- package/src/core/templates/review-loop-report.html +88 -88
- package/src/core/templates/review-loop-report.md +5 -5
- package/src/core/workflows/advanced-elicitation/workflow.xml +117 -117
- package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +212 -212
- package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -122
- package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -225
- package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -237
- package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -209
- package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -264
- package/src/core/workflows/brainstorming/steps/step-02e-deep-dive.md +68 -68
- package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +403 -403
- package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -303
- package/src/core/workflows/brainstorming/workflow.md +60 -60
- package/src/core/workflows/extract-trackers/workflow.md +45 -45
- package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +142 -142
- package/src/core/workflows/party-mode/workflow.md +194 -194
- package/src/docs/dev/tmux/actions_popup.py +291 -291
- package/src/docs/dev/tmux/tmux-setup.md +62 -1
|
@@ -1,748 +1,748 @@
|
|
|
1
|
-
# MapKit Patterns Reference
|
|
2
|
-
|
|
3
|
-
Extended patterns for MapKit on iOS 17+ with SwiftUI. Import `MapKit` and
|
|
4
|
-
`SwiftUI` in every file that uses these APIs.
|
|
5
|
-
|
|
6
|
-
```swift
|
|
7
|
-
import MapKit
|
|
8
|
-
import SwiftUI
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Contents
|
|
14
|
-
|
|
15
|
-
- [Complete Map View Setup](#complete-map-view-setup)
|
|
16
|
-
- [Custom Annotation Views](#custom-annotation-views)
|
|
17
|
-
- [Camera Control (MapCameraPosition)](#camera-control-mapcameraposition)
|
|
18
|
-
- [Map Selection Handling](#map-selection-handling)
|
|
19
|
-
- [Search with Autocomplete](#search-with-autocomplete)
|
|
20
|
-
- [Route Display](#route-display)
|
|
21
|
-
- [Look Around Preview](#look-around-preview)
|
|
22
|
-
- [Map Snapshots (MKMapSnapshotter)](#map-snapshots-mkmapsnapshotter)
|
|
23
|
-
- [Clustering Annotations](#clustering-annotations)
|
|
24
|
-
- [MapKit with MKMapItem Utilities](#mapkit-with-mkmapitem-utilities)
|
|
25
|
-
- [iOS 26 New APIs](#ios-26-new-apis)
|
|
26
|
-
- [User Location Display](#user-location-display)
|
|
27
|
-
- [Map in a List or ScrollView](#map-in-a-list-or-scrollview)
|
|
28
|
-
- [Coordinate Utilities](#coordinate-utilities)
|
|
29
|
-
- [Accessibility](#accessibility)
|
|
30
|
-
- [References](#references)
|
|
31
|
-
|
|
32
|
-
## Complete Map View Setup
|
|
33
|
-
|
|
34
|
-
A production-ready map view with markers, user location, and controls.
|
|
35
|
-
|
|
36
|
-
```swift
|
|
37
|
-
struct StoreLocatorMap: View {
|
|
38
|
-
let stores: [Store]
|
|
39
|
-
@State private var position: MapCameraPosition = .automatic
|
|
40
|
-
@State private var selectedStore: Store?
|
|
41
|
-
|
|
42
|
-
var body: some View {
|
|
43
|
-
Map(position: $position, selection: $selectedStore) {
|
|
44
|
-
UserAnnotation()
|
|
45
|
-
|
|
46
|
-
ForEach(stores) { store in
|
|
47
|
-
Marker(store.name, systemImage: "storefront",
|
|
48
|
-
coordinate: store.coordinate)
|
|
49
|
-
.tint(store.isOpen ? .green : .gray)
|
|
50
|
-
.tag(store)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
.mapStyle(.standard(pointsOfInterest: .excludingAll))
|
|
54
|
-
.mapControls {
|
|
55
|
-
MapUserLocationButton()
|
|
56
|
-
MapCompass()
|
|
57
|
-
MapScaleView()
|
|
58
|
-
MapPitchToggle()
|
|
59
|
-
}
|
|
60
|
-
.safeAreaInset(edge: .bottom) {
|
|
61
|
-
if let store = selectedStore {
|
|
62
|
-
StoreDetailCard(store: store)
|
|
63
|
-
.padding()
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Conform the data model to `Hashable` for use with map selection:
|
|
71
|
-
|
|
72
|
-
```swift
|
|
73
|
-
struct Store: Identifiable, Hashable {
|
|
74
|
-
let id: UUID
|
|
75
|
-
let name: String
|
|
76
|
-
let coordinate: CLLocationCoordinate2D
|
|
77
|
-
let isOpen: Bool
|
|
78
|
-
|
|
79
|
-
static func == (lhs: Store, rhs: Store) -> Bool { lhs.id == rhs.id }
|
|
80
|
-
func hash(into hasher: inout Hasher) { hasher.combine(id) }
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## Custom Annotation Views
|
|
87
|
-
|
|
88
|
-
Use `Annotation` for fully custom SwiftUI content at a coordinate. Prefer
|
|
89
|
-
`Marker` for standard pins because it handles clustering and accessibility
|
|
90
|
-
automatically.
|
|
91
|
-
|
|
92
|
-
```swift
|
|
93
|
-
Map {
|
|
94
|
-
ForEach(friends) { friend in
|
|
95
|
-
Annotation(friend.name, coordinate: friend.coordinate, anchor: .bottom) {
|
|
96
|
-
VStack(spacing: 0) {
|
|
97
|
-
AsyncImage(url: friend.avatarURL) { image in
|
|
98
|
-
image.resizable().scaledToFill()
|
|
99
|
-
} placeholder: {
|
|
100
|
-
Color.gray
|
|
101
|
-
}
|
|
102
|
-
.frame(width: 40, height: 40)
|
|
103
|
-
.clipShape(.circle)
|
|
104
|
-
.overlay(Circle().stroke(.white, lineWidth: 2))
|
|
105
|
-
|
|
106
|
-
Image(systemName: "triangle.fill")
|
|
107
|
-
.font(.caption2)
|
|
108
|
-
.foregroundStyle(.white)
|
|
109
|
-
.rotationEffect(.degrees(180))
|
|
110
|
-
.offset(y: -3)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Annotation with anchorOffset for callout-style layout
|
|
118
|
-
|
|
119
|
-
```swift
|
|
120
|
-
Annotation(place.name, coordinate: place.coordinate, anchor: .bottom) {
|
|
121
|
-
VStack(spacing: 2) {
|
|
122
|
-
Text(place.name)
|
|
123
|
-
.font(.caption)
|
|
124
|
-
.fontWeight(.semibold)
|
|
125
|
-
.padding(.horizontal, 8)
|
|
126
|
-
.padding(.vertical, 4)
|
|
127
|
-
.background(.ultraThinMaterial, in: .capsule)
|
|
128
|
-
|
|
129
|
-
Image(systemName: "mappin.circle.fill")
|
|
130
|
-
.font(.title)
|
|
131
|
-
.foregroundStyle(.red)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## Camera Control (MapCameraPosition)
|
|
139
|
-
|
|
140
|
-
### Animate camera changes
|
|
141
|
-
|
|
142
|
-
Wrap position updates in `withAnimation` for smooth transitions:
|
|
143
|
-
|
|
144
|
-
```swift
|
|
145
|
-
func flyTo(_ coordinate: CLLocationCoordinate2D) {
|
|
146
|
-
withAnimation(.easeInOut(duration: 1.0)) {
|
|
147
|
-
position = .camera(
|
|
148
|
-
MapCamera(centerCoordinate: coordinate, distance: 2000,
|
|
149
|
-
heading: 0, pitch: 45)
|
|
150
|
-
)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Frame multiple annotations
|
|
156
|
-
|
|
157
|
-
```swift
|
|
158
|
-
func frameAllStores() {
|
|
159
|
-
withAnimation {
|
|
160
|
-
position = .automatic // Frames all map content
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Or frame a specific rect
|
|
165
|
-
func frameRegion(_ region: MKCoordinateRegion) {
|
|
166
|
-
withAnimation {
|
|
167
|
-
position = .region(region)
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### Read current camera position
|
|
173
|
-
|
|
174
|
-
Use `onMapCameraChange` to observe what the user is looking at:
|
|
175
|
-
|
|
176
|
-
```swift
|
|
177
|
-
@State private var visibleRegion: MKCoordinateRegion?
|
|
178
|
-
|
|
179
|
-
Map(position: $position) { ... }
|
|
180
|
-
.onMapCameraChange(frequency: .onEnd) { context in
|
|
181
|
-
visibleRegion = context.region
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
`frequency: .onEnd` fires after the user finishes scrolling. Use
|
|
186
|
-
`.continuous` only when you need live tracking (costs more CPU).
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
## Map Selection Handling
|
|
191
|
-
|
|
192
|
-
### Select markers by MKMapItem
|
|
193
|
-
|
|
194
|
-
Use `MKMapItem` as the selection type when you want to look up place details:
|
|
195
|
-
|
|
196
|
-
```swift
|
|
197
|
-
@State private var selectedItem: MKMapItem?
|
|
198
|
-
|
|
199
|
-
Map(selection: $selectedItem) {
|
|
200
|
-
ForEach(searchResults) { result in
|
|
201
|
-
Marker(item: result)
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
.onChange(of: selectedItem) { _, newItem in
|
|
205
|
-
guard let item = newItem else { return }
|
|
206
|
-
Task { await fetchLookAround(for: item) }
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Select by custom Identifiable tag
|
|
211
|
-
|
|
212
|
-
```swift
|
|
213
|
-
@State private var selectedPlaceID: Place.ID?
|
|
214
|
-
|
|
215
|
-
Map(selection: $selectedPlaceID) {
|
|
216
|
-
ForEach(places) { place in
|
|
217
|
-
Marker(place.name, coordinate: place.coordinate)
|
|
218
|
-
.tag(place.id)
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## Search with Autocomplete
|
|
226
|
-
|
|
227
|
-
Full pattern: completer feeds suggestions, selecting a suggestion triggers a
|
|
228
|
-
full search that returns `MKMapItem` results.
|
|
229
|
-
|
|
230
|
-
```swift
|
|
231
|
-
@Observable
|
|
232
|
-
final class MapSearchService: NSObject, MKLocalSearchCompleterDelegate {
|
|
233
|
-
var completions: [MKLocalSearchCompletion] = []
|
|
234
|
-
var searchResults: [MKMapItem] = []
|
|
235
|
-
var queryFragment: String = "" {
|
|
236
|
-
didSet { completer.queryFragment = queryFragment }
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
private let completer: MKLocalSearchCompleter
|
|
240
|
-
|
|
241
|
-
override init() {
|
|
242
|
-
completer = MKLocalSearchCompleter()
|
|
243
|
-
super.init()
|
|
244
|
-
completer.delegate = self
|
|
245
|
-
completer.resultTypes = [.address, .pointOfInterest]
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Restrict suggestions to the visible map region
|
|
249
|
-
func updateRegion(_ region: MKCoordinateRegion) {
|
|
250
|
-
completer.region = region
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
|
|
254
|
-
completions = completer.results
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
|
|
258
|
-
completions = []
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
func select(_ completion: MKLocalSearchCompletion) async {
|
|
262
|
-
let request = MKLocalSearch.Request(completion: completion)
|
|
263
|
-
request.resultTypes = [.pointOfInterest, .address]
|
|
264
|
-
do {
|
|
265
|
-
let response = try await MKLocalSearch(request: request).start()
|
|
266
|
-
searchResults = response.mapItems
|
|
267
|
-
} catch {
|
|
268
|
-
searchResults = []
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
### Search View Integration
|
|
275
|
-
|
|
276
|
-
```swift
|
|
277
|
-
struct MapSearchView: View {
|
|
278
|
-
@State private var searchService = MapSearchService()
|
|
279
|
-
@State private var searchText = ""
|
|
280
|
-
@State private var position: MapCameraPosition = .automatic
|
|
281
|
-
|
|
282
|
-
var body: some View {
|
|
283
|
-
Map(position: $position) {
|
|
284
|
-
ForEach(searchService.searchResults, id: \.self) { item in
|
|
285
|
-
Marker(item: item)
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
.searchable(text: $searchText, prompt: "Search places")
|
|
289
|
-
.searchSuggestions {
|
|
290
|
-
ForEach(searchService.completions, id: \.self) { completion in
|
|
291
|
-
Button {
|
|
292
|
-
Task { await searchService.select(completion) }
|
|
293
|
-
} label: {
|
|
294
|
-
VStack(alignment: .leading) {
|
|
295
|
-
Text(completion.title)
|
|
296
|
-
Text(completion.subtitle)
|
|
297
|
-
.font(.caption)
|
|
298
|
-
.foregroundStyle(.secondary)
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
.task(id: searchText) {
|
|
304
|
-
try? await Task.sleep(for: .milliseconds(300))
|
|
305
|
-
guard !Task.isCancelled else { return }
|
|
306
|
-
searchService.queryFragment = searchText
|
|
307
|
-
}
|
|
308
|
-
.onMapCameraChange(frequency: .onEnd) { context in
|
|
309
|
-
searchService.updateRegion(context.region)
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
---
|
|
316
|
-
|
|
317
|
-
## Route Display
|
|
318
|
-
|
|
319
|
-
Calculate directions and draw the route polyline on the map.
|
|
320
|
-
|
|
321
|
-
```swift
|
|
322
|
-
struct DirectionsMapView: View {
|
|
323
|
-
let source: MKMapItem
|
|
324
|
-
let destination: MKMapItem
|
|
325
|
-
@State private var route: MKRoute?
|
|
326
|
-
@State private var position: MapCameraPosition = .automatic
|
|
327
|
-
@State private var travelTime: String = ""
|
|
328
|
-
|
|
329
|
-
var body: some View {
|
|
330
|
-
Map(position: $position) {
|
|
331
|
-
if let route {
|
|
332
|
-
MapPolyline(route.polyline)
|
|
333
|
-
.stroke(.blue, lineWidth: 6)
|
|
334
|
-
}
|
|
335
|
-
Marker(item: source)
|
|
336
|
-
.tint(.green)
|
|
337
|
-
Marker(item: destination)
|
|
338
|
-
.tint(.red)
|
|
339
|
-
}
|
|
340
|
-
.overlay(alignment: .top) {
|
|
341
|
-
if !travelTime.isEmpty {
|
|
342
|
-
Text(travelTime)
|
|
343
|
-
.font(.caption)
|
|
344
|
-
.padding(8)
|
|
345
|
-
.background(.ultraThinMaterial, in: .capsule)
|
|
346
|
-
.padding(.top, 8)
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
.task { await calculateRoute() }
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
private func calculateRoute() async {
|
|
353
|
-
let request = MKDirections.Request()
|
|
354
|
-
request.source = source
|
|
355
|
-
request.destination = destination
|
|
356
|
-
request.transportType = .automobile
|
|
357
|
-
|
|
358
|
-
do {
|
|
359
|
-
let response = try await MKDirections(request: request).calculate()
|
|
360
|
-
route = response.routes.first
|
|
361
|
-
if let route {
|
|
362
|
-
let formatter = DateComponentsFormatter()
|
|
363
|
-
formatter.unitsStyle = .abbreviated
|
|
364
|
-
formatter.allowedUnits = [.hour, .minute]
|
|
365
|
-
travelTime = formatter.string(from: route.expectedTravelTime) ?? ""
|
|
366
|
-
|
|
367
|
-
withAnimation {
|
|
368
|
-
position = .rect(route.polyline.boundingMapRect)
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
} catch {
|
|
372
|
-
print("Directions error: \(error.localizedDescription)")
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
### Multiple Route Options
|
|
379
|
-
|
|
380
|
-
```swift
|
|
381
|
-
request.requestsAlternateRoutes = true
|
|
382
|
-
let response = try await MKDirections(request: request).calculate()
|
|
383
|
-
|
|
384
|
-
// response.routes contains multiple route options
|
|
385
|
-
// Display all routes, highlight the selected one:
|
|
386
|
-
ForEach(Array(response.routes.enumerated()), id: \.offset) { index, route in
|
|
387
|
-
MapPolyline(route.polyline)
|
|
388
|
-
.stroke(index == 0 ? .blue : .gray.opacity(0.5), lineWidth: index == 0 ? 6 : 3)
|
|
389
|
-
}
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
---
|
|
393
|
-
|
|
394
|
-
## Look Around Preview
|
|
395
|
-
|
|
396
|
-
Show Apple's street-level imagery for a selected location. Availability
|
|
397
|
-
depends on region coverage.
|
|
398
|
-
|
|
399
|
-
```swift
|
|
400
|
-
struct LookAroundView: View {
|
|
401
|
-
let mapItem: MKMapItem
|
|
402
|
-
@State private var scene: MKLookAroundScene?
|
|
403
|
-
|
|
404
|
-
var body: some View {
|
|
405
|
-
Group {
|
|
406
|
-
if let scene {
|
|
407
|
-
LookAroundPreview(scene: .constant(scene))
|
|
408
|
-
.frame(height: 200)
|
|
409
|
-
.clipShape(.rect(cornerRadius: 12))
|
|
410
|
-
} else {
|
|
411
|
-
ContentUnavailableView("No Look Around",
|
|
412
|
-
systemImage: "eye.slash",
|
|
413
|
-
description: Text("Look Around is not available here."))
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
.task(id: mapItem) {
|
|
417
|
-
scene = nil
|
|
418
|
-
let request = MKLookAroundSceneRequest(mapItem: mapItem)
|
|
419
|
-
scene = try? await request.scene
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
### Look Around overlay on a Map
|
|
426
|
-
|
|
427
|
-
```swift
|
|
428
|
-
Map(selection: $selectedItem) { ... }
|
|
429
|
-
.overlay(alignment: .bottomTrailing) {
|
|
430
|
-
if lookAroundScene != nil {
|
|
431
|
-
LookAroundPreview(scene: $lookAroundScene)
|
|
432
|
-
.frame(width: 200, height: 130)
|
|
433
|
-
.clipShape(.rect(cornerRadius: 10))
|
|
434
|
-
.padding()
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
.onChange(of: selectedItem) { _, newItem in
|
|
438
|
-
guard let item = newItem else {
|
|
439
|
-
lookAroundScene = nil
|
|
440
|
-
return
|
|
441
|
-
}
|
|
442
|
-
Task {
|
|
443
|
-
let request = MKLookAroundSceneRequest(mapItem: item)
|
|
444
|
-
lookAroundScene = try? await request.scene
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
---
|
|
450
|
-
|
|
451
|
-
## Map Snapshots (MKMapSnapshotter)
|
|
452
|
-
|
|
453
|
-
Generate a static image of a map region. Useful for share sheets, widgets,
|
|
454
|
-
notifications, or thumbnails.
|
|
455
|
-
|
|
456
|
-
```swift
|
|
457
|
-
func generateMapSnapshot(center: CLLocationCoordinate2D,
|
|
458
|
-
size: CGSize = CGSize(width: 300, height: 200)) async throws -> UIImage {
|
|
459
|
-
let options = MKMapSnapshotter.Options()
|
|
460
|
-
options.region = MKCoordinateRegion(
|
|
461
|
-
center: center,
|
|
462
|
-
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
|
|
463
|
-
)
|
|
464
|
-
options.size = size
|
|
465
|
-
options.mapType = .standard
|
|
466
|
-
options.showsBuildings = true
|
|
467
|
-
|
|
468
|
-
let snapshotter = MKMapSnapshotter(options: options)
|
|
469
|
-
let snapshot = try await snapshotter.start()
|
|
470
|
-
|
|
471
|
-
// Draw a pin at the center
|
|
472
|
-
let image = UIGraphicsImageRenderer(size: size).image { context in
|
|
473
|
-
snapshot.image.draw(at: .zero)
|
|
474
|
-
let point = snapshot.point(for: center)
|
|
475
|
-
let pin = UIImage(systemName: "mappin.circle.fill")?
|
|
476
|
-
.withTintColor(.red, renderingMode: .alwaysOriginal)
|
|
477
|
-
pin?.draw(at: CGPoint(x: point.x - 15, y: point.y - 30),
|
|
478
|
-
blendMode: .normal, alpha: 1.0)
|
|
479
|
-
}
|
|
480
|
-
return image
|
|
481
|
-
}
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
## Clustering Annotations
|
|
487
|
-
|
|
488
|
-
MapKit clusters `Marker` views automatically when they overlap. Control the
|
|
489
|
-
clustering priority and displayed count.
|
|
490
|
-
|
|
491
|
-
```swift
|
|
492
|
-
Map {
|
|
493
|
-
ForEach(allStores) { store in
|
|
494
|
-
Marker(store.name, systemImage: "cart", coordinate: store.coordinate)
|
|
495
|
-
.annotationTitles(.hidden) // Hide titles at low zoom
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
.mapStyle(.standard(pointsOfInterest: .excludingAll))
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
For custom clustering behavior with `MKMapView` (UIKit interop), set
|
|
502
|
-
`clusteringIdentifier` on `MKMarkerAnnotationView`. The SwiftUI `Map`
|
|
503
|
-
handles basic clustering automatically.
|
|
504
|
-
|
|
505
|
-
---
|
|
506
|
-
|
|
507
|
-
## MapKit with MKMapItem Utilities
|
|
508
|
-
|
|
509
|
-
### Open in Apple Maps
|
|
510
|
-
|
|
511
|
-
```swift
|
|
512
|
-
let item = MKMapItem(placemark: MKPlacemark(coordinate: coordinate))
|
|
513
|
-
item.name = "Destination"
|
|
514
|
-
item.openInMaps(launchOptions: [
|
|
515
|
-
MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving
|
|
516
|
-
])
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
### Distance Calculation
|
|
520
|
-
|
|
521
|
-
```swift
|
|
522
|
-
func distanceBetween(_ a: CLLocationCoordinate2D,
|
|
523
|
-
_ b: CLLocationCoordinate2D) -> CLLocationDistance {
|
|
524
|
-
let locA = CLLocation(latitude: a.latitude, longitude: a.longitude)
|
|
525
|
-
let locB = CLLocation(latitude: b.latitude, longitude: b.longitude)
|
|
526
|
-
return locA.distance(from: locB) // meters
|
|
527
|
-
}
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
### Format Distance for Display
|
|
531
|
-
|
|
532
|
-
```swift
|
|
533
|
-
let formatter = MKDistanceFormatter()
|
|
534
|
-
formatter.unitStyle = .abbreviated
|
|
535
|
-
let text = formatter.string(fromDistance: 1500) // "0.9 mi" or "1.5 km"
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
---
|
|
539
|
-
|
|
540
|
-
## iOS 26 New APIs
|
|
541
|
-
|
|
542
|
-
### MKGeocodingRequest
|
|
543
|
-
|
|
544
|
-
Convert an address string to map items with richer data than `CLGeocoder`:
|
|
545
|
-
|
|
546
|
-
```swift
|
|
547
|
-
@available(iOS 26, *)
|
|
548
|
-
func geocodeAddresses(_ addresses: [String]) async -> [MKMapItem] {
|
|
549
|
-
var items: [MKMapItem] = []
|
|
550
|
-
for address in addresses {
|
|
551
|
-
let request = MKGeocodingRequest(address: address)
|
|
552
|
-
if let mapItems = try? await request.mapItems {
|
|
553
|
-
items.append(contentsOf: mapItems)
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
return items
|
|
557
|
-
}
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
### MKReverseGeocodingRequest
|
|
561
|
-
|
|
562
|
-
Convert coordinates to map items with `MKAddress`:
|
|
563
|
-
|
|
564
|
-
```swift
|
|
565
|
-
@available(iOS 26, *)
|
|
566
|
-
func reverseGeocode(_ coordinate: CLLocationCoordinate2D) async -> MKAddress? {
|
|
567
|
-
let location = CLLocation(latitude: coordinate.latitude,
|
|
568
|
-
longitude: coordinate.longitude)
|
|
569
|
-
guard let request = MKReverseGeocodingRequest(location: location) else {
|
|
570
|
-
return nil
|
|
571
|
-
}
|
|
572
|
-
let mapItems = try? await request.mapItems
|
|
573
|
-
return mapItems?.first?.address
|
|
574
|
-
}
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
### MKAddress and MKAddressRepresentations
|
|
578
|
-
|
|
579
|
-
`MKAddress` provides structured address components. Use
|
|
580
|
-
`MKAddressRepresentations` to format addresses for different contexts:
|
|
581
|
-
|
|
582
|
-
```swift
|
|
583
|
-
@available(iOS 26, *)
|
|
584
|
-
func formatAddress(_ address: MKAddress) -> String {
|
|
585
|
-
// Use address representations for locale-aware formatting
|
|
586
|
-
return address.representations.fullAddress()
|
|
587
|
-
}
|
|
588
|
-
```
|
|
589
|
-
|
|
590
|
-
### PlaceDescriptor (via GeoToolbox)
|
|
591
|
-
|
|
592
|
-
Create place references from coordinates when you do not have a Place ID:
|
|
593
|
-
|
|
594
|
-
```swift
|
|
595
|
-
@available(iOS 26, *)
|
|
596
|
-
import GeoToolbox
|
|
597
|
-
|
|
598
|
-
let descriptor = PlaceDescriptor(
|
|
599
|
-
representations: [.coordinate(myCoordinate)],
|
|
600
|
-
commonName: "My Favorite Cafe"
|
|
601
|
-
)
|
|
602
|
-
|
|
603
|
-
let request = MKMapItemRequest(placeDescriptor: descriptor)
|
|
604
|
-
let mapItem = try await request.mapItem
|
|
605
|
-
|
|
606
|
-
// Use mapItem with any MapKit API: Marker(item:), directions, place cards
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
### Cycling Directions
|
|
610
|
-
|
|
611
|
-
```swift
|
|
612
|
-
@available(iOS 26, *)
|
|
613
|
-
func cyclingRoute(to destination: MKMapItem) async throws -> MKRoute? {
|
|
614
|
-
let request = MKDirections.Request()
|
|
615
|
-
request.source = .forCurrentLocation()
|
|
616
|
-
request.destination = destination
|
|
617
|
-
request.transportType = .cycling
|
|
618
|
-
let response = try await MKDirections(request: request).calculate()
|
|
619
|
-
return response.routes.first
|
|
620
|
-
}
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
---
|
|
624
|
-
|
|
625
|
-
## User Location Display
|
|
626
|
-
|
|
627
|
-
Show the user's position with the built-in blue dot:
|
|
628
|
-
|
|
629
|
-
```swift
|
|
630
|
-
Map(position: $position) {
|
|
631
|
-
UserAnnotation() // Blue dot with accuracy ring
|
|
632
|
-
// ... other content
|
|
633
|
-
}
|
|
634
|
-
.mapControls {
|
|
635
|
-
MapUserLocationButton() // Button to re-center on user
|
|
636
|
-
}
|
|
637
|
-
```
|
|
638
|
-
|
|
639
|
-
`UserAnnotation()` requires location authorization. If authorization is
|
|
640
|
-
denied, the annotation does not appear and no error is thrown.
|
|
641
|
-
|
|
642
|
-
---
|
|
643
|
-
|
|
644
|
-
## Map in a List or ScrollView
|
|
645
|
-
|
|
646
|
-
When embedding a `Map` inside a `ScrollView`, disable map gestures that
|
|
647
|
-
conflict with scrolling:
|
|
648
|
-
|
|
649
|
-
```swift
|
|
650
|
-
ScrollView {
|
|
651
|
-
Map(position: $position, interactionModes: []) {
|
|
652
|
-
Marker("Location", coordinate: coord)
|
|
653
|
-
}
|
|
654
|
-
.frame(height: 200)
|
|
655
|
-
.clipShape(.rect(cornerRadius: 12))
|
|
656
|
-
|
|
657
|
-
Text("Details below the map...")
|
|
658
|
-
}
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
Use `interactionModes: []` for a fully static map thumbnail or
|
|
662
|
-
`interactionModes: [.zoom]` to allow pinch-to-zoom without pan conflicts.
|
|
663
|
-
|
|
664
|
-
---
|
|
665
|
-
|
|
666
|
-
## Coordinate Utilities
|
|
667
|
-
|
|
668
|
-
### Region from an array of coordinates
|
|
669
|
-
|
|
670
|
-
```swift
|
|
671
|
-
func regionForCoordinates(_ coords: [CLLocationCoordinate2D]) -> MKCoordinateRegion {
|
|
672
|
-
guard !coords.isEmpty else {
|
|
673
|
-
return MKCoordinateRegion()
|
|
674
|
-
}
|
|
675
|
-
var minLat = coords[0].latitude
|
|
676
|
-
var maxLat = coords[0].latitude
|
|
677
|
-
var minLon = coords[0].longitude
|
|
678
|
-
var maxLon = coords[0].longitude
|
|
679
|
-
|
|
680
|
-
for coord in coords {
|
|
681
|
-
minLat = min(minLat, coord.latitude)
|
|
682
|
-
maxLat = max(maxLat, coord.latitude)
|
|
683
|
-
minLon = min(minLon, coord.longitude)
|
|
684
|
-
maxLon = max(maxLon, coord.longitude)
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
let center = CLLocationCoordinate2D(
|
|
688
|
-
latitude: (minLat + maxLat) / 2,
|
|
689
|
-
longitude: (minLon + maxLon) / 2
|
|
690
|
-
)
|
|
691
|
-
let span = MKCoordinateSpan(
|
|
692
|
-
latitudeDelta: (maxLat - minLat) * 1.3, // 30% padding
|
|
693
|
-
longitudeDelta: (maxLon - minLon) * 1.3
|
|
694
|
-
)
|
|
695
|
-
return MKCoordinateRegion(center: center, span: span)
|
|
696
|
-
}
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
### CLLocationCoordinate2D Equatable conformance
|
|
700
|
-
|
|
701
|
-
`CLLocationCoordinate2D` does not conform to `Equatable` by default.
|
|
702
|
-
Extend it when needed for comparisons:
|
|
703
|
-
|
|
704
|
-
```swift
|
|
705
|
-
extension CLLocationCoordinate2D: @retroactive Equatable {
|
|
706
|
-
public static func == (lhs: Self, rhs: Self) -> Bool {
|
|
707
|
-
lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
Note: Use `@retroactive` (Swift 5.10+) to silence the warning about
|
|
713
|
-
conforming types from other modules.
|
|
714
|
-
|
|
715
|
-
---
|
|
716
|
-
|
|
717
|
-
## Accessibility
|
|
718
|
-
|
|
719
|
-
### Marker accessibility
|
|
720
|
-
|
|
721
|
-
`Marker` views include built-in VoiceOver support using the title string.
|
|
722
|
-
Add `.accessibilityLabel` for richer descriptions:
|
|
723
|
-
|
|
724
|
-
```swift
|
|
725
|
-
Marker(store.name, coordinate: store.coordinate)
|
|
726
|
-
.accessibilityLabel("\(store.name), \(store.distanceText) away")
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
### Map accessibility
|
|
730
|
-
|
|
731
|
-
Add a concise description of the map purpose:
|
|
732
|
-
|
|
733
|
-
```swift
|
|
734
|
-
Map { ... }
|
|
735
|
-
.accessibilityElement()
|
|
736
|
-
.accessibilityLabel("Store locations map showing \(stores.count) stores")
|
|
737
|
-
```
|
|
738
|
-
|
|
739
|
-
---
|
|
740
|
-
|
|
741
|
-
## References
|
|
742
|
-
|
|
743
|
-
- Apple docs: [MapKit for SwiftUI](https://sosumi.ai/documentation/MapKit/MapKit-for-SwiftUI)
|
|
744
|
-
- Apple docs: [Map](https://sosumi.ai/documentation/MapKit/Map)
|
|
745
|
-
- Apple docs: [MapCameraPosition](https://sosumi.ai/documentation/MapKit/MapCameraPosition)
|
|
746
|
-
- Apple docs: [MKLocalSearch](https://sosumi.ai/documentation/MapKit/MKLocalSearch)
|
|
747
|
-
- Apple docs: [MKDirections](https://sosumi.ai/documentation/MapKit/MKDirections)
|
|
748
|
-
- Apple docs: [MKLookAroundScene](https://sosumi.ai/documentation/MapKit/MKLookAroundScene)
|
|
1
|
+
# MapKit Patterns Reference
|
|
2
|
+
|
|
3
|
+
Extended patterns for MapKit on iOS 17+ with SwiftUI. Import `MapKit` and
|
|
4
|
+
`SwiftUI` in every file that uses these APIs.
|
|
5
|
+
|
|
6
|
+
```swift
|
|
7
|
+
import MapKit
|
|
8
|
+
import SwiftUI
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Contents
|
|
14
|
+
|
|
15
|
+
- [Complete Map View Setup](#complete-map-view-setup)
|
|
16
|
+
- [Custom Annotation Views](#custom-annotation-views)
|
|
17
|
+
- [Camera Control (MapCameraPosition)](#camera-control-mapcameraposition)
|
|
18
|
+
- [Map Selection Handling](#map-selection-handling)
|
|
19
|
+
- [Search with Autocomplete](#search-with-autocomplete)
|
|
20
|
+
- [Route Display](#route-display)
|
|
21
|
+
- [Look Around Preview](#look-around-preview)
|
|
22
|
+
- [Map Snapshots (MKMapSnapshotter)](#map-snapshots-mkmapsnapshotter)
|
|
23
|
+
- [Clustering Annotations](#clustering-annotations)
|
|
24
|
+
- [MapKit with MKMapItem Utilities](#mapkit-with-mkmapitem-utilities)
|
|
25
|
+
- [iOS 26 New APIs](#ios-26-new-apis)
|
|
26
|
+
- [User Location Display](#user-location-display)
|
|
27
|
+
- [Map in a List or ScrollView](#map-in-a-list-or-scrollview)
|
|
28
|
+
- [Coordinate Utilities](#coordinate-utilities)
|
|
29
|
+
- [Accessibility](#accessibility)
|
|
30
|
+
- [References](#references)
|
|
31
|
+
|
|
32
|
+
## Complete Map View Setup
|
|
33
|
+
|
|
34
|
+
A production-ready map view with markers, user location, and controls.
|
|
35
|
+
|
|
36
|
+
```swift
|
|
37
|
+
struct StoreLocatorMap: View {
|
|
38
|
+
let stores: [Store]
|
|
39
|
+
@State private var position: MapCameraPosition = .automatic
|
|
40
|
+
@State private var selectedStore: Store?
|
|
41
|
+
|
|
42
|
+
var body: some View {
|
|
43
|
+
Map(position: $position, selection: $selectedStore) {
|
|
44
|
+
UserAnnotation()
|
|
45
|
+
|
|
46
|
+
ForEach(stores) { store in
|
|
47
|
+
Marker(store.name, systemImage: "storefront",
|
|
48
|
+
coordinate: store.coordinate)
|
|
49
|
+
.tint(store.isOpen ? .green : .gray)
|
|
50
|
+
.tag(store)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
.mapStyle(.standard(pointsOfInterest: .excludingAll))
|
|
54
|
+
.mapControls {
|
|
55
|
+
MapUserLocationButton()
|
|
56
|
+
MapCompass()
|
|
57
|
+
MapScaleView()
|
|
58
|
+
MapPitchToggle()
|
|
59
|
+
}
|
|
60
|
+
.safeAreaInset(edge: .bottom) {
|
|
61
|
+
if let store = selectedStore {
|
|
62
|
+
StoreDetailCard(store: store)
|
|
63
|
+
.padding()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Conform the data model to `Hashable` for use with map selection:
|
|
71
|
+
|
|
72
|
+
```swift
|
|
73
|
+
struct Store: Identifiable, Hashable {
|
|
74
|
+
let id: UUID
|
|
75
|
+
let name: String
|
|
76
|
+
let coordinate: CLLocationCoordinate2D
|
|
77
|
+
let isOpen: Bool
|
|
78
|
+
|
|
79
|
+
static func == (lhs: Store, rhs: Store) -> Bool { lhs.id == rhs.id }
|
|
80
|
+
func hash(into hasher: inout Hasher) { hasher.combine(id) }
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Custom Annotation Views
|
|
87
|
+
|
|
88
|
+
Use `Annotation` for fully custom SwiftUI content at a coordinate. Prefer
|
|
89
|
+
`Marker` for standard pins because it handles clustering and accessibility
|
|
90
|
+
automatically.
|
|
91
|
+
|
|
92
|
+
```swift
|
|
93
|
+
Map {
|
|
94
|
+
ForEach(friends) { friend in
|
|
95
|
+
Annotation(friend.name, coordinate: friend.coordinate, anchor: .bottom) {
|
|
96
|
+
VStack(spacing: 0) {
|
|
97
|
+
AsyncImage(url: friend.avatarURL) { image in
|
|
98
|
+
image.resizable().scaledToFill()
|
|
99
|
+
} placeholder: {
|
|
100
|
+
Color.gray
|
|
101
|
+
}
|
|
102
|
+
.frame(width: 40, height: 40)
|
|
103
|
+
.clipShape(.circle)
|
|
104
|
+
.overlay(Circle().stroke(.white, lineWidth: 2))
|
|
105
|
+
|
|
106
|
+
Image(systemName: "triangle.fill")
|
|
107
|
+
.font(.caption2)
|
|
108
|
+
.foregroundStyle(.white)
|
|
109
|
+
.rotationEffect(.degrees(180))
|
|
110
|
+
.offset(y: -3)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Annotation with anchorOffset for callout-style layout
|
|
118
|
+
|
|
119
|
+
```swift
|
|
120
|
+
Annotation(place.name, coordinate: place.coordinate, anchor: .bottom) {
|
|
121
|
+
VStack(spacing: 2) {
|
|
122
|
+
Text(place.name)
|
|
123
|
+
.font(.caption)
|
|
124
|
+
.fontWeight(.semibold)
|
|
125
|
+
.padding(.horizontal, 8)
|
|
126
|
+
.padding(.vertical, 4)
|
|
127
|
+
.background(.ultraThinMaterial, in: .capsule)
|
|
128
|
+
|
|
129
|
+
Image(systemName: "mappin.circle.fill")
|
|
130
|
+
.font(.title)
|
|
131
|
+
.foregroundStyle(.red)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Camera Control (MapCameraPosition)
|
|
139
|
+
|
|
140
|
+
### Animate camera changes
|
|
141
|
+
|
|
142
|
+
Wrap position updates in `withAnimation` for smooth transitions:
|
|
143
|
+
|
|
144
|
+
```swift
|
|
145
|
+
func flyTo(_ coordinate: CLLocationCoordinate2D) {
|
|
146
|
+
withAnimation(.easeInOut(duration: 1.0)) {
|
|
147
|
+
position = .camera(
|
|
148
|
+
MapCamera(centerCoordinate: coordinate, distance: 2000,
|
|
149
|
+
heading: 0, pitch: 45)
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Frame multiple annotations
|
|
156
|
+
|
|
157
|
+
```swift
|
|
158
|
+
func frameAllStores() {
|
|
159
|
+
withAnimation {
|
|
160
|
+
position = .automatic // Frames all map content
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Or frame a specific rect
|
|
165
|
+
func frameRegion(_ region: MKCoordinateRegion) {
|
|
166
|
+
withAnimation {
|
|
167
|
+
position = .region(region)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Read current camera position
|
|
173
|
+
|
|
174
|
+
Use `onMapCameraChange` to observe what the user is looking at:
|
|
175
|
+
|
|
176
|
+
```swift
|
|
177
|
+
@State private var visibleRegion: MKCoordinateRegion?
|
|
178
|
+
|
|
179
|
+
Map(position: $position) { ... }
|
|
180
|
+
.onMapCameraChange(frequency: .onEnd) { context in
|
|
181
|
+
visibleRegion = context.region
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`frequency: .onEnd` fires after the user finishes scrolling. Use
|
|
186
|
+
`.continuous` only when you need live tracking (costs more CPU).
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Map Selection Handling
|
|
191
|
+
|
|
192
|
+
### Select markers by MKMapItem
|
|
193
|
+
|
|
194
|
+
Use `MKMapItem` as the selection type when you want to look up place details:
|
|
195
|
+
|
|
196
|
+
```swift
|
|
197
|
+
@State private var selectedItem: MKMapItem?
|
|
198
|
+
|
|
199
|
+
Map(selection: $selectedItem) {
|
|
200
|
+
ForEach(searchResults) { result in
|
|
201
|
+
Marker(item: result)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
.onChange(of: selectedItem) { _, newItem in
|
|
205
|
+
guard let item = newItem else { return }
|
|
206
|
+
Task { await fetchLookAround(for: item) }
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Select by custom Identifiable tag
|
|
211
|
+
|
|
212
|
+
```swift
|
|
213
|
+
@State private var selectedPlaceID: Place.ID?
|
|
214
|
+
|
|
215
|
+
Map(selection: $selectedPlaceID) {
|
|
216
|
+
ForEach(places) { place in
|
|
217
|
+
Marker(place.name, coordinate: place.coordinate)
|
|
218
|
+
.tag(place.id)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Search with Autocomplete
|
|
226
|
+
|
|
227
|
+
Full pattern: completer feeds suggestions, selecting a suggestion triggers a
|
|
228
|
+
full search that returns `MKMapItem` results.
|
|
229
|
+
|
|
230
|
+
```swift
|
|
231
|
+
@Observable
|
|
232
|
+
final class MapSearchService: NSObject, MKLocalSearchCompleterDelegate {
|
|
233
|
+
var completions: [MKLocalSearchCompletion] = []
|
|
234
|
+
var searchResults: [MKMapItem] = []
|
|
235
|
+
var queryFragment: String = "" {
|
|
236
|
+
didSet { completer.queryFragment = queryFragment }
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private let completer: MKLocalSearchCompleter
|
|
240
|
+
|
|
241
|
+
override init() {
|
|
242
|
+
completer = MKLocalSearchCompleter()
|
|
243
|
+
super.init()
|
|
244
|
+
completer.delegate = self
|
|
245
|
+
completer.resultTypes = [.address, .pointOfInterest]
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Restrict suggestions to the visible map region
|
|
249
|
+
func updateRegion(_ region: MKCoordinateRegion) {
|
|
250
|
+
completer.region = region
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
|
|
254
|
+
completions = completer.results
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
|
|
258
|
+
completions = []
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
func select(_ completion: MKLocalSearchCompletion) async {
|
|
262
|
+
let request = MKLocalSearch.Request(completion: completion)
|
|
263
|
+
request.resultTypes = [.pointOfInterest, .address]
|
|
264
|
+
do {
|
|
265
|
+
let response = try await MKLocalSearch(request: request).start()
|
|
266
|
+
searchResults = response.mapItems
|
|
267
|
+
} catch {
|
|
268
|
+
searchResults = []
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Search View Integration
|
|
275
|
+
|
|
276
|
+
```swift
|
|
277
|
+
struct MapSearchView: View {
|
|
278
|
+
@State private var searchService = MapSearchService()
|
|
279
|
+
@State private var searchText = ""
|
|
280
|
+
@State private var position: MapCameraPosition = .automatic
|
|
281
|
+
|
|
282
|
+
var body: some View {
|
|
283
|
+
Map(position: $position) {
|
|
284
|
+
ForEach(searchService.searchResults, id: \.self) { item in
|
|
285
|
+
Marker(item: item)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
.searchable(text: $searchText, prompt: "Search places")
|
|
289
|
+
.searchSuggestions {
|
|
290
|
+
ForEach(searchService.completions, id: \.self) { completion in
|
|
291
|
+
Button {
|
|
292
|
+
Task { await searchService.select(completion) }
|
|
293
|
+
} label: {
|
|
294
|
+
VStack(alignment: .leading) {
|
|
295
|
+
Text(completion.title)
|
|
296
|
+
Text(completion.subtitle)
|
|
297
|
+
.font(.caption)
|
|
298
|
+
.foregroundStyle(.secondary)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
.task(id: searchText) {
|
|
304
|
+
try? await Task.sleep(for: .milliseconds(300))
|
|
305
|
+
guard !Task.isCancelled else { return }
|
|
306
|
+
searchService.queryFragment = searchText
|
|
307
|
+
}
|
|
308
|
+
.onMapCameraChange(frequency: .onEnd) { context in
|
|
309
|
+
searchService.updateRegion(context.region)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Route Display
|
|
318
|
+
|
|
319
|
+
Calculate directions and draw the route polyline on the map.
|
|
320
|
+
|
|
321
|
+
```swift
|
|
322
|
+
struct DirectionsMapView: View {
|
|
323
|
+
let source: MKMapItem
|
|
324
|
+
let destination: MKMapItem
|
|
325
|
+
@State private var route: MKRoute?
|
|
326
|
+
@State private var position: MapCameraPosition = .automatic
|
|
327
|
+
@State private var travelTime: String = ""
|
|
328
|
+
|
|
329
|
+
var body: some View {
|
|
330
|
+
Map(position: $position) {
|
|
331
|
+
if let route {
|
|
332
|
+
MapPolyline(route.polyline)
|
|
333
|
+
.stroke(.blue, lineWidth: 6)
|
|
334
|
+
}
|
|
335
|
+
Marker(item: source)
|
|
336
|
+
.tint(.green)
|
|
337
|
+
Marker(item: destination)
|
|
338
|
+
.tint(.red)
|
|
339
|
+
}
|
|
340
|
+
.overlay(alignment: .top) {
|
|
341
|
+
if !travelTime.isEmpty {
|
|
342
|
+
Text(travelTime)
|
|
343
|
+
.font(.caption)
|
|
344
|
+
.padding(8)
|
|
345
|
+
.background(.ultraThinMaterial, in: .capsule)
|
|
346
|
+
.padding(.top, 8)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
.task { await calculateRoute() }
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private func calculateRoute() async {
|
|
353
|
+
let request = MKDirections.Request()
|
|
354
|
+
request.source = source
|
|
355
|
+
request.destination = destination
|
|
356
|
+
request.transportType = .automobile
|
|
357
|
+
|
|
358
|
+
do {
|
|
359
|
+
let response = try await MKDirections(request: request).calculate()
|
|
360
|
+
route = response.routes.first
|
|
361
|
+
if let route {
|
|
362
|
+
let formatter = DateComponentsFormatter()
|
|
363
|
+
formatter.unitsStyle = .abbreviated
|
|
364
|
+
formatter.allowedUnits = [.hour, .minute]
|
|
365
|
+
travelTime = formatter.string(from: route.expectedTravelTime) ?? ""
|
|
366
|
+
|
|
367
|
+
withAnimation {
|
|
368
|
+
position = .rect(route.polyline.boundingMapRect)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
} catch {
|
|
372
|
+
print("Directions error: \(error.localizedDescription)")
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Multiple Route Options
|
|
379
|
+
|
|
380
|
+
```swift
|
|
381
|
+
request.requestsAlternateRoutes = true
|
|
382
|
+
let response = try await MKDirections(request: request).calculate()
|
|
383
|
+
|
|
384
|
+
// response.routes contains multiple route options
|
|
385
|
+
// Display all routes, highlight the selected one:
|
|
386
|
+
ForEach(Array(response.routes.enumerated()), id: \.offset) { index, route in
|
|
387
|
+
MapPolyline(route.polyline)
|
|
388
|
+
.stroke(index == 0 ? .blue : .gray.opacity(0.5), lineWidth: index == 0 ? 6 : 3)
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Look Around Preview
|
|
395
|
+
|
|
396
|
+
Show Apple's street-level imagery for a selected location. Availability
|
|
397
|
+
depends on region coverage.
|
|
398
|
+
|
|
399
|
+
```swift
|
|
400
|
+
struct LookAroundView: View {
|
|
401
|
+
let mapItem: MKMapItem
|
|
402
|
+
@State private var scene: MKLookAroundScene?
|
|
403
|
+
|
|
404
|
+
var body: some View {
|
|
405
|
+
Group {
|
|
406
|
+
if let scene {
|
|
407
|
+
LookAroundPreview(scene: .constant(scene))
|
|
408
|
+
.frame(height: 200)
|
|
409
|
+
.clipShape(.rect(cornerRadius: 12))
|
|
410
|
+
} else {
|
|
411
|
+
ContentUnavailableView("No Look Around",
|
|
412
|
+
systemImage: "eye.slash",
|
|
413
|
+
description: Text("Look Around is not available here."))
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
.task(id: mapItem) {
|
|
417
|
+
scene = nil
|
|
418
|
+
let request = MKLookAroundSceneRequest(mapItem: mapItem)
|
|
419
|
+
scene = try? await request.scene
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Look Around overlay on a Map
|
|
426
|
+
|
|
427
|
+
```swift
|
|
428
|
+
Map(selection: $selectedItem) { ... }
|
|
429
|
+
.overlay(alignment: .bottomTrailing) {
|
|
430
|
+
if lookAroundScene != nil {
|
|
431
|
+
LookAroundPreview(scene: $lookAroundScene)
|
|
432
|
+
.frame(width: 200, height: 130)
|
|
433
|
+
.clipShape(.rect(cornerRadius: 10))
|
|
434
|
+
.padding()
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
.onChange(of: selectedItem) { _, newItem in
|
|
438
|
+
guard let item = newItem else {
|
|
439
|
+
lookAroundScene = nil
|
|
440
|
+
return
|
|
441
|
+
}
|
|
442
|
+
Task {
|
|
443
|
+
let request = MKLookAroundSceneRequest(mapItem: item)
|
|
444
|
+
lookAroundScene = try? await request.scene
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Map Snapshots (MKMapSnapshotter)
|
|
452
|
+
|
|
453
|
+
Generate a static image of a map region. Useful for share sheets, widgets,
|
|
454
|
+
notifications, or thumbnails.
|
|
455
|
+
|
|
456
|
+
```swift
|
|
457
|
+
func generateMapSnapshot(center: CLLocationCoordinate2D,
|
|
458
|
+
size: CGSize = CGSize(width: 300, height: 200)) async throws -> UIImage {
|
|
459
|
+
let options = MKMapSnapshotter.Options()
|
|
460
|
+
options.region = MKCoordinateRegion(
|
|
461
|
+
center: center,
|
|
462
|
+
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
|
|
463
|
+
)
|
|
464
|
+
options.size = size
|
|
465
|
+
options.mapType = .standard
|
|
466
|
+
options.showsBuildings = true
|
|
467
|
+
|
|
468
|
+
let snapshotter = MKMapSnapshotter(options: options)
|
|
469
|
+
let snapshot = try await snapshotter.start()
|
|
470
|
+
|
|
471
|
+
// Draw a pin at the center
|
|
472
|
+
let image = UIGraphicsImageRenderer(size: size).image { context in
|
|
473
|
+
snapshot.image.draw(at: .zero)
|
|
474
|
+
let point = snapshot.point(for: center)
|
|
475
|
+
let pin = UIImage(systemName: "mappin.circle.fill")?
|
|
476
|
+
.withTintColor(.red, renderingMode: .alwaysOriginal)
|
|
477
|
+
pin?.draw(at: CGPoint(x: point.x - 15, y: point.y - 30),
|
|
478
|
+
blendMode: .normal, alpha: 1.0)
|
|
479
|
+
}
|
|
480
|
+
return image
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## Clustering Annotations
|
|
487
|
+
|
|
488
|
+
MapKit clusters `Marker` views automatically when they overlap. Control the
|
|
489
|
+
clustering priority and displayed count.
|
|
490
|
+
|
|
491
|
+
```swift
|
|
492
|
+
Map {
|
|
493
|
+
ForEach(allStores) { store in
|
|
494
|
+
Marker(store.name, systemImage: "cart", coordinate: store.coordinate)
|
|
495
|
+
.annotationTitles(.hidden) // Hide titles at low zoom
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
.mapStyle(.standard(pointsOfInterest: .excludingAll))
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
For custom clustering behavior with `MKMapView` (UIKit interop), set
|
|
502
|
+
`clusteringIdentifier` on `MKMarkerAnnotationView`. The SwiftUI `Map`
|
|
503
|
+
handles basic clustering automatically.
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## MapKit with MKMapItem Utilities
|
|
508
|
+
|
|
509
|
+
### Open in Apple Maps
|
|
510
|
+
|
|
511
|
+
```swift
|
|
512
|
+
let item = MKMapItem(placemark: MKPlacemark(coordinate: coordinate))
|
|
513
|
+
item.name = "Destination"
|
|
514
|
+
item.openInMaps(launchOptions: [
|
|
515
|
+
MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving
|
|
516
|
+
])
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Distance Calculation
|
|
520
|
+
|
|
521
|
+
```swift
|
|
522
|
+
func distanceBetween(_ a: CLLocationCoordinate2D,
|
|
523
|
+
_ b: CLLocationCoordinate2D) -> CLLocationDistance {
|
|
524
|
+
let locA = CLLocation(latitude: a.latitude, longitude: a.longitude)
|
|
525
|
+
let locB = CLLocation(latitude: b.latitude, longitude: b.longitude)
|
|
526
|
+
return locA.distance(from: locB) // meters
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Format Distance for Display
|
|
531
|
+
|
|
532
|
+
```swift
|
|
533
|
+
let formatter = MKDistanceFormatter()
|
|
534
|
+
formatter.unitStyle = .abbreviated
|
|
535
|
+
let text = formatter.string(fromDistance: 1500) // "0.9 mi" or "1.5 km"
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## iOS 26 New APIs
|
|
541
|
+
|
|
542
|
+
### MKGeocodingRequest
|
|
543
|
+
|
|
544
|
+
Convert an address string to map items with richer data than `CLGeocoder`:
|
|
545
|
+
|
|
546
|
+
```swift
|
|
547
|
+
@available(iOS 26, *)
|
|
548
|
+
func geocodeAddresses(_ addresses: [String]) async -> [MKMapItem] {
|
|
549
|
+
var items: [MKMapItem] = []
|
|
550
|
+
for address in addresses {
|
|
551
|
+
let request = MKGeocodingRequest(address: address)
|
|
552
|
+
if let mapItems = try? await request.mapItems {
|
|
553
|
+
items.append(contentsOf: mapItems)
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return items
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### MKReverseGeocodingRequest
|
|
561
|
+
|
|
562
|
+
Convert coordinates to map items with `MKAddress`:
|
|
563
|
+
|
|
564
|
+
```swift
|
|
565
|
+
@available(iOS 26, *)
|
|
566
|
+
func reverseGeocode(_ coordinate: CLLocationCoordinate2D) async -> MKAddress? {
|
|
567
|
+
let location = CLLocation(latitude: coordinate.latitude,
|
|
568
|
+
longitude: coordinate.longitude)
|
|
569
|
+
guard let request = MKReverseGeocodingRequest(location: location) else {
|
|
570
|
+
return nil
|
|
571
|
+
}
|
|
572
|
+
let mapItems = try? await request.mapItems
|
|
573
|
+
return mapItems?.first?.address
|
|
574
|
+
}
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### MKAddress and MKAddressRepresentations
|
|
578
|
+
|
|
579
|
+
`MKAddress` provides structured address components. Use
|
|
580
|
+
`MKAddressRepresentations` to format addresses for different contexts:
|
|
581
|
+
|
|
582
|
+
```swift
|
|
583
|
+
@available(iOS 26, *)
|
|
584
|
+
func formatAddress(_ address: MKAddress) -> String {
|
|
585
|
+
// Use address representations for locale-aware formatting
|
|
586
|
+
return address.representations.fullAddress()
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### PlaceDescriptor (via GeoToolbox)
|
|
591
|
+
|
|
592
|
+
Create place references from coordinates when you do not have a Place ID:
|
|
593
|
+
|
|
594
|
+
```swift
|
|
595
|
+
@available(iOS 26, *)
|
|
596
|
+
import GeoToolbox
|
|
597
|
+
|
|
598
|
+
let descriptor = PlaceDescriptor(
|
|
599
|
+
representations: [.coordinate(myCoordinate)],
|
|
600
|
+
commonName: "My Favorite Cafe"
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
let request = MKMapItemRequest(placeDescriptor: descriptor)
|
|
604
|
+
let mapItem = try await request.mapItem
|
|
605
|
+
|
|
606
|
+
// Use mapItem with any MapKit API: Marker(item:), directions, place cards
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### Cycling Directions
|
|
610
|
+
|
|
611
|
+
```swift
|
|
612
|
+
@available(iOS 26, *)
|
|
613
|
+
func cyclingRoute(to destination: MKMapItem) async throws -> MKRoute? {
|
|
614
|
+
let request = MKDirections.Request()
|
|
615
|
+
request.source = .forCurrentLocation()
|
|
616
|
+
request.destination = destination
|
|
617
|
+
request.transportType = .cycling
|
|
618
|
+
let response = try await MKDirections(request: request).calculate()
|
|
619
|
+
return response.routes.first
|
|
620
|
+
}
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
---
|
|
624
|
+
|
|
625
|
+
## User Location Display
|
|
626
|
+
|
|
627
|
+
Show the user's position with the built-in blue dot:
|
|
628
|
+
|
|
629
|
+
```swift
|
|
630
|
+
Map(position: $position) {
|
|
631
|
+
UserAnnotation() // Blue dot with accuracy ring
|
|
632
|
+
// ... other content
|
|
633
|
+
}
|
|
634
|
+
.mapControls {
|
|
635
|
+
MapUserLocationButton() // Button to re-center on user
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
`UserAnnotation()` requires location authorization. If authorization is
|
|
640
|
+
denied, the annotation does not appear and no error is thrown.
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
## Map in a List or ScrollView
|
|
645
|
+
|
|
646
|
+
When embedding a `Map` inside a `ScrollView`, disable map gestures that
|
|
647
|
+
conflict with scrolling:
|
|
648
|
+
|
|
649
|
+
```swift
|
|
650
|
+
ScrollView {
|
|
651
|
+
Map(position: $position, interactionModes: []) {
|
|
652
|
+
Marker("Location", coordinate: coord)
|
|
653
|
+
}
|
|
654
|
+
.frame(height: 200)
|
|
655
|
+
.clipShape(.rect(cornerRadius: 12))
|
|
656
|
+
|
|
657
|
+
Text("Details below the map...")
|
|
658
|
+
}
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
Use `interactionModes: []` for a fully static map thumbnail or
|
|
662
|
+
`interactionModes: [.zoom]` to allow pinch-to-zoom without pan conflicts.
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
## Coordinate Utilities
|
|
667
|
+
|
|
668
|
+
### Region from an array of coordinates
|
|
669
|
+
|
|
670
|
+
```swift
|
|
671
|
+
func regionForCoordinates(_ coords: [CLLocationCoordinate2D]) -> MKCoordinateRegion {
|
|
672
|
+
guard !coords.isEmpty else {
|
|
673
|
+
return MKCoordinateRegion()
|
|
674
|
+
}
|
|
675
|
+
var minLat = coords[0].latitude
|
|
676
|
+
var maxLat = coords[0].latitude
|
|
677
|
+
var minLon = coords[0].longitude
|
|
678
|
+
var maxLon = coords[0].longitude
|
|
679
|
+
|
|
680
|
+
for coord in coords {
|
|
681
|
+
minLat = min(minLat, coord.latitude)
|
|
682
|
+
maxLat = max(maxLat, coord.latitude)
|
|
683
|
+
minLon = min(minLon, coord.longitude)
|
|
684
|
+
maxLon = max(maxLon, coord.longitude)
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
let center = CLLocationCoordinate2D(
|
|
688
|
+
latitude: (minLat + maxLat) / 2,
|
|
689
|
+
longitude: (minLon + maxLon) / 2
|
|
690
|
+
)
|
|
691
|
+
let span = MKCoordinateSpan(
|
|
692
|
+
latitudeDelta: (maxLat - minLat) * 1.3, // 30% padding
|
|
693
|
+
longitudeDelta: (maxLon - minLon) * 1.3
|
|
694
|
+
)
|
|
695
|
+
return MKCoordinateRegion(center: center, span: span)
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### CLLocationCoordinate2D Equatable conformance
|
|
700
|
+
|
|
701
|
+
`CLLocationCoordinate2D` does not conform to `Equatable` by default.
|
|
702
|
+
Extend it when needed for comparisons:
|
|
703
|
+
|
|
704
|
+
```swift
|
|
705
|
+
extension CLLocationCoordinate2D: @retroactive Equatable {
|
|
706
|
+
public static func == (lhs: Self, rhs: Self) -> Bool {
|
|
707
|
+
lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
Note: Use `@retroactive` (Swift 5.10+) to silence the warning about
|
|
713
|
+
conforming types from other modules.
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
## Accessibility
|
|
718
|
+
|
|
719
|
+
### Marker accessibility
|
|
720
|
+
|
|
721
|
+
`Marker` views include built-in VoiceOver support using the title string.
|
|
722
|
+
Add `.accessibilityLabel` for richer descriptions:
|
|
723
|
+
|
|
724
|
+
```swift
|
|
725
|
+
Marker(store.name, coordinate: store.coordinate)
|
|
726
|
+
.accessibilityLabel("\(store.name), \(store.distanceText) away")
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### Map accessibility
|
|
730
|
+
|
|
731
|
+
Add a concise description of the map purpose:
|
|
732
|
+
|
|
733
|
+
```swift
|
|
734
|
+
Map { ... }
|
|
735
|
+
.accessibilityElement()
|
|
736
|
+
.accessibilityLabel("Store locations map showing \(stores.count) stores")
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
---
|
|
740
|
+
|
|
741
|
+
## References
|
|
742
|
+
|
|
743
|
+
- Apple docs: [MapKit for SwiftUI](https://sosumi.ai/documentation/MapKit/MapKit-for-SwiftUI)
|
|
744
|
+
- Apple docs: [Map](https://sosumi.ai/documentation/MapKit/Map)
|
|
745
|
+
- Apple docs: [MapCameraPosition](https://sosumi.ai/documentation/MapKit/MapCameraPosition)
|
|
746
|
+
- Apple docs: [MKLocalSearch](https://sosumi.ai/documentation/MapKit/MKLocalSearch)
|
|
747
|
+
- Apple docs: [MKDirections](https://sosumi.ai/documentation/MapKit/MKDirections)
|
|
748
|
+
- Apple docs: [MKLookAroundScene](https://sosumi.ai/documentation/MapKit/MKLookAroundScene)
|