@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,441 +1,441 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: ios-networking
|
|
3
|
-
description: "Build, review, or improve networking code in iOS/macOS apps using URLSession with async/await, structured concurrency, and modern Swift patterns. Use when working with REST APIs, downloading files, uploading data, WebSocket connections, pagination, retry logic, request middleware, caching, background transfers, or network reachability monitoring. Also use when handling HTTP requests, API clients, network error handling, or data fetching in Swift apps."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# iOS Networking
|
|
7
|
-
|
|
8
|
-
Modern networking patterns for iOS 26+ using URLSession with async/await and
|
|
9
|
-
structured concurrency. All examples target Swift 6.2. No third-party
|
|
10
|
-
dependencies required -- URLSession covers the vast majority of networking
|
|
11
|
-
needs.
|
|
12
|
-
|
|
13
|
-
## Contents
|
|
14
|
-
|
|
15
|
-
- [Core URLSession async/await](#core-urlsession-asyncawait)
|
|
16
|
-
- [API Client Architecture](#api-client-architecture)
|
|
17
|
-
- [Error Handling](#error-handling)
|
|
18
|
-
- [Pagination](#pagination)
|
|
19
|
-
- [Network Reachability](#network-reachability)
|
|
20
|
-
- [Configuring URLSession](#configuring-urlsession)
|
|
21
|
-
- [Common Mistakes](#common-mistakes)
|
|
22
|
-
- [Review Checklist](#review-checklist)
|
|
23
|
-
- [References](#references)
|
|
24
|
-
|
|
25
|
-
## Core URLSession async/await
|
|
26
|
-
|
|
27
|
-
URLSession gained native async/await overloads in iOS 15. These are the
|
|
28
|
-
only networking APIs to use in new code. Never use completion-handler
|
|
29
|
-
variants in new projects.
|
|
30
|
-
|
|
31
|
-
### Data Requests
|
|
32
|
-
|
|
33
|
-
```swift
|
|
34
|
-
// Basic GET
|
|
35
|
-
let (data, response) = try await URLSession.shared.data(from: url)
|
|
36
|
-
|
|
37
|
-
// With a configured URLRequest
|
|
38
|
-
var request = URLRequest(url: url)
|
|
39
|
-
request.httpMethod = "POST"
|
|
40
|
-
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
41
|
-
request.httpBody = try JSONEncoder().encode(payload)
|
|
42
|
-
request.timeoutInterval = 30
|
|
43
|
-
request.cachePolicy = .reloadIgnoringLocalCacheData
|
|
44
|
-
|
|
45
|
-
let (data, response) = try await URLSession.shared.data(for: request)
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### Response Validation
|
|
49
|
-
|
|
50
|
-
Always validate the HTTP status code before decoding. URLSession does not
|
|
51
|
-
throw for 4xx/5xx responses -- it only throws for transport-level failures.
|
|
52
|
-
|
|
53
|
-
```swift
|
|
54
|
-
guard let httpResponse = response as? HTTPURLResponse else {
|
|
55
|
-
throw NetworkError.invalidResponse
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
guard (200..<300).contains(httpResponse.statusCode) else {
|
|
59
|
-
throw NetworkError.httpError(
|
|
60
|
-
statusCode: httpResponse.statusCode,
|
|
61
|
-
data: data
|
|
62
|
-
)
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### JSON Decoding with Codable
|
|
67
|
-
|
|
68
|
-
```swift
|
|
69
|
-
func fetch<T: Decodable>(_ type: T.Type, from url: URL) async throws -> T {
|
|
70
|
-
let (data, response) = try await URLSession.shared.data(from: url)
|
|
71
|
-
|
|
72
|
-
guard let httpResponse = response as? HTTPURLResponse,
|
|
73
|
-
(200..<300).contains(httpResponse.statusCode) else {
|
|
74
|
-
throw NetworkError.invalidResponse
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
let decoder = JSONDecoder()
|
|
78
|
-
decoder.dateDecodingStrategy = .iso8601
|
|
79
|
-
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
|
80
|
-
return try decoder.decode(T.self, from: data)
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Downloads and Uploads
|
|
85
|
-
|
|
86
|
-
Use `download(for:)` for large files -- it streams to disk instead of
|
|
87
|
-
loading the entire payload into memory.
|
|
88
|
-
|
|
89
|
-
```swift
|
|
90
|
-
// Download to a temporary file
|
|
91
|
-
let (localURL, response) = try await URLSession.shared.download(for: request)
|
|
92
|
-
|
|
93
|
-
// Move from temp location before the method returns
|
|
94
|
-
let destination = documentsDirectory.appendingPathComponent("file.zip")
|
|
95
|
-
try FileManager.default.moveItem(at: localURL, to: destination)
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
```swift
|
|
99
|
-
// Upload data
|
|
100
|
-
let (data, response) = try await URLSession.shared.upload(for: request, from: bodyData)
|
|
101
|
-
|
|
102
|
-
// Upload from file
|
|
103
|
-
let (data, response) = try await URLSession.shared.upload(for: request, fromFile: fileURL)
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Streaming with AsyncBytes
|
|
107
|
-
|
|
108
|
-
Use `bytes(for:)` for streaming responses, progress tracking, or
|
|
109
|
-
line-delimited data (e.g., server-sent events).
|
|
110
|
-
|
|
111
|
-
```swift
|
|
112
|
-
let (bytes, response) = try await URLSession.shared.bytes(for: request)
|
|
113
|
-
|
|
114
|
-
for try await line in bytes.lines {
|
|
115
|
-
// Process each line as it arrives (e.g., SSE stream)
|
|
116
|
-
handleEvent(line)
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## API Client Architecture
|
|
121
|
-
|
|
122
|
-
### Protocol-Based Client
|
|
123
|
-
|
|
124
|
-
Define a protocol for testability. This lets you swap implementations in
|
|
125
|
-
tests without mocking URLSession directly.
|
|
126
|
-
|
|
127
|
-
```swift
|
|
128
|
-
protocol APIClientProtocol: Sendable {
|
|
129
|
-
func fetch<T: Decodable & Sendable>(
|
|
130
|
-
_ type: T.Type,
|
|
131
|
-
endpoint: Endpoint
|
|
132
|
-
) async throws -> T
|
|
133
|
-
|
|
134
|
-
func send<T: Decodable & Sendable>(
|
|
135
|
-
_ type: T.Type,
|
|
136
|
-
endpoint: Endpoint,
|
|
137
|
-
body: some Encodable & Sendable
|
|
138
|
-
) async throws -> T
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
```swift
|
|
143
|
-
struct Endpoint: Sendable {
|
|
144
|
-
let path: String
|
|
145
|
-
var method: String = "GET"
|
|
146
|
-
var queryItems: [URLQueryItem] = []
|
|
147
|
-
var headers: [String: String] = [:]
|
|
148
|
-
|
|
149
|
-
func url(relativeTo baseURL: URL) -> URL {
|
|
150
|
-
guard let components = URLComponents(
|
|
151
|
-
url: baseURL.appendingPathComponent(path),
|
|
152
|
-
resolvingAgainstBaseURL: true
|
|
153
|
-
) else {
|
|
154
|
-
preconditionFailure("Invalid URL components for path: \(path)")
|
|
155
|
-
}
|
|
156
|
-
var mutableComponents = components
|
|
157
|
-
if !queryItems.isEmpty {
|
|
158
|
-
mutableComponents.queryItems = queryItems
|
|
159
|
-
}
|
|
160
|
-
guard let url = mutableComponents.url else {
|
|
161
|
-
preconditionFailure("Failed to construct URL from components")
|
|
162
|
-
}
|
|
163
|
-
return url
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
The client accepts a `baseURL`, optional custom `URLSession`, `JSONDecoder`,
|
|
169
|
-
and an array of `RequestMiddleware` interceptors. Each method builds a
|
|
170
|
-
`URLRequest` from the endpoint, applies middleware, executes the request,
|
|
171
|
-
validates the status code, and decodes the result. See
|
|
172
|
-
`references/urlsession-patterns.md` for the complete `APIClient` implementation
|
|
173
|
-
with convenience methods, request builder, and test setup.
|
|
174
|
-
|
|
175
|
-
### Lightweight Closure-Based Client
|
|
176
|
-
|
|
177
|
-
For apps using the MV pattern, use closure-based clients for testability
|
|
178
|
-
and SwiftUI preview support. See `references/lightweight-clients.md` for
|
|
179
|
-
the full pattern (struct of async closures, injected via init).
|
|
180
|
-
|
|
181
|
-
### Request Middleware / Interceptors
|
|
182
|
-
|
|
183
|
-
Middleware transforms requests before they are sent. Use this for
|
|
184
|
-
authentication, logging, analytics headers, and similar cross-cutting
|
|
185
|
-
concerns.
|
|
186
|
-
|
|
187
|
-
```swift
|
|
188
|
-
protocol RequestMiddleware: Sendable {
|
|
189
|
-
func prepare(_ request: URLRequest) async throws -> URLRequest
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
```swift
|
|
194
|
-
struct AuthMiddleware: RequestMiddleware {
|
|
195
|
-
let tokenProvider: @Sendable () async throws -> String
|
|
196
|
-
|
|
197
|
-
func prepare(_ request: URLRequest) async throws -> URLRequest {
|
|
198
|
-
var request = request
|
|
199
|
-
let token = try await tokenProvider()
|
|
200
|
-
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
|
201
|
-
return request
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Token Refresh Flow
|
|
207
|
-
|
|
208
|
-
Handle 401 responses by refreshing the token and retrying once.
|
|
209
|
-
|
|
210
|
-
```swift
|
|
211
|
-
func fetchWithTokenRefresh<T: Decodable & Sendable>(
|
|
212
|
-
_ type: T.Type,
|
|
213
|
-
endpoint: Endpoint,
|
|
214
|
-
tokenStore: TokenStore
|
|
215
|
-
) async throws -> T {
|
|
216
|
-
do {
|
|
217
|
-
return try await fetch(type, endpoint: endpoint)
|
|
218
|
-
} catch NetworkError.httpError(statusCode: 401, _) {
|
|
219
|
-
try await tokenStore.refreshToken()
|
|
220
|
-
return try await fetch(type, endpoint: endpoint)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
## Error Handling
|
|
226
|
-
|
|
227
|
-
### Structured Error Types
|
|
228
|
-
|
|
229
|
-
```swift
|
|
230
|
-
enum NetworkError: Error, Sendable {
|
|
231
|
-
case invalidResponse
|
|
232
|
-
case httpError(statusCode: Int, data: Data)
|
|
233
|
-
case decodingFailed(Error)
|
|
234
|
-
case noConnection
|
|
235
|
-
case timedOut
|
|
236
|
-
case cancelled
|
|
237
|
-
|
|
238
|
-
/// Map a URLError to a typed NetworkError
|
|
239
|
-
static func from(_ urlError: URLError) -> NetworkError {
|
|
240
|
-
switch urlError.code {
|
|
241
|
-
case .notConnectedToInternet, .networkConnectionLost:
|
|
242
|
-
return .noConnection
|
|
243
|
-
case .timedOut:
|
|
244
|
-
return .timedOut
|
|
245
|
-
case .cancelled:
|
|
246
|
-
return .cancelled
|
|
247
|
-
default:
|
|
248
|
-
return .httpError(statusCode: -1, data: Data())
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### Key URLError Cases
|
|
255
|
-
|
|
256
|
-
| URLError Code | Meaning | Action |
|
|
257
|
-
|---|---|---|
|
|
258
|
-
| `.notConnectedToInternet` | Device offline | Show offline UI, queue for retry |
|
|
259
|
-
| `.networkConnectionLost` | Connection dropped mid-request | Retry with backoff |
|
|
260
|
-
| `.timedOut` | Server did not respond in time | Retry once, then show error |
|
|
261
|
-
| `.cancelled` | Task was cancelled | No action needed; do not show error |
|
|
262
|
-
| `.cannotFindHost` | DNS failure | Check URL, show error |
|
|
263
|
-
| `.secureConnectionFailed` | TLS handshake failed | Check cert pinning, ATS config |
|
|
264
|
-
| `.userAuthenticationRequired` | 401 from proxy | Trigger auth flow |
|
|
265
|
-
|
|
266
|
-
### Decoding Server Error Bodies
|
|
267
|
-
|
|
268
|
-
```swift
|
|
269
|
-
struct APIErrorResponse: Decodable, Sendable {
|
|
270
|
-
let code: String
|
|
271
|
-
let message: String
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
func decodeAPIError(from data: Data) -> APIErrorResponse? {
|
|
275
|
-
try? JSONDecoder().decode(APIErrorResponse.self, from: data)
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Usage in catch block
|
|
279
|
-
catch NetworkError.httpError(let statusCode, let data) {
|
|
280
|
-
if let apiError = decodeAPIError(from: data) {
|
|
281
|
-
showError("Server error: \(apiError.message)")
|
|
282
|
-
} else {
|
|
283
|
-
showError("HTTP \(statusCode)")
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### Retry with Exponential Backoff
|
|
289
|
-
|
|
290
|
-
Use structured concurrency for retries. Respect task cancellation between
|
|
291
|
-
attempts. Skip retries for cancellation and 4xx client errors (except 429).
|
|
292
|
-
|
|
293
|
-
```swift
|
|
294
|
-
func withRetry<T: Sendable>(
|
|
295
|
-
maxAttempts: Int = 3,
|
|
296
|
-
initialDelay: Duration = .seconds(1),
|
|
297
|
-
operation: @Sendable () async throws -> T
|
|
298
|
-
) async throws -> T {
|
|
299
|
-
var lastError: Error?
|
|
300
|
-
for attempt in 0..<maxAttempts {
|
|
301
|
-
do {
|
|
302
|
-
return try await operation()
|
|
303
|
-
} catch {
|
|
304
|
-
lastError = error
|
|
305
|
-
if error is CancellationError { throw error }
|
|
306
|
-
if case NetworkError.httpError(let code, _) = error,
|
|
307
|
-
(400..<500).contains(code), code != 429 { throw error }
|
|
308
|
-
if attempt < maxAttempts - 1 {
|
|
309
|
-
try await Task.sleep(for: initialDelay * Int(pow(2.0, Double(attempt))))
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
throw lastError!
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
## Pagination
|
|
318
|
-
|
|
319
|
-
Build cursor-based or offset-based pagination with `AsyncSequence`.
|
|
320
|
-
Always check `Task.isCancelled` between pages. See
|
|
321
|
-
`references/urlsession-patterns.md` for complete `CursorPaginator` and
|
|
322
|
-
offset-based implementations.
|
|
323
|
-
|
|
324
|
-
## Network Reachability
|
|
325
|
-
|
|
326
|
-
Use `NWPathMonitor` from the Network framework — not third-party
|
|
327
|
-
Reachability libraries. Wrap in `AsyncStream` for structured concurrency.
|
|
328
|
-
|
|
329
|
-
```swift
|
|
330
|
-
import Network
|
|
331
|
-
|
|
332
|
-
func networkStatusStream() -> AsyncStream<NWPath.Status> {
|
|
333
|
-
AsyncStream { continuation in
|
|
334
|
-
let monitor = NWPathMonitor()
|
|
335
|
-
monitor.pathUpdateHandler = { continuation.yield($0.status) }
|
|
336
|
-
continuation.onTermination = { _ in monitor.cancel() }
|
|
337
|
-
monitor.start(queue: DispatchQueue(label: "NetworkMonitor"))
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
Check `path.isExpensive` (cellular) and `path.isConstrained` (Low Data
|
|
343
|
-
Mode) to adapt behavior (reduce image quality, skip prefetching).
|
|
344
|
-
|
|
345
|
-
## Configuring URLSession
|
|
346
|
-
|
|
347
|
-
Create a configured session for production code. `URLSession.shared` is
|
|
348
|
-
acceptable only for simple, one-off requests.
|
|
349
|
-
|
|
350
|
-
```swift
|
|
351
|
-
let configuration = URLSessionConfiguration.default
|
|
352
|
-
configuration.timeoutIntervalForRequest = 30
|
|
353
|
-
configuration.timeoutIntervalForResource = 300
|
|
354
|
-
configuration.waitsForConnectivity = true
|
|
355
|
-
configuration.requestCachePolicy = .returnCacheDataElseLoad
|
|
356
|
-
configuration.httpAdditionalHeaders = [
|
|
357
|
-
"Accept": "application/json",
|
|
358
|
-
"Accept-Language": Locale.preferredLanguages.first ?? "en"
|
|
359
|
-
]
|
|
360
|
-
|
|
361
|
-
let session = URLSession(configuration: configuration)
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
`waitsForConnectivity = true` is valuable -- it makes the session wait for
|
|
365
|
-
a network path instead of failing immediately when offline. Combine with
|
|
366
|
-
`urlSession(_:taskIsWaitingForConnectivity:)` delegate callback for UI
|
|
367
|
-
feedback.
|
|
368
|
-
|
|
369
|
-
## Common Mistakes
|
|
370
|
-
|
|
371
|
-
**DON'T:** Use `URLSession.shared` with custom configuration needs.
|
|
372
|
-
**DO:** Create a configured `URLSession` with appropriate timeouts, caching,
|
|
373
|
-
and delegate for production code.
|
|
374
|
-
|
|
375
|
-
**DON'T:** Force-unwrap `URL(string:)` with dynamic input.
|
|
376
|
-
**DO:** Use `URL(string:)` with proper error handling. Force-unwrap is
|
|
377
|
-
acceptable only for compile-time-constant strings.
|
|
378
|
-
|
|
379
|
-
**DON'T:** Decode JSON on the main thread for large payloads.
|
|
380
|
-
**DO:** Keep decoding on the calling context of the URLSession call, which
|
|
381
|
-
is off-main by default. Only hop to `@MainActor` to update UI state.
|
|
382
|
-
|
|
383
|
-
**DON'T:** Ignore cancellation in long-running network tasks.
|
|
384
|
-
**DO:** Check `Task.isCancelled` or call `try Task.checkCancellation()` in
|
|
385
|
-
loops (pagination, streaming, retry). Use `.task` in SwiftUI for automatic
|
|
386
|
-
cancellation.
|
|
387
|
-
|
|
388
|
-
**DON'T:** Use Alamofire or Moya when URLSession async/await handles the
|
|
389
|
-
need.
|
|
390
|
-
**DO:** Use URLSession directly. With async/await, the ergonomic gap that
|
|
391
|
-
justified third-party libraries no longer exists. Reserve third-party
|
|
392
|
-
libraries for genuinely missing features (e.g., image caching).
|
|
393
|
-
|
|
394
|
-
**DON'T:** Mock URLSession directly in tests.
|
|
395
|
-
**DO:** Use `URLProtocol` subclass for transport-level mocking, or use
|
|
396
|
-
protocol-based clients that accept a test double.
|
|
397
|
-
|
|
398
|
-
**DON'T:** Use `data(for:)` for large file downloads.
|
|
399
|
-
**DO:** Use `download(for:)` which streams to disk and avoids memory spikes.
|
|
400
|
-
|
|
401
|
-
**DON'T:** Fire network requests from `body` or view initializers.
|
|
402
|
-
**DO:** Use `.task` or `.task(id:)` to trigger network calls.
|
|
403
|
-
|
|
404
|
-
**DON'T:** Hardcode authentication tokens in requests.
|
|
405
|
-
**DO:** Inject tokens via middleware so they are centralized and refreshable.
|
|
406
|
-
|
|
407
|
-
**DON'T:** Ignore HTTP status codes and decode blindly.
|
|
408
|
-
**DO:** Validate status codes before decoding. A 200 with invalid JSON and
|
|
409
|
-
a 500 with an error body require different handling.
|
|
410
|
-
|
|
411
|
-
## Review Checklist
|
|
412
|
-
|
|
413
|
-
- [ ] All network calls use async/await (not completion handlers)
|
|
414
|
-
- [ ] Error handling covers URLError cases (.notConnectedToInternet, .timedOut, .cancelled)
|
|
415
|
-
- [ ] Requests are cancellable (respect Task cancellation via `.task` modifier or stored Task references)
|
|
416
|
-
- [ ] Authentication tokens injected via middleware, not hardcoded
|
|
417
|
-
- [ ] Response HTTP status codes validated before decoding
|
|
418
|
-
- [ ] Large downloads use `download(for:)` not `data(for:)`
|
|
419
|
-
- [ ] Network calls happen off `@MainActor` (only UI updates on main)
|
|
420
|
-
- [ ] URLSession configured with appropriate timeouts and caching
|
|
421
|
-
- [ ] Retry logic excludes cancellation and 4xx client errors
|
|
422
|
-
- [ ] Pagination checks `Task.isCancelled` between pages
|
|
423
|
-
- [ ] Sensitive tokens stored in Keychain (not UserDefaults or plain files)
|
|
424
|
-
- [ ] No force-unwrapped URLs from dynamic input
|
|
425
|
-
- [ ] Server error responses decoded and surfaced to users
|
|
426
|
-
- [ ] Ensure network response model types conform to Sendable; use @MainActor for UI-updating completion paths
|
|
427
|
-
|
|
428
|
-
## References
|
|
429
|
-
|
|
430
|
-
- See `references/urlsession-patterns.md` for complete API client
|
|
431
|
-
implementation, multipart uploads, download progress, URLProtocol
|
|
432
|
-
mocking, retry/backoff, certificate pinning, request logging, and
|
|
433
|
-
pagination implementations.
|
|
434
|
-
- See `references/background-websocket.md` for background URLSession
|
|
435
|
-
configuration, background downloads/uploads, WebSocket patterns with
|
|
436
|
-
structured concurrency, and reconnection strategies.
|
|
437
|
-
- See `references/lightweight-clients.md` for the lightweight closure-based
|
|
438
|
-
client pattern (struct of async closures, injected via init for testability
|
|
439
|
-
and preview support).
|
|
440
|
-
- See `references/network-framework.md` for Network.framework (NWConnection,
|
|
441
|
-
NWListener, NWBrowser, NWPathMonitor) and low-level TCP/UDP/WebSocket patterns.
|
|
1
|
+
---
|
|
2
|
+
name: ios-networking
|
|
3
|
+
description: "Build, review, or improve networking code in iOS/macOS apps using URLSession with async/await, structured concurrency, and modern Swift patterns. Use when working with REST APIs, downloading files, uploading data, WebSocket connections, pagination, retry logic, request middleware, caching, background transfers, or network reachability monitoring. Also use when handling HTTP requests, API clients, network error handling, or data fetching in Swift apps."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# iOS Networking
|
|
7
|
+
|
|
8
|
+
Modern networking patterns for iOS 26+ using URLSession with async/await and
|
|
9
|
+
structured concurrency. All examples target Swift 6.2. No third-party
|
|
10
|
+
dependencies required -- URLSession covers the vast majority of networking
|
|
11
|
+
needs.
|
|
12
|
+
|
|
13
|
+
## Contents
|
|
14
|
+
|
|
15
|
+
- [Core URLSession async/await](#core-urlsession-asyncawait)
|
|
16
|
+
- [API Client Architecture](#api-client-architecture)
|
|
17
|
+
- [Error Handling](#error-handling)
|
|
18
|
+
- [Pagination](#pagination)
|
|
19
|
+
- [Network Reachability](#network-reachability)
|
|
20
|
+
- [Configuring URLSession](#configuring-urlsession)
|
|
21
|
+
- [Common Mistakes](#common-mistakes)
|
|
22
|
+
- [Review Checklist](#review-checklist)
|
|
23
|
+
- [References](#references)
|
|
24
|
+
|
|
25
|
+
## Core URLSession async/await
|
|
26
|
+
|
|
27
|
+
URLSession gained native async/await overloads in iOS 15. These are the
|
|
28
|
+
only networking APIs to use in new code. Never use completion-handler
|
|
29
|
+
variants in new projects.
|
|
30
|
+
|
|
31
|
+
### Data Requests
|
|
32
|
+
|
|
33
|
+
```swift
|
|
34
|
+
// Basic GET
|
|
35
|
+
let (data, response) = try await URLSession.shared.data(from: url)
|
|
36
|
+
|
|
37
|
+
// With a configured URLRequest
|
|
38
|
+
var request = URLRequest(url: url)
|
|
39
|
+
request.httpMethod = "POST"
|
|
40
|
+
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
41
|
+
request.httpBody = try JSONEncoder().encode(payload)
|
|
42
|
+
request.timeoutInterval = 30
|
|
43
|
+
request.cachePolicy = .reloadIgnoringLocalCacheData
|
|
44
|
+
|
|
45
|
+
let (data, response) = try await URLSession.shared.data(for: request)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Response Validation
|
|
49
|
+
|
|
50
|
+
Always validate the HTTP status code before decoding. URLSession does not
|
|
51
|
+
throw for 4xx/5xx responses -- it only throws for transport-level failures.
|
|
52
|
+
|
|
53
|
+
```swift
|
|
54
|
+
guard let httpResponse = response as? HTTPURLResponse else {
|
|
55
|
+
throw NetworkError.invalidResponse
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
guard (200..<300).contains(httpResponse.statusCode) else {
|
|
59
|
+
throw NetworkError.httpError(
|
|
60
|
+
statusCode: httpResponse.statusCode,
|
|
61
|
+
data: data
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### JSON Decoding with Codable
|
|
67
|
+
|
|
68
|
+
```swift
|
|
69
|
+
func fetch<T: Decodable>(_ type: T.Type, from url: URL) async throws -> T {
|
|
70
|
+
let (data, response) = try await URLSession.shared.data(from: url)
|
|
71
|
+
|
|
72
|
+
guard let httpResponse = response as? HTTPURLResponse,
|
|
73
|
+
(200..<300).contains(httpResponse.statusCode) else {
|
|
74
|
+
throw NetworkError.invalidResponse
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let decoder = JSONDecoder()
|
|
78
|
+
decoder.dateDecodingStrategy = .iso8601
|
|
79
|
+
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
|
80
|
+
return try decoder.decode(T.self, from: data)
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Downloads and Uploads
|
|
85
|
+
|
|
86
|
+
Use `download(for:)` for large files -- it streams to disk instead of
|
|
87
|
+
loading the entire payload into memory.
|
|
88
|
+
|
|
89
|
+
```swift
|
|
90
|
+
// Download to a temporary file
|
|
91
|
+
let (localURL, response) = try await URLSession.shared.download(for: request)
|
|
92
|
+
|
|
93
|
+
// Move from temp location before the method returns
|
|
94
|
+
let destination = documentsDirectory.appendingPathComponent("file.zip")
|
|
95
|
+
try FileManager.default.moveItem(at: localURL, to: destination)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```swift
|
|
99
|
+
// Upload data
|
|
100
|
+
let (data, response) = try await URLSession.shared.upload(for: request, from: bodyData)
|
|
101
|
+
|
|
102
|
+
// Upload from file
|
|
103
|
+
let (data, response) = try await URLSession.shared.upload(for: request, fromFile: fileURL)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Streaming with AsyncBytes
|
|
107
|
+
|
|
108
|
+
Use `bytes(for:)` for streaming responses, progress tracking, or
|
|
109
|
+
line-delimited data (e.g., server-sent events).
|
|
110
|
+
|
|
111
|
+
```swift
|
|
112
|
+
let (bytes, response) = try await URLSession.shared.bytes(for: request)
|
|
113
|
+
|
|
114
|
+
for try await line in bytes.lines {
|
|
115
|
+
// Process each line as it arrives (e.g., SSE stream)
|
|
116
|
+
handleEvent(line)
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## API Client Architecture
|
|
121
|
+
|
|
122
|
+
### Protocol-Based Client
|
|
123
|
+
|
|
124
|
+
Define a protocol for testability. This lets you swap implementations in
|
|
125
|
+
tests without mocking URLSession directly.
|
|
126
|
+
|
|
127
|
+
```swift
|
|
128
|
+
protocol APIClientProtocol: Sendable {
|
|
129
|
+
func fetch<T: Decodable & Sendable>(
|
|
130
|
+
_ type: T.Type,
|
|
131
|
+
endpoint: Endpoint
|
|
132
|
+
) async throws -> T
|
|
133
|
+
|
|
134
|
+
func send<T: Decodable & Sendable>(
|
|
135
|
+
_ type: T.Type,
|
|
136
|
+
endpoint: Endpoint,
|
|
137
|
+
body: some Encodable & Sendable
|
|
138
|
+
) async throws -> T
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```swift
|
|
143
|
+
struct Endpoint: Sendable {
|
|
144
|
+
let path: String
|
|
145
|
+
var method: String = "GET"
|
|
146
|
+
var queryItems: [URLQueryItem] = []
|
|
147
|
+
var headers: [String: String] = [:]
|
|
148
|
+
|
|
149
|
+
func url(relativeTo baseURL: URL) -> URL {
|
|
150
|
+
guard let components = URLComponents(
|
|
151
|
+
url: baseURL.appendingPathComponent(path),
|
|
152
|
+
resolvingAgainstBaseURL: true
|
|
153
|
+
) else {
|
|
154
|
+
preconditionFailure("Invalid URL components for path: \(path)")
|
|
155
|
+
}
|
|
156
|
+
var mutableComponents = components
|
|
157
|
+
if !queryItems.isEmpty {
|
|
158
|
+
mutableComponents.queryItems = queryItems
|
|
159
|
+
}
|
|
160
|
+
guard let url = mutableComponents.url else {
|
|
161
|
+
preconditionFailure("Failed to construct URL from components")
|
|
162
|
+
}
|
|
163
|
+
return url
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The client accepts a `baseURL`, optional custom `URLSession`, `JSONDecoder`,
|
|
169
|
+
and an array of `RequestMiddleware` interceptors. Each method builds a
|
|
170
|
+
`URLRequest` from the endpoint, applies middleware, executes the request,
|
|
171
|
+
validates the status code, and decodes the result. See
|
|
172
|
+
`references/urlsession-patterns.md` for the complete `APIClient` implementation
|
|
173
|
+
with convenience methods, request builder, and test setup.
|
|
174
|
+
|
|
175
|
+
### Lightweight Closure-Based Client
|
|
176
|
+
|
|
177
|
+
For apps using the MV pattern, use closure-based clients for testability
|
|
178
|
+
and SwiftUI preview support. See `references/lightweight-clients.md` for
|
|
179
|
+
the full pattern (struct of async closures, injected via init).
|
|
180
|
+
|
|
181
|
+
### Request Middleware / Interceptors
|
|
182
|
+
|
|
183
|
+
Middleware transforms requests before they are sent. Use this for
|
|
184
|
+
authentication, logging, analytics headers, and similar cross-cutting
|
|
185
|
+
concerns.
|
|
186
|
+
|
|
187
|
+
```swift
|
|
188
|
+
protocol RequestMiddleware: Sendable {
|
|
189
|
+
func prepare(_ request: URLRequest) async throws -> URLRequest
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```swift
|
|
194
|
+
struct AuthMiddleware: RequestMiddleware {
|
|
195
|
+
let tokenProvider: @Sendable () async throws -> String
|
|
196
|
+
|
|
197
|
+
func prepare(_ request: URLRequest) async throws -> URLRequest {
|
|
198
|
+
var request = request
|
|
199
|
+
let token = try await tokenProvider()
|
|
200
|
+
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
|
201
|
+
return request
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Token Refresh Flow
|
|
207
|
+
|
|
208
|
+
Handle 401 responses by refreshing the token and retrying once.
|
|
209
|
+
|
|
210
|
+
```swift
|
|
211
|
+
func fetchWithTokenRefresh<T: Decodable & Sendable>(
|
|
212
|
+
_ type: T.Type,
|
|
213
|
+
endpoint: Endpoint,
|
|
214
|
+
tokenStore: TokenStore
|
|
215
|
+
) async throws -> T {
|
|
216
|
+
do {
|
|
217
|
+
return try await fetch(type, endpoint: endpoint)
|
|
218
|
+
} catch NetworkError.httpError(statusCode: 401, _) {
|
|
219
|
+
try await tokenStore.refreshToken()
|
|
220
|
+
return try await fetch(type, endpoint: endpoint)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Error Handling
|
|
226
|
+
|
|
227
|
+
### Structured Error Types
|
|
228
|
+
|
|
229
|
+
```swift
|
|
230
|
+
enum NetworkError: Error, Sendable {
|
|
231
|
+
case invalidResponse
|
|
232
|
+
case httpError(statusCode: Int, data: Data)
|
|
233
|
+
case decodingFailed(Error)
|
|
234
|
+
case noConnection
|
|
235
|
+
case timedOut
|
|
236
|
+
case cancelled
|
|
237
|
+
|
|
238
|
+
/// Map a URLError to a typed NetworkError
|
|
239
|
+
static func from(_ urlError: URLError) -> NetworkError {
|
|
240
|
+
switch urlError.code {
|
|
241
|
+
case .notConnectedToInternet, .networkConnectionLost:
|
|
242
|
+
return .noConnection
|
|
243
|
+
case .timedOut:
|
|
244
|
+
return .timedOut
|
|
245
|
+
case .cancelled:
|
|
246
|
+
return .cancelled
|
|
247
|
+
default:
|
|
248
|
+
return .httpError(statusCode: -1, data: Data())
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Key URLError Cases
|
|
255
|
+
|
|
256
|
+
| URLError Code | Meaning | Action |
|
|
257
|
+
|---|---|---|
|
|
258
|
+
| `.notConnectedToInternet` | Device offline | Show offline UI, queue for retry |
|
|
259
|
+
| `.networkConnectionLost` | Connection dropped mid-request | Retry with backoff |
|
|
260
|
+
| `.timedOut` | Server did not respond in time | Retry once, then show error |
|
|
261
|
+
| `.cancelled` | Task was cancelled | No action needed; do not show error |
|
|
262
|
+
| `.cannotFindHost` | DNS failure | Check URL, show error |
|
|
263
|
+
| `.secureConnectionFailed` | TLS handshake failed | Check cert pinning, ATS config |
|
|
264
|
+
| `.userAuthenticationRequired` | 401 from proxy | Trigger auth flow |
|
|
265
|
+
|
|
266
|
+
### Decoding Server Error Bodies
|
|
267
|
+
|
|
268
|
+
```swift
|
|
269
|
+
struct APIErrorResponse: Decodable, Sendable {
|
|
270
|
+
let code: String
|
|
271
|
+
let message: String
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
func decodeAPIError(from data: Data) -> APIErrorResponse? {
|
|
275
|
+
try? JSONDecoder().decode(APIErrorResponse.self, from: data)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Usage in catch block
|
|
279
|
+
catch NetworkError.httpError(let statusCode, let data) {
|
|
280
|
+
if let apiError = decodeAPIError(from: data) {
|
|
281
|
+
showError("Server error: \(apiError.message)")
|
|
282
|
+
} else {
|
|
283
|
+
showError("HTTP \(statusCode)")
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Retry with Exponential Backoff
|
|
289
|
+
|
|
290
|
+
Use structured concurrency for retries. Respect task cancellation between
|
|
291
|
+
attempts. Skip retries for cancellation and 4xx client errors (except 429).
|
|
292
|
+
|
|
293
|
+
```swift
|
|
294
|
+
func withRetry<T: Sendable>(
|
|
295
|
+
maxAttempts: Int = 3,
|
|
296
|
+
initialDelay: Duration = .seconds(1),
|
|
297
|
+
operation: @Sendable () async throws -> T
|
|
298
|
+
) async throws -> T {
|
|
299
|
+
var lastError: Error?
|
|
300
|
+
for attempt in 0..<maxAttempts {
|
|
301
|
+
do {
|
|
302
|
+
return try await operation()
|
|
303
|
+
} catch {
|
|
304
|
+
lastError = error
|
|
305
|
+
if error is CancellationError { throw error }
|
|
306
|
+
if case NetworkError.httpError(let code, _) = error,
|
|
307
|
+
(400..<500).contains(code), code != 429 { throw error }
|
|
308
|
+
if attempt < maxAttempts - 1 {
|
|
309
|
+
try await Task.sleep(for: initialDelay * Int(pow(2.0, Double(attempt))))
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
throw lastError!
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Pagination
|
|
318
|
+
|
|
319
|
+
Build cursor-based or offset-based pagination with `AsyncSequence`.
|
|
320
|
+
Always check `Task.isCancelled` between pages. See
|
|
321
|
+
`references/urlsession-patterns.md` for complete `CursorPaginator` and
|
|
322
|
+
offset-based implementations.
|
|
323
|
+
|
|
324
|
+
## Network Reachability
|
|
325
|
+
|
|
326
|
+
Use `NWPathMonitor` from the Network framework — not third-party
|
|
327
|
+
Reachability libraries. Wrap in `AsyncStream` for structured concurrency.
|
|
328
|
+
|
|
329
|
+
```swift
|
|
330
|
+
import Network
|
|
331
|
+
|
|
332
|
+
func networkStatusStream() -> AsyncStream<NWPath.Status> {
|
|
333
|
+
AsyncStream { continuation in
|
|
334
|
+
let monitor = NWPathMonitor()
|
|
335
|
+
monitor.pathUpdateHandler = { continuation.yield($0.status) }
|
|
336
|
+
continuation.onTermination = { _ in monitor.cancel() }
|
|
337
|
+
monitor.start(queue: DispatchQueue(label: "NetworkMonitor"))
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Check `path.isExpensive` (cellular) and `path.isConstrained` (Low Data
|
|
343
|
+
Mode) to adapt behavior (reduce image quality, skip prefetching).
|
|
344
|
+
|
|
345
|
+
## Configuring URLSession
|
|
346
|
+
|
|
347
|
+
Create a configured session for production code. `URLSession.shared` is
|
|
348
|
+
acceptable only for simple, one-off requests.
|
|
349
|
+
|
|
350
|
+
```swift
|
|
351
|
+
let configuration = URLSessionConfiguration.default
|
|
352
|
+
configuration.timeoutIntervalForRequest = 30
|
|
353
|
+
configuration.timeoutIntervalForResource = 300
|
|
354
|
+
configuration.waitsForConnectivity = true
|
|
355
|
+
configuration.requestCachePolicy = .returnCacheDataElseLoad
|
|
356
|
+
configuration.httpAdditionalHeaders = [
|
|
357
|
+
"Accept": "application/json",
|
|
358
|
+
"Accept-Language": Locale.preferredLanguages.first ?? "en"
|
|
359
|
+
]
|
|
360
|
+
|
|
361
|
+
let session = URLSession(configuration: configuration)
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
`waitsForConnectivity = true` is valuable -- it makes the session wait for
|
|
365
|
+
a network path instead of failing immediately when offline. Combine with
|
|
366
|
+
`urlSession(_:taskIsWaitingForConnectivity:)` delegate callback for UI
|
|
367
|
+
feedback.
|
|
368
|
+
|
|
369
|
+
## Common Mistakes
|
|
370
|
+
|
|
371
|
+
**DON'T:** Use `URLSession.shared` with custom configuration needs.
|
|
372
|
+
**DO:** Create a configured `URLSession` with appropriate timeouts, caching,
|
|
373
|
+
and delegate for production code.
|
|
374
|
+
|
|
375
|
+
**DON'T:** Force-unwrap `URL(string:)` with dynamic input.
|
|
376
|
+
**DO:** Use `URL(string:)` with proper error handling. Force-unwrap is
|
|
377
|
+
acceptable only for compile-time-constant strings.
|
|
378
|
+
|
|
379
|
+
**DON'T:** Decode JSON on the main thread for large payloads.
|
|
380
|
+
**DO:** Keep decoding on the calling context of the URLSession call, which
|
|
381
|
+
is off-main by default. Only hop to `@MainActor` to update UI state.
|
|
382
|
+
|
|
383
|
+
**DON'T:** Ignore cancellation in long-running network tasks.
|
|
384
|
+
**DO:** Check `Task.isCancelled` or call `try Task.checkCancellation()` in
|
|
385
|
+
loops (pagination, streaming, retry). Use `.task` in SwiftUI for automatic
|
|
386
|
+
cancellation.
|
|
387
|
+
|
|
388
|
+
**DON'T:** Use Alamofire or Moya when URLSession async/await handles the
|
|
389
|
+
need.
|
|
390
|
+
**DO:** Use URLSession directly. With async/await, the ergonomic gap that
|
|
391
|
+
justified third-party libraries no longer exists. Reserve third-party
|
|
392
|
+
libraries for genuinely missing features (e.g., image caching).
|
|
393
|
+
|
|
394
|
+
**DON'T:** Mock URLSession directly in tests.
|
|
395
|
+
**DO:** Use `URLProtocol` subclass for transport-level mocking, or use
|
|
396
|
+
protocol-based clients that accept a test double.
|
|
397
|
+
|
|
398
|
+
**DON'T:** Use `data(for:)` for large file downloads.
|
|
399
|
+
**DO:** Use `download(for:)` which streams to disk and avoids memory spikes.
|
|
400
|
+
|
|
401
|
+
**DON'T:** Fire network requests from `body` or view initializers.
|
|
402
|
+
**DO:** Use `.task` or `.task(id:)` to trigger network calls.
|
|
403
|
+
|
|
404
|
+
**DON'T:** Hardcode authentication tokens in requests.
|
|
405
|
+
**DO:** Inject tokens via middleware so they are centralized and refreshable.
|
|
406
|
+
|
|
407
|
+
**DON'T:** Ignore HTTP status codes and decode blindly.
|
|
408
|
+
**DO:** Validate status codes before decoding. A 200 with invalid JSON and
|
|
409
|
+
a 500 with an error body require different handling.
|
|
410
|
+
|
|
411
|
+
## Review Checklist
|
|
412
|
+
|
|
413
|
+
- [ ] All network calls use async/await (not completion handlers)
|
|
414
|
+
- [ ] Error handling covers URLError cases (.notConnectedToInternet, .timedOut, .cancelled)
|
|
415
|
+
- [ ] Requests are cancellable (respect Task cancellation via `.task` modifier or stored Task references)
|
|
416
|
+
- [ ] Authentication tokens injected via middleware, not hardcoded
|
|
417
|
+
- [ ] Response HTTP status codes validated before decoding
|
|
418
|
+
- [ ] Large downloads use `download(for:)` not `data(for:)`
|
|
419
|
+
- [ ] Network calls happen off `@MainActor` (only UI updates on main)
|
|
420
|
+
- [ ] URLSession configured with appropriate timeouts and caching
|
|
421
|
+
- [ ] Retry logic excludes cancellation and 4xx client errors
|
|
422
|
+
- [ ] Pagination checks `Task.isCancelled` between pages
|
|
423
|
+
- [ ] Sensitive tokens stored in Keychain (not UserDefaults or plain files)
|
|
424
|
+
- [ ] No force-unwrapped URLs from dynamic input
|
|
425
|
+
- [ ] Server error responses decoded and surfaced to users
|
|
426
|
+
- [ ] Ensure network response model types conform to Sendable; use @MainActor for UI-updating completion paths
|
|
427
|
+
|
|
428
|
+
## References
|
|
429
|
+
|
|
430
|
+
- See `references/urlsession-patterns.md` for complete API client
|
|
431
|
+
implementation, multipart uploads, download progress, URLProtocol
|
|
432
|
+
mocking, retry/backoff, certificate pinning, request logging, and
|
|
433
|
+
pagination implementations.
|
|
434
|
+
- See `references/background-websocket.md` for background URLSession
|
|
435
|
+
configuration, background downloads/uploads, WebSocket patterns with
|
|
436
|
+
structured concurrency, and reconnection strategies.
|
|
437
|
+
- See `references/lightweight-clients.md` for the lightweight closure-based
|
|
438
|
+
client pattern (struct of async closures, injected via init for testability
|
|
439
|
+
and preview support).
|
|
440
|
+
- See `references/network-framework.md` for Network.framework (NWConnection,
|
|
441
|
+
NWListener, NWBrowser, NWPathMonitor) and low-level TCP/UDP/WebSocket patterns.
|