@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.1
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/CHANGELOG.md +21 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +285 -285
- package/package.json +3 -3
- package/pipeline/lib/redact.mjs +3 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/scripts/gen-skills-index.mjs +13 -1
- package/pipeline/scripts/pre-commit-check.sh +4 -0
- package/pipeline/skills/.skill-manifest.json +69 -69
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +345 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +107 -121
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +145 -165
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +306 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +335 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +277 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +135 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +274 -384
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +173 -321
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +228 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +302 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +236 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +3 -3
- package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +1 -1
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +8 -7
- package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +2 -2
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +5 -2
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/_shared.py +100 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +45 -26
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +14 -16
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +12 -5
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +2 -1
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +6 -5
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +44 -18
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +5 -2
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +10 -11
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +4 -33
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +12 -59
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +297 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/skill-creator/template.md +7 -1
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +302 -241
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +304 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +183 -162
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +411 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +375 -491
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +397 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +191 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +81 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +394 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +201 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +134 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +111 -138
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +160 -315
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +224 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +416 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,430 +1,383 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: swift-concurrency
|
|
3
|
-
description: "Swift concurrency
|
|
3
|
+
description: "Swift 6.2+ concurrency: actor isolation, Sendable, sending, approachable concurrency (SE-0466 default MainActor, @concurrent, nonisolated(nonsending), Task.immediate), TaskGroup, cancellation, AsyncStream, bridging callbacks and GCD, Swift 6 migration. Use when answering concurrency questions, reading Sendable or isolation diagnostics, or repairing strict-concurrency errors. Not for single-diagnostic fixes or per-file reviews."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Swift Concurrency
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
behavior
|
|
10
|
+
Baseline: Swift 6.2 compilers and later. The aim of every change made with this
|
|
11
|
+
skill is data-race safety through isolation and `Sendable`, reached with the
|
|
12
|
+
smallest possible change in behavior. Features newer than Swift 6.0 name the
|
|
13
|
+
release or proposal that introduced them; the few that need Swift 6.3, such as
|
|
14
|
+
`weak let` (SE-0481), are marked as 6.3 only.
|
|
15
|
+
|
|
16
|
+
Not for the smallest fix to a single diagnostic (use `swift-concurrency-expert`)
|
|
17
|
+
or a file-by-file review pass (use `swift-concurrency-pro`).
|
|
13
18
|
|
|
14
19
|
## Contents
|
|
15
20
|
|
|
16
|
-
- [Triage
|
|
17
|
-
- [Swift 6.2
|
|
18
|
-
- [
|
|
19
|
-
- [Sendable
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
- [Actor
|
|
23
|
-
- [
|
|
24
|
-
- [
|
|
25
|
-
- [
|
|
26
|
-
- [
|
|
27
|
-
- [
|
|
21
|
+
- [Triage workflow](#triage-workflow)
|
|
22
|
+
- [Approachable concurrency in Swift 6.2](#approachable-concurrency-in-swift-62)
|
|
23
|
+
- [Isolation rules](#isolation-rules)
|
|
24
|
+
- [Sendable rules](#sendable-rules)
|
|
25
|
+
- [Tasks and structured concurrency](#tasks-and-structured-concurrency)
|
|
26
|
+
- [Cancellation](#cancellation)
|
|
27
|
+
- [Actor reentrancy](#actor-reentrancy)
|
|
28
|
+
- [Streams and continuations](#streams-and-continuations)
|
|
29
|
+
- [Observable models](#observable-models)
|
|
30
|
+
- [Locks and atomics](#locks-and-atomics)
|
|
31
|
+
- [Choosing how code moves between contexts](#choosing-how-code-moves-between-contexts)
|
|
32
|
+
- [Mistakes to catch](#mistakes-to-catch)
|
|
33
|
+
- [Review checklist](#review-checklist)
|
|
28
34
|
- [References](#references)
|
|
29
35
|
|
|
30
|
-
## Triage
|
|
31
|
-
|
|
32
|
-
When diagnosing a concurrency issue, follow this sequence:
|
|
33
|
-
|
|
34
|
-
### Step 1: Capture context
|
|
36
|
+
## Triage workflow
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
- Identify the project's concurrency settings:
|
|
38
|
-
- Swift language version (must be 6.2+).
|
|
39
|
-
- Whether Approachable Concurrency is enabled.
|
|
40
|
-
- Whether Default Actor Isolation is set to `MainActor`.
|
|
41
|
-
- Swift 6 strict concurrency status: complete/errors in Swift 6 language mode;
|
|
42
|
-
Complete / Targeted / Minimal only when auditing Swift 5 migration settings.
|
|
43
|
-
- Determine the current actor context of the code (`@MainActor`, custom `actor`,
|
|
44
|
-
`nonisolated`) and whether a default isolation mode is active.
|
|
45
|
-
- Confirm whether the code is UI-bound or intended to run off the main actor.
|
|
38
|
+
**1. Collect the facts before editing.**
|
|
46
39
|
|
|
47
|
-
|
|
40
|
+
- Copy the compiler message word for word and note every symbol it names.
|
|
41
|
+
- Find the Swift language version. This workflow assumes 6.2 or newer.
|
|
42
|
+
- Check two build settings separately: is Approachable Concurrency on, and is
|
|
43
|
+
Default Actor Isolation set to `MainActor`?
|
|
44
|
+
- In Swift 6 language mode strict checking is always complete and every
|
|
45
|
+
data-race diagnostic is an error. The Minimal, Targeted and Complete levels
|
|
46
|
+
only matter when you audit a target still in Swift 5 mode.
|
|
47
|
+
- Work out where the failing code is isolated today: `@MainActor`, a custom
|
|
48
|
+
`actor`, or `nonisolated`, and whether a module-wide default applies.
|
|
49
|
+
- Decide whether the code belongs to the UI or is meant to run away from the
|
|
50
|
+
main actor.
|
|
48
51
|
|
|
49
|
-
|
|
52
|
+
**2. Make the smallest change that is still safe.** Keep behavior as it is.
|
|
50
53
|
|
|
51
|
-
| Situation |
|
|
54
|
+
| Situation | Change |
|
|
52
55
|
|---|---|
|
|
53
|
-
| UI
|
|
54
|
-
|
|
|
55
|
-
| Global
|
|
56
|
-
|
|
|
57
|
-
| Sendable
|
|
58
|
-
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
**Effect:** Eliminates most data-race safety errors for UI-bound code and
|
|
83
|
-
global/static state without writing `@MainActor` everywhere.
|
|
56
|
+
| Type drives UI | `@MainActor` on the type or on the members that need it |
|
|
57
|
+
| Main-actor type must satisfy a protocol | Isolated conformance: `extension Foo: @MainActor Proto` |
|
|
58
|
+
| Global or `static` mutable state | Put it on `@MainActor` or inside an actor |
|
|
59
|
+
| Work must leave the caller's actor | `@concurrent` async function on a `nonisolated` type |
|
|
60
|
+
| Type fails a `Sendable` check | Prefer an immutable value type; declare `Sendable` only when it is true |
|
|
61
|
+
| Value handed to another isolation once | A `sending` parameter or result (SE-0430) |
|
|
62
|
+
|
|
63
|
+
**3. Confirm.** Rebuild and check the diagnostic is gone, no new warnings
|
|
64
|
+
appeared, and no `@unchecked Sendable` or `nonisolated(unsafe)` slipped in
|
|
65
|
+
without a proven reason.
|
|
66
|
+
|
|
67
|
+
## Approachable concurrency in Swift 6.2
|
|
68
|
+
|
|
69
|
+
Swift 6.2 changed defaults so that ordinary code needs fewer annotations and is
|
|
70
|
+
safe without them. Xcode exposes two independent settings, and mixing them up
|
|
71
|
+
is the most common planning error:
|
|
72
|
+
|
|
73
|
+
- **Approachable Concurrency** switches on a group of upcoming features,
|
|
74
|
+
including `NonisolatedNonsendingByDefault` and isolated-conformance
|
|
75
|
+
inference. It does not make the module main-actor isolated.
|
|
76
|
+
- **Default Actor Isolation = `MainActor`** (SE-0466) is the setting that makes
|
|
77
|
+
unannotated declarations infer `@MainActor`.
|
|
78
|
+
|
|
79
|
+
Default isolation can be turned on three ways: the compiler flag
|
|
80
|
+
`-default-isolation MainActor`, `.defaultIsolation(MainActor.self)` in a
|
|
81
|
+
SwiftPM `swiftSettings` array, or the Xcode build setting. Use it for app
|
|
82
|
+
targets, scripts and other executables where most code serves the UI. Leave
|
|
83
|
+
library targets without it so they stay usable from any isolation.
|
|
84
84
|
|
|
85
85
|
```swift
|
|
86
|
-
//
|
|
87
|
-
final class
|
|
88
|
-
static let
|
|
89
|
-
var
|
|
86
|
+
// Target built with Default Actor Isolation = MainActor: no annotations needed.
|
|
87
|
+
final class PlaybackQueue {
|
|
88
|
+
static let current = PlaybackQueue() // main-actor protected
|
|
89
|
+
var upcoming: [Episode] = [] // main-actor protected
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
final class
|
|
93
|
-
let
|
|
94
|
-
var
|
|
92
|
+
final class EpisodeLibrary {
|
|
93
|
+
let renderer = WaveformRenderer()
|
|
94
|
+
var pinned: [Episode.ID] = []
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
func export() {
|
|
100
|
-
photoProcessor.exportAsPNG()
|
|
101
|
-
}
|
|
97
|
+
extension EpisodeLibrary: Archivable { // conformance is main-actor isolated too
|
|
98
|
+
func archive() -> Data { Data() }
|
|
102
99
|
}
|
|
103
100
|
```
|
|
104
101
|
|
|
105
|
-
**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
Nonisolated async functions now stay on the caller's actor by default instead
|
|
112
|
-
of hopping to the global concurrent executor. This is the
|
|
113
|
-
`nonisolated(nonsending)` behavior.
|
|
102
|
+
**Async functions stay where they are called (SE-0461).** With
|
|
103
|
+
`NonisolatedNonsendingByDefault` on, a `nonisolated` async function runs on the
|
|
104
|
+
caller's actor instead of jumping to the global concurrent executor. That
|
|
105
|
+
behavior is spelled `nonisolated(nonsending)`. Without the feature, Swift 6.0
|
|
106
|
+
and 6.1 semantics still apply and the function leaves the caller's actor.
|
|
114
107
|
|
|
115
108
|
```swift
|
|
116
|
-
class
|
|
117
|
-
func
|
|
118
|
-
// In Swift 6.2+, this runs on the caller's actor (e.g., MainActor)
|
|
119
|
-
// instead of hopping to a background thread.
|
|
120
|
-
// ...
|
|
121
|
-
}
|
|
109
|
+
final class WaveformRenderer {
|
|
110
|
+
func render(_ audio: Data) async -> Waveform { Waveform(samples: []) }
|
|
122
111
|
}
|
|
123
112
|
|
|
124
113
|
@MainActor
|
|
125
|
-
final class
|
|
126
|
-
let
|
|
127
|
-
|
|
128
|
-
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
|
|
129
|
-
guard let data = try await item.loadTransferable(type: Data.self) else {
|
|
130
|
-
return nil
|
|
131
|
-
}
|
|
132
|
-
// No data race -- photoProcessor stays on MainActor
|
|
133
|
-
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Use `@concurrent` to explicitly request background execution when needed.
|
|
114
|
+
final class EpisodeDetailModel {
|
|
115
|
+
let renderer = WaveformRenderer()
|
|
116
|
+
var waveform: Waveform?
|
|
139
117
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
freeing the calling actor to run other tasks.
|
|
144
|
-
|
|
145
|
-
```swift
|
|
146
|
-
class PhotoProcessor {
|
|
147
|
-
var cachedStickers: [String: Sticker] = [:]
|
|
148
|
-
|
|
149
|
-
func extractSticker(data: Data, with id: String) async -> Sticker {
|
|
150
|
-
if let sticker = cachedStickers[id] { return sticker }
|
|
151
|
-
|
|
152
|
-
let sticker = await Self.extractSubject(from: data)
|
|
153
|
-
cachedStickers[id] = sticker
|
|
154
|
-
return sticker
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
@concurrent
|
|
158
|
-
static func extractSubject(from data: Data) async -> Sticker {
|
|
159
|
-
// Expensive image processing -- runs on background thread pool
|
|
160
|
-
// ...
|
|
118
|
+
func open(_ url: URL) async throws {
|
|
119
|
+
let (audio, _) = try await URLSession.shared.data(from: url)
|
|
120
|
+
waveform = await renderer.render(audio) // stays on the main actor: no race reported
|
|
161
121
|
}
|
|
162
122
|
}
|
|
163
123
|
```
|
|
164
124
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
125
|
+
**`@concurrent` asks for the thread pool on purpose.** A `@concurrent` function
|
|
126
|
+
always runs on the concurrent pool, which frees the calling actor. To move a
|
|
127
|
+
piece of work off the caller:
|
|
128
|
+
|
|
129
|
+
1. Make the type, or just the function, `nonisolated`.
|
|
130
|
+
2. Add `@concurrent`.
|
|
131
|
+
3. Make the function `async` if it is not already.
|
|
132
|
+
4. Put `await` at each call site.
|
|
170
133
|
|
|
171
134
|
```swift
|
|
172
|
-
nonisolated struct
|
|
135
|
+
nonisolated struct TranscriptBuilder {
|
|
173
136
|
@concurrent
|
|
174
|
-
func
|
|
137
|
+
func build(from audio: Data) async -> Transcript? { Transcript(lines: []) }
|
|
175
138
|
}
|
|
176
139
|
|
|
177
|
-
//
|
|
178
|
-
|
|
140
|
+
// On the main actor:
|
|
141
|
+
transcripts[episode.id] = await builder.build(from: audio)
|
|
179
142
|
```
|
|
180
143
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
144
|
+
**Other 6.2 additions**, each covered in
|
|
145
|
+
[concurrency-patterns.md](references/concurrency-patterns.md):
|
|
146
|
+
|
|
147
|
+
- `Task.immediate` (SE-0472) runs synchronously on the current actor until its
|
|
148
|
+
first suspension instead of being enqueued; `Task.immediateDetached` adds
|
|
149
|
+
detached semantics. Use it where the enqueue delay is visible to the user,
|
|
150
|
+
for example `Task.immediate { await search.apply(query) }`.
|
|
151
|
+
- `Observations { }` (SE-0475) turns reads of `@Observable` properties into an
|
|
152
|
+
`AsyncSequence` with transactional updates:
|
|
153
|
+
`for await elapsed in Observations({ player.elapsed }) { scrubber.move(to: elapsed) }`.
|
|
154
|
+
- Isolated conformances let a main-actor type conform to a protocol whose
|
|
155
|
+
requirements it can only meet on the main actor. The compiler allows the
|
|
156
|
+
conformance only where that isolation holds.
|
|
157
|
+
- SE-0473 adds `.epoch` to `ContinuousClock` and `SuspendingClock`, but the
|
|
158
|
+
iOS 26.4 SDK does not ship it yet ("value of type 'ContinuousClock' has no
|
|
159
|
+
member 'epoch'"); check your SDK before relying on it.
|
|
160
|
+
|
|
161
|
+
## Isolation rules
|
|
162
|
+
|
|
163
|
+
- Shared mutable state needs one owner: an actor, a global actor, or a
|
|
164
|
+
`Sendable` synchronization primitive such as `Mutex` or `Atomic`. Unowned
|
|
165
|
+
shared state is a race.
|
|
166
|
+
- Code that reads or writes UI state is `@MainActor`. SwiftUI closures that the
|
|
167
|
+
framework documents as running off the main thread are the exception, and
|
|
168
|
+
they receive copies, not UI state (see
|
|
169
|
+
[swiftui-concurrency.md](references/swiftui-concurrency.md)).
|
|
170
|
+
- `nonisolated` suits members that only read immutable `let` storage or do pure
|
|
171
|
+
computation.
|
|
172
|
+
- `@concurrent` is the explicit way to leave the caller's actor.
|
|
173
|
+
- `nonisolated(unsafe)` is for state whose synchronization you have proven by
|
|
174
|
+
other means, after every checked option has been ruled out.
|
|
175
|
+
- An actor already serializes access. Adding `NSLock` or `DispatchSemaphore`
|
|
176
|
+
inside it buys nothing and can deadlock.
|
|
177
|
+
|
|
178
|
+
## Sendable rules
|
|
179
|
+
|
|
180
|
+
- A struct or enum is implicitly `Sendable` when all its stored properties are.
|
|
181
|
+
- Actors are `Sendable`. So are classes isolated to a global actor, such as a
|
|
182
|
+
`@MainActor` class; writing `Sendable` on them again is noise.
|
|
183
|
+
- Any other class qualifies only if it is `final` and every stored property is
|
|
184
|
+
a `let` of a `Sendable` type (a `let` holding a `Mutex` counts).
|
|
185
|
+
- `@unchecked Sendable` is the final option, and the declaration must carry a
|
|
186
|
+
note saying which lock or invariant the compiler cannot see.
|
|
187
|
+
- `sending` parameters and results (SE-0430) move a non-`Sendable` value into
|
|
188
|
+
another isolation region once, without making the type `Sendable`.
|
|
189
|
+
- `@preconcurrency import` is for third-party modules you cannot change. Record
|
|
190
|
+
a plan to remove it.
|
|
191
|
+
|
|
192
|
+
## Tasks and structured concurrency
|
|
193
|
+
|
|
194
|
+
| Tool | What it inherits | Use for |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| `async let` | Everything; child task | A fixed number of concurrent calls |
|
|
197
|
+
| `withTaskGroup` / `withThrowingTaskGroup` | Everything; child tasks | A number of calls known only at run time |
|
|
198
|
+
| `Task { }` | Actor, priority, task-locals | Starting async work from synchronous code |
|
|
199
|
+
| `Task.immediate { }` | Same as `Task`, starts at once | Latency-sensitive starts (OS 26 runtime) |
|
|
200
|
+
| `Task.detached { }` | Nothing | Deliberately breaking inheritance; see the rule below |
|
|
185
201
|
|
|
186
202
|
```swift
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
Use for latency-sensitive work that should begin without delay. There is also
|
|
191
|
-
`Task.immediateDetached` which combines immediate start with detached semantics.
|
|
192
|
-
|
|
193
|
-
### SE-0475: Transactional Observation (Observations)
|
|
194
|
-
|
|
195
|
-
`Observations { }` provides async observation of `@Observable` types via
|
|
196
|
-
`AsyncSequence`, enabling transactional change tracking.
|
|
203
|
+
async let profile = api.profile(for: userID)
|
|
204
|
+
async let badges = api.badges(for: userID)
|
|
205
|
+
let (loadedProfile, loadedBadges) = try await (profile, badges)
|
|
197
206
|
|
|
198
|
-
|
|
199
|
-
for
|
|
200
|
-
|
|
207
|
+
let episodes = try await withThrowingTaskGroup(of: Episode.self) { group in
|
|
208
|
+
for id in episodeIDs {
|
|
209
|
+
group.addTask { try await api.episode(id) }
|
|
210
|
+
}
|
|
211
|
+
var collected: [Episode] = []
|
|
212
|
+
for try await episode in group { collected.append(episode) }
|
|
213
|
+
return collected
|
|
201
214
|
}
|
|
202
215
|
```
|
|
203
216
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
The compiler ensures it is only used in a matching isolation context.
|
|
217
|
+
SE-0493 lets a `defer` body `await`, but the Swift 6.3.1 compiler in Xcode
|
|
218
|
+
26.4 still rejects it with "'async' call cannot occur in a defer body". Until
|
|
219
|
+
your toolchain accepts it, run async cleanup on every exit path yourself:
|
|
208
220
|
|
|
209
221
|
```swift
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
222
|
+
func exportSession() async throws -> Int {
|
|
223
|
+
let file = try await LogFile.open(named: "session")
|
|
224
|
+
do {
|
|
225
|
+
let written = try await file.write(pendingEntries)
|
|
226
|
+
await file.close()
|
|
227
|
+
return written
|
|
228
|
+
} catch {
|
|
229
|
+
await file.close()
|
|
230
|
+
throw error
|
|
218
231
|
}
|
|
219
232
|
}
|
|
220
|
-
|
|
221
|
-
@MainActor
|
|
222
|
-
struct ImageExporter {
|
|
223
|
-
var items: [any Exportable]
|
|
224
|
-
|
|
225
|
-
mutating func add(_ item: StickerModel) {
|
|
226
|
-
items.append(item) // OK -- ImageExporter is on MainActor
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
If `ImageExporter` were `nonisolated`, adding a `StickerModel` would fail:
|
|
232
|
-
"Main actor-isolated conformance of 'StickerModel' to 'Exportable' cannot be
|
|
233
|
-
used in nonisolated context."
|
|
234
|
-
|
|
235
|
-
### Clock Epochs
|
|
236
|
-
|
|
237
|
-
`ContinuousClock` and `SuspendingClock` now expose `.epoch` (SE-0473), enabling instant comparison and conversion between clock types.
|
|
238
|
-
|
|
239
|
-
```swift
|
|
240
|
-
let continuous = ContinuousClock()
|
|
241
|
-
let elapsed = continuous.now - continuous.epoch // Duration since system boot
|
|
242
233
|
```
|
|
243
234
|
|
|
244
|
-
##
|
|
245
|
-
|
|
246
|
-
1. All mutable shared state MUST be protected by an actor or global actor.
|
|
247
|
-
2. `@MainActor` for all UI-touching code. No exceptions.
|
|
248
|
-
3. Use `nonisolated` only for methods that access immutable (`let`) properties
|
|
249
|
-
or are pure computations.
|
|
250
|
-
4. Use `@concurrent` to explicitly move work off the caller's actor.
|
|
251
|
-
5. Never use `nonisolated(unsafe)` unless you have proven internal
|
|
252
|
-
synchronization and exhausted all other options.
|
|
253
|
-
6. Never add manual locks (`NSLock`, `DispatchSemaphore`) inside actors.
|
|
254
|
-
|
|
255
|
-
## Sendable Rules
|
|
235
|
+
## Cancellation
|
|
256
236
|
|
|
257
|
-
|
|
258
|
-
properties are `Sendable`.
|
|
259
|
-
2. Actors are implicitly `Sendable`.
|
|
260
|
-
3. `@MainActor` classes are implicitly `Sendable`. Do NOT add redundant
|
|
261
|
-
`Sendable` conformance.
|
|
262
|
-
4. Non-actor classes: must be `final` with all stored properties `let` and
|
|
263
|
-
`Sendable`.
|
|
264
|
-
5. `@unchecked Sendable` is a last resort. Document why the compiler cannot
|
|
265
|
-
prove safety.
|
|
266
|
-
6. Use `sending` parameters (SE-0430) for finer-grained isolation control.
|
|
267
|
-
7. Use `@preconcurrency import` only for third-party libraries you cannot
|
|
268
|
-
modify. Plan to remove it.
|
|
237
|
+
Cancellation is a request the task must honor itself.
|
|
269
238
|
|
|
270
|
-
|
|
239
|
+
- In loops, test `Task.isCancelled` or call `try Task.checkCancellation()`.
|
|
240
|
+
- In SwiftUI, start work with `.task`; it is cancelled when the view goes away.
|
|
241
|
+
- Release resources on cancellation with `withTaskCancellationHandler`.
|
|
242
|
+
- A `Task` you store must be cancelled by you, in `deinit` or `onDisappear`.
|
|
243
|
+
An unstructured task is never cancelled because its creator was.
|
|
271
244
|
|
|
272
|
-
|
|
245
|
+
## Actor reentrancy
|
|
273
246
|
|
|
274
|
-
|
|
247
|
+
An actor runs one piece of code at a time, but every `await` inside it lets
|
|
248
|
+
other calls in. State read before an `await` may be stale after it.
|
|
275
249
|
|
|
276
250
|
```swift
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return try await connection.read()
|
|
281
|
-
}
|
|
282
|
-
```
|
|
251
|
+
actor TicketCounter {
|
|
252
|
+
private var sold = 0
|
|
253
|
+
private let audit: AuditLog
|
|
283
254
|
|
|
284
|
-
|
|
285
|
-
```swift
|
|
286
|
-
Task { await doWork() }
|
|
287
|
-
```
|
|
255
|
+
init(audit: AuditLog) { self.audit = audit }
|
|
288
256
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
latency-sensitive work.
|
|
294
|
-
```swift
|
|
295
|
-
Task.immediate { await handleUserInput() }
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
**async let:** Fixed number of concurrent operations.
|
|
299
|
-
```swift
|
|
300
|
-
async let a = fetchA()
|
|
301
|
-
async let b = fetchB()
|
|
302
|
-
let result = try await (a, b)
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
**TaskGroup:** Dynamic number of concurrent operations.
|
|
306
|
-
```swift
|
|
307
|
-
try await withThrowingTaskGroup(of: Item.self) { group in
|
|
308
|
-
for id in ids {
|
|
309
|
-
group.addTask { try await fetch(id) }
|
|
257
|
+
func sellLosingUpdates() async {
|
|
258
|
+
let current = sold
|
|
259
|
+
await audit.record(current)
|
|
260
|
+
sold = current + 1 // another call may have run during the await
|
|
310
261
|
}
|
|
311
|
-
for try await item in group { process(item) }
|
|
312
|
-
}
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
## Task Cancellation
|
|
316
|
-
|
|
317
|
-
- Cancellation is cooperative. Check `Task.isCancelled` or call
|
|
318
|
-
`try Task.checkCancellation()` in loops.
|
|
319
|
-
- Use `.task` modifier in SwiftUI -- it handles cancellation on view disappear.
|
|
320
|
-
- Use `withTaskCancellationHandler` for cleanup.
|
|
321
|
-
- Cancel stored tasks in `deinit` or `onDisappear`.
|
|
322
|
-
|
|
323
|
-
## Actor Reentrancy
|
|
324
262
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
// WRONG: State may change during await
|
|
329
|
-
actor Counter {
|
|
330
|
-
var count = 0
|
|
331
|
-
func increment() async {
|
|
332
|
-
let current = count
|
|
333
|
-
await someWork()
|
|
334
|
-
count = current + 1 // BUG: count may have changed
|
|
263
|
+
func sell() async {
|
|
264
|
+
sold += 1 // read and write with no suspension between them
|
|
265
|
+
await audit.record(sold)
|
|
335
266
|
}
|
|
336
267
|
}
|
|
337
|
-
|
|
338
|
-
// CORRECT: Mutate synchronously, no reentrancy risk
|
|
339
|
-
actor Counter {
|
|
340
|
-
var count = 0
|
|
341
|
-
func increment() { count += 1 }
|
|
342
|
-
}
|
|
343
268
|
```
|
|
344
269
|
|
|
345
|
-
##
|
|
270
|
+
## Streams and continuations
|
|
346
271
|
|
|
347
|
-
|
|
272
|
+
Bridge APIs that call back many times with `AsyncStream`, and single-shot
|
|
273
|
+
callbacks with `withCheckedContinuation` or `withCheckedThrowingContinuation`.
|
|
274
|
+
A continuation is resumed exactly once on every path.
|
|
348
275
|
|
|
349
276
|
```swift
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
continuation.yield(
|
|
277
|
+
func readings(from scale: BluetoothScale) -> AsyncStream<Measurement<UnitMass>> {
|
|
278
|
+
AsyncStream { continuation in
|
|
279
|
+
let subscription = scale.observe { continuation.yield($0) }
|
|
280
|
+
continuation.onTermination = { _ in subscription.cancel() }
|
|
281
|
+
scale.startStreaming()
|
|
353
282
|
}
|
|
354
|
-
continuation.onTermination = { _ in delegate.stop() }
|
|
355
|
-
delegate.start()
|
|
356
283
|
}
|
|
357
284
|
```
|
|
358
285
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
##
|
|
363
|
-
|
|
364
|
-
- `@Observable`
|
|
365
|
-
-
|
|
366
|
-
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
-
|
|
412
|
-
-
|
|
413
|
-
-
|
|
414
|
-
-
|
|
415
|
-
|
|
416
|
-
-
|
|
417
|
-
-
|
|
418
|
-
-
|
|
419
|
-
|
|
420
|
-
-
|
|
286
|
+
Delegate bridges, cancellation-aware continuations and the full GCD mapping are
|
|
287
|
+
in [bridging-interop.md](references/bridging-interop.md).
|
|
288
|
+
|
|
289
|
+
## Observable models
|
|
290
|
+
|
|
291
|
+
- Isolate `@Observable` view models to `@MainActor`.
|
|
292
|
+
- Own them in a view with `@State`, which replaces `@StateObject`.
|
|
293
|
+
- Read their changes from async code with `Observations { }` (SE-0475).
|
|
294
|
+
|
|
295
|
+
## Locks and atomics
|
|
296
|
+
|
|
297
|
+
Actors are the default for shared mutable state. Locks fit when callers must
|
|
298
|
+
stay synchronous, when a path is hot enough that an actor hop costs too much,
|
|
299
|
+
or when C and Objective-C callbacks touch the state.
|
|
300
|
+
|
|
301
|
+
| Primitive | Minimum OS | Module | Use |
|
|
302
|
+
|---|---|---|---|
|
|
303
|
+
| `Mutex<Value>` | iOS 18 | `Synchronization` | First choice in new code; owns the state it guards, accessed through `withLock` |
|
|
304
|
+
| `OSAllocatedUnfairLock` | iOS 16 | `os` | Older deployment targets; offers ownership preconditions for debugging |
|
|
305
|
+
| `Atomic<Value>` | iOS 18 | `Synchronization` | Lock-free counters and flags; every call names a memory ordering |
|
|
306
|
+
|
|
307
|
+
Two absolute rules: no lock inside an actor, since that synchronizes twice, and
|
|
308
|
+
no lock held across an `await`, since that can deadlock. Details and a decision
|
|
309
|
+
guide: [synchronization-primitives.md](references/synchronization-primitives.md).
|
|
310
|
+
|
|
311
|
+
## Choosing how code moves between contexts
|
|
312
|
+
|
|
313
|
+
The same rule covers GCD, `MainActor.run` and `Task.detached`: **state
|
|
314
|
+
isolation in the declaration first, hop dynamically only when the declaration
|
|
315
|
+
cannot say it, and keep Dispatch only where an API requires a queue.** A
|
|
316
|
+
declaration is checked by the compiler; a hop inside a function body and a
|
|
317
|
+
Dispatch queue are not visible to that checking in the same way, so each step
|
|
318
|
+
down this list gives up guarantees.
|
|
319
|
+
|
|
320
|
+
| Goal | Default | Acceptable when | Do not |
|
|
321
|
+
|---|---|---|---|
|
|
322
|
+
| Run on the main actor | `@MainActor` on the type, function or closure | `await MainActor.run { }` for one short batch of UI updates inside an async function that must itself stay `nonisolated`; `Task { @MainActor in }` from synchronous nonisolated code; `MainActor.assumeIsolated { }` in a synchronous callback documented to arrive on the main thread | `DispatchQueue.main.async` in new code |
|
|
323
|
+
| Run away from the caller | `@concurrent` async function (6.2+); on 6.0 and 6.1 a `nonisolated` async function | `Task.detached` when the work must not inherit the actor, priority or task-local values, and you will cancel it yourself | `DispatchQueue.global().async` in new code |
|
|
324
|
+
| Protect state | An actor | `Mutex`, `Atomic` or `OSAllocatedUnfairLock` for synchronous callers | A serial queue used as a lock in new code |
|
|
325
|
+
| Fan out and join | `async let`, task groups | | `DispatchGroup`, `concurrentPerform` |
|
|
326
|
+
| Wait for a result | `await` | | `DispatchSemaphore.wait()` or `DispatchGroup.wait()` from async code, ever |
|
|
327
|
+
| API demands a queue | Pass a `DispatchQueue` and enter isolation at once in the callback | Back a custom actor with a `DispatchSerialQueue` as its executor (iOS 17) when it must share a queue with such an API | Mix queue-confined state with actor state |
|
|
328
|
+
|
|
329
|
+
So GCD is not banned as a type in the codebase; it is banned as the way new code
|
|
330
|
+
expresses isolation or coordination, because the compiler cannot prove what a
|
|
331
|
+
custom queue protects. `MainActor.run` and `Task.detached` are legitimate but
|
|
332
|
+
second choices, each with a named reason.
|
|
333
|
+
|
|
334
|
+
## Mistakes to catch
|
|
335
|
+
|
|
336
|
+
- Heavy computation on the main actor freezes the UI; move it to `@concurrent`.
|
|
337
|
+
- `@MainActor` on networking, parsing or model code that never touches the UI.
|
|
338
|
+
- An actor for code with no mutable state; use a struct or a free function.
|
|
339
|
+
- An actor wrapping immutable data; use a `Sendable` struct.
|
|
340
|
+
- `Task.detached` with no stated reason: it discards the actor, priority and
|
|
341
|
+
task-local values. Like any unstructured task it is also not cancelled with
|
|
342
|
+
its creator.
|
|
343
|
+
- Tasks started and forgotten; keep the handle and cancel it, or use `.task`.
|
|
344
|
+
- A long-lived stored task capturing `self` strongly; capture `[weak self]`.
|
|
345
|
+
- `DispatchSemaphore.wait()` in async code: it blocks a cooperative thread and
|
|
346
|
+
can deadlock the pool.
|
|
347
|
+
- Mutable state split across isolations in one type, for example some `var`s
|
|
348
|
+
on the main actor and others `nonisolated`. `nonisolated let` constants and
|
|
349
|
+
pure helpers next to isolated state are fine.
|
|
350
|
+
- `await MainActor.run { }` where `@MainActor` on the function would do.
|
|
351
|
+
- New Dispatch code for isolation or coordination (see the rule above).
|
|
352
|
+
|
|
353
|
+
## Review checklist
|
|
354
|
+
|
|
355
|
+
- [ ] Every piece of shared mutable state has one isolation owner.
|
|
356
|
+
- [ ] No access crosses isolation without the compiler's approval.
|
|
357
|
+
- [ ] Tasks are cancelled when their result is no longer wanted.
|
|
358
|
+
- [ ] Nothing blocking runs on `@MainActor`.
|
|
359
|
+
- [ ] No manual locks inside actors.
|
|
360
|
+
- [ ] `Sendable` conformances are true; each `@unchecked` names its invariant.
|
|
361
|
+
- [ ] No assumption about actor state survives an `await`.
|
|
362
|
+
- [ ] Each `@preconcurrency import` has a removal plan.
|
|
363
|
+
- [ ] CPU-heavy work is `@concurrent`, not on the main actor.
|
|
364
|
+
- [ ] SwiftUI work starts from `.task`, not hand-managed tasks.
|
|
421
365
|
|
|
422
366
|
## References
|
|
423
367
|
|
|
424
|
-
- [
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
- [
|
|
428
|
-
|
|
429
|
-
- [
|
|
430
|
-
|
|
368
|
+
- [concurrency-patterns.md](references/concurrency-patterns.md): approachable
|
|
369
|
+
concurrency in depth, before and after examples, `weak let`, global state,
|
|
370
|
+
build settings and migration.
|
|
371
|
+
- [approachable-concurrency.md](references/approachable-concurrency.md): quick
|
|
372
|
+
reference for detecting the mode and fixing code inside it.
|
|
373
|
+
- [swiftui-concurrency.md](references/swiftui-concurrency.md): main-actor
|
|
374
|
+
views, framework callbacks that run off the main thread, `.task`, view models.
|
|
375
|
+
- [synchronization-primitives.md](references/synchronization-primitives.md):
|
|
376
|
+
`Mutex`, `OSAllocatedUnfairLock`, `Atomic`, memory ordering, locks versus
|
|
377
|
+
actors.
|
|
378
|
+
- [bridging-interop.md](references/bridging-interop.md): continuations,
|
|
379
|
+
delegates, streams from callbacks, and replacing GCD.
|
|
380
|
+
- [diagnostics.md](references/diagnostics.md): compiler messages and fixes,
|
|
381
|
+
adoption order, Thread Sanitizer.
|
|
382
|
+
- [async-algorithms.md](references/async-algorithms.md): the
|
|
383
|
+
swift-async-algorithms package.
|