@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
package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md
CHANGED
|
@@ -1,415 +1,267 @@
|
|
|
1
|
-
# Background Task Patterns
|
|
1
|
+
# Background Task Patterns
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
SwiftUI
|
|
3
|
+
Deeper material for the `background-processing` skill: debugging, error codes,
|
|
4
|
+
checkpointed processing, background transfer details, silent push limits, the
|
|
5
|
+
SwiftUI modifier and continued-processing specifics.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Debugging
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- [Advanced BGProcessingTask Patterns](#advanced-bgprocessingtask-patterns)
|
|
11
|
-
- [Background URLSession - Extended Patterns](#background-urlsession--extended-patterns)
|
|
12
|
-
- [Background Push - Extended Patterns](#background-push--extended-patterns)
|
|
13
|
-
- [SwiftUI BackgroundTask Modifier](#swiftui-backgroundtask-modifier)
|
|
14
|
-
- [BGContinuedProcessingTask - Extended Patterns](#bgcontinuedprocessingtask--extended-patterns)
|
|
9
|
+
### Forcing a launch from the debugger
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
Background tasks rarely fire while you are watching. With the app running
|
|
12
|
+
under Xcode, pause it and type in the LLDB console:
|
|
17
13
|
|
|
18
|
-
### Simulating Task Launches in Xcode
|
|
19
|
-
|
|
20
|
-
Use the LLDB console to trigger tasks instantly during development. The app must
|
|
21
|
-
be running on a device in the debugger with a breakpoint hit or paused.
|
|
22
|
-
|
|
23
|
-
These are Apple-documented private functions for development only. Do not
|
|
24
|
-
include references to them in App Store-submitted code.
|
|
25
|
-
|
|
26
|
-
```swift
|
|
27
|
-
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.app.refresh"]
|
|
28
14
|
```
|
|
29
|
-
|
|
30
|
-
For processing tasks:
|
|
31
|
-
|
|
32
|
-
```swift
|
|
33
|
-
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.app.db-cleanup"]
|
|
15
|
+
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"org.sample.photos.refresh"]
|
|
34
16
|
```
|
|
35
17
|
|
|
36
|
-
To
|
|
18
|
+
Resume, and the registered handler runs. To test the expiry path, start the
|
|
19
|
+
task that way, pause again and send:
|
|
37
20
|
|
|
38
|
-
```
|
|
39
|
-
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"
|
|
21
|
+
```
|
|
22
|
+
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"org.sample.photos.refresh"]
|
|
40
23
|
```
|
|
41
24
|
|
|
42
|
-
|
|
25
|
+
These selectors are private. Apple documents them for development only; they
|
|
26
|
+
must never appear in code that ships to the App Store.
|
|
43
27
|
|
|
44
|
-
|
|
28
|
+
### What is scheduled right now
|
|
45
29
|
|
|
46
30
|
```swift
|
|
47
|
-
BGTaskScheduler.shared.getPendingTaskRequests {
|
|
48
|
-
for request in
|
|
49
|
-
|
|
31
|
+
BGTaskScheduler.shared.getPendingTaskRequests { pending in
|
|
32
|
+
for request in pending {
|
|
33
|
+
Log.background.debug("\(request.identifier) not before \(String(describing: request.earliestBeginDate))")
|
|
50
34
|
}
|
|
51
35
|
}
|
|
52
36
|
```
|
|
53
37
|
|
|
54
|
-
###
|
|
38
|
+
### Why a task never runs
|
|
55
39
|
|
|
56
|
-
| Symptom |
|
|
57
|
-
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
| `.notPermitted`
|
|
62
|
-
| `.unavailable`
|
|
63
|
-
| `.tooManyPendingTaskRequests` |
|
|
40
|
+
| Symptom | Likely cause | Fix |
|
|
41
|
+
|---------|--------------|-----|
|
|
42
|
+
| Never fires anywhere | Identifier absent from `BGTaskSchedulerPermittedIdentifiers` | Add the exact string |
|
|
43
|
+
| Never fires anywhere | Background modes off | Enable `fetch` or `processing` |
|
|
44
|
+
| Never fires on one device | Background App Refresh turned off, or Low Power Mode on | Read `UIApplication.shared.backgroundRefreshStatus`; Low Power Mode also shortens runtime |
|
|
45
|
+
| `submit` throws `.notPermitted` | Code and Info.plist spell the identifier differently | Make them match |
|
|
46
|
+
| `submit` throws `.unavailable` | Called from an app extension | Schedule from the app; extensions cannot use `BGTaskScheduler` |
|
|
47
|
+
| `submit` throws `.tooManyPendingTaskRequests` | Over the limit of 1 refresh and 10 processing requests pending | Cancel stale requests before adding new ones |
|
|
48
|
+
| `submit` throws `.immediateRunIneligible` | A continued processing request used `.fail` and could not start now | Tell the user, or submit with `.queue` |
|
|
64
49
|
|
|
65
|
-
|
|
66
|
-
|
|
50
|
+
A new request with the same identifier as one that has not run yet replaces
|
|
51
|
+
the old one, so resubmitting is safe.
|
|
67
52
|
|
|
68
|
-
##
|
|
53
|
+
## Processing tasks in depth
|
|
69
54
|
|
|
70
|
-
###
|
|
71
|
-
|
|
72
|
-
Use `requiresExternalPower` and `requiresNetworkConnectivity` to ensure the
|
|
73
|
-
system only launches your task when conditions are met:
|
|
55
|
+
### Overnight work on power and network
|
|
74
56
|
|
|
75
57
|
```swift
|
|
76
|
-
func
|
|
77
|
-
let request = BGProcessingTaskRequest(
|
|
78
|
-
identifier: "com.example.app.full-sync"
|
|
79
|
-
)
|
|
80
|
-
// Only run when charging and connected to network
|
|
58
|
+
func scheduleNightlyBackup() {
|
|
59
|
+
let request = BGProcessingTaskRequest(identifier: "org.sample.photos.backup")
|
|
81
60
|
request.requiresExternalPower = true
|
|
82
61
|
request.requiresNetworkConnectivity = true
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if let twoAM = Calendar.current.nextDate(
|
|
87
|
-
after: Date(),
|
|
88
|
-
matching: components,
|
|
62
|
+
request.earliestBeginDate = Calendar.current.nextDate(
|
|
63
|
+
after: .now,
|
|
64
|
+
matching: DateComponents(hour: 2, minute: 0),
|
|
89
65
|
matchingPolicy: .nextTime
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
do {
|
|
95
|
-
try BGTaskScheduler.shared.submit(request)
|
|
96
|
-
} catch {
|
|
97
|
-
print("Failed to schedule sync: \(error)")
|
|
98
|
-
}
|
|
66
|
+
)
|
|
67
|
+
try? BGTaskScheduler.shared.submit(request)
|
|
99
68
|
}
|
|
100
69
|
```
|
|
101
70
|
|
|
102
|
-
|
|
71
|
+
`nextDate(after:matching:matchingPolicy:)` finds the next 2 AM, which becomes
|
|
72
|
+
the lower bound for the run.
|
|
73
|
+
|
|
74
|
+
### Checkpointing
|
|
103
75
|
|
|
104
|
-
|
|
76
|
+
Long jobs rarely finish in one window. Save a cursor after each item and pick
|
|
77
|
+
up where the last run stopped:
|
|
105
78
|
|
|
106
79
|
```swift
|
|
107
|
-
func
|
|
80
|
+
func backUp(_ task: BGProcessingTask, assets: [PhotoAsset]) {
|
|
81
|
+
nonisolated(unsafe) let task = task
|
|
82
|
+
let cursorKey = "backup.cursor"
|
|
108
83
|
let work = Task {
|
|
109
|
-
|
|
110
|
-
forKey:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
forKey: "migrationLastIndex"
|
|
121
|
-
)
|
|
84
|
+
do {
|
|
85
|
+
let start = UserDefaults.standard.integer(forKey: cursorKey)
|
|
86
|
+
for (offset, asset) in assets.dropFirst(start).enumerated() {
|
|
87
|
+
try Task.checkCancellation()
|
|
88
|
+
try await upload(asset)
|
|
89
|
+
UserDefaults.standard.set(start + offset + 1, forKey: cursorKey)
|
|
90
|
+
}
|
|
91
|
+
UserDefaults.standard.removeObject(forKey: cursorKey)
|
|
92
|
+
task.setTaskCompleted(success: true)
|
|
93
|
+
} catch {
|
|
94
|
+
task.setTaskCompleted(success: false)
|
|
122
95
|
}
|
|
123
|
-
|
|
124
|
-
task.setTaskCompleted(success: true)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
task.expirationHandler = {
|
|
128
|
-
work.cancel()
|
|
129
|
-
// Progress is saved -- next launch picks up where we left off
|
|
130
|
-
task.setTaskCompleted(success: false)
|
|
131
96
|
}
|
|
97
|
+
task.expirationHandler = { work.cancel() }
|
|
132
98
|
}
|
|
133
99
|
```
|
|
134
100
|
|
|
135
|
-
|
|
101
|
+
When the window closes, the handler cancels the work, `checkCancellation()`
|
|
102
|
+
throws, and the `catch` reports `success: false`: one completion path, as in
|
|
103
|
+
`perform` and `handleExport`. The saved cursor lets the next run skip what is
|
|
104
|
+
done.
|
|
105
|
+
The `nonisolated(unsafe)` copy lets the non-`Sendable` `BGTask` be shared by
|
|
106
|
+
the work `Task` and the expiration handler under Swift 6.
|
|
136
107
|
|
|
137
|
-
|
|
108
|
+
## Background URLSession details
|
|
138
109
|
|
|
139
|
-
|
|
140
|
-
let config = URLSessionConfiguration.background(
|
|
141
|
-
withIdentifier: "com.example.app.background-transfer"
|
|
142
|
-
)
|
|
110
|
+
### Configuration options
|
|
143
111
|
|
|
144
|
-
|
|
145
|
-
|
|
112
|
+
```swift
|
|
113
|
+
let config = URLSessionConfiguration.background(withIdentifier: "org.sample.podcasts.transfers")
|
|
146
114
|
config.isDiscretionary = true
|
|
147
|
-
|
|
148
|
-
// sessionSendsLaunchEvents = true: app relaunched when transfer completes
|
|
149
115
|
config.sessionSendsLaunchEvents = true
|
|
150
|
-
|
|
151
|
-
// Set reasonable timeouts
|
|
152
|
-
config.timeoutIntervalForResource = 60 * 60 * 24 * 7 // 7 days
|
|
153
|
-
|
|
154
|
-
// Allow cellular (default is true)
|
|
116
|
+
config.timeoutIntervalForResource = 7 * 24 * 60 * 60
|
|
155
117
|
config.allowsCellularAccess = true
|
|
156
118
|
```
|
|
157
119
|
|
|
158
|
-
|
|
120
|
+
- `isDiscretionary`: for transfers that are not urgent; the system waits for
|
|
121
|
+
Wi-Fi and power.
|
|
122
|
+
- `sessionSendsLaunchEvents`: relaunch the app when transfers complete.
|
|
123
|
+
- `timeoutIntervalForResource`: how long a transfer may take overall; one week
|
|
124
|
+
here.
|
|
125
|
+
- `allowsCellularAccess` is already `true` by default; set it to `false` to
|
|
126
|
+
keep large transfers on Wi-Fi.
|
|
159
127
|
|
|
160
|
-
|
|
161
|
-
func uploadFile(at fileURL: URL) {
|
|
162
|
-
var request = URLRequest(url: URL(string: "https://api.example.com/upload")!)
|
|
163
|
-
request.httpMethod = "POST"
|
|
164
|
-
let uploadTask = session.uploadTask(with: request, fromFile: fileURL)
|
|
165
|
-
uploadTask.resume()
|
|
166
|
-
}
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
**Important:** Background sessions only support `uploadTask(with:fromFile:)` and
|
|
170
|
-
`downloadTask(with:)`. Data tasks, `uploadTask(with:from:)` (Data), and
|
|
171
|
-
closure/async-based tasks are **not** supported.
|
|
172
|
-
|
|
173
|
-
### Handling App Relaunch
|
|
174
|
-
|
|
175
|
-
When the system completes a background transfer and your app is not running, it
|
|
176
|
-
relaunches the app. You must:
|
|
177
|
-
|
|
178
|
-
1. Store the completion handler from
|
|
179
|
-
`application(_:handleEventsForBackgroundURLSession:completionHandler:)`
|
|
180
|
-
2. Recreate the `URLSession` with the **same identifier**
|
|
181
|
-
3. Call the stored completion handler in `urlSessionDidFinishEvents`
|
|
128
|
+
### Uploads
|
|
182
129
|
|
|
183
130
|
```swift
|
|
184
|
-
|
|
185
|
-
func
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
)
|
|
190
|
-
// Recreating session with the same identifier reconnects to the transfer
|
|
191
|
-
_ = DownloadManager.shared.session // trigger lazy init
|
|
192
|
-
DownloadManager.shared.completionHandler = completionHandler
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// In your URLSessionDelegate
|
|
196
|
-
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
|
|
197
|
-
Task { @MainActor in
|
|
198
|
-
self.completionHandler?()
|
|
199
|
-
self.completionHandler = nil
|
|
200
|
-
}
|
|
131
|
+
@MainActor
|
|
132
|
+
func sendRecording(at file: URL, to endpoint: URL) {
|
|
133
|
+
var request = URLRequest(url: endpoint)
|
|
134
|
+
request.httpMethod = "PUT"
|
|
135
|
+
let job = EpisodeDownloader.shared.session.uploadTask(with: request, fromFile: file)
|
|
136
|
+
job.resume()
|
|
201
137
|
}
|
|
202
138
|
```
|
|
203
139
|
|
|
204
|
-
|
|
140
|
+
Background sessions accept only `uploadTask(with:fromFile:)` and
|
|
141
|
+
`downloadTask(with:)` style tasks. Data tasks, uploads from in-memory `Data`
|
|
142
|
+
(`uploadTask(with:from:)`) and every completion-handler or async variant are
|
|
143
|
+
rejected.
|
|
205
144
|
|
|
206
|
-
|
|
207
|
-
func urlSession(
|
|
208
|
-
_ session: URLSession,
|
|
209
|
-
task: URLSessionTask,
|
|
210
|
-
didCompleteWithError error: (any Error)?
|
|
211
|
-
) {
|
|
212
|
-
guard let error else { return } // Success handled in didFinishDownloadingTo
|
|
213
|
-
|
|
214
|
-
let nsError = error as NSError
|
|
215
|
-
|
|
216
|
-
// Check if download can be resumed
|
|
217
|
-
if let resumeData = nsError.userInfo[NSURLSessionDownloadTaskResumeData] as? Data {
|
|
218
|
-
// Store resumeData and retry later
|
|
219
|
-
let downloadTask = session.downloadTask(withResumeData: resumeData)
|
|
220
|
-
downloadTask.resume()
|
|
221
|
-
return
|
|
222
|
-
}
|
|
145
|
+
### Relaunch sequence
|
|
223
146
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
```
|
|
147
|
+
1. The system calls `application(_:handleEventsForBackgroundURLSession:completionHandler:)`.
|
|
148
|
+
Keep the handler.
|
|
149
|
+
2. Create the session again with the same identifier; touching the lazy
|
|
150
|
+
`session` property is enough to reconnect to the running transfers.
|
|
151
|
+
3. The delegate receives the queued events, then
|
|
152
|
+
`urlSessionDidFinishEvents(forBackgroundURLSession:)`, where you call the
|
|
153
|
+
stored handler on the main actor.
|
|
234
154
|
|
|
235
|
-
###
|
|
155
|
+
### Resuming after failure
|
|
236
156
|
|
|
237
157
|
```swift
|
|
238
|
-
func urlSession(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
) {
|
|
245
|
-
let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
|
|
246
|
-
// Update UI on main actor if app is in foreground
|
|
247
|
-
Task { @MainActor in
|
|
248
|
-
DownloadProgressStore.shared.update(
|
|
249
|
-
taskID: downloadTask.taskIdentifier,
|
|
250
|
-
progress: progress
|
|
251
|
-
)
|
|
158
|
+
nonisolated func urlSession(_ s: URLSession, task job: URLSessionTask, didCompleteWithError error: (any Error)?) {
|
|
159
|
+
guard let error = error as NSError? else { return }
|
|
160
|
+
if let partial = error.userInfo[NSURLSessionDownloadTaskResumeData] as? Data {
|
|
161
|
+
s.downloadTask(withResumeData: partial).resume()
|
|
162
|
+
} else if error.code == NSURLErrorNetworkConnectionLost, let url = job.originalRequest?.url {
|
|
163
|
+
s.downloadTask(with: url).resume()
|
|
252
164
|
}
|
|
253
165
|
}
|
|
254
166
|
```
|
|
255
167
|
|
|
256
|
-
|
|
168
|
+
Resume data lets the transfer continue from where it stopped. Without it, a
|
|
169
|
+
dropped connection is retried from the start using the original URL.
|
|
257
170
|
|
|
258
|
-
###
|
|
171
|
+
### Progress
|
|
259
172
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
{
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
"type": "new-message",
|
|
268
|
-
"conversation-id": "abc-123"
|
|
173
|
+
```swift
|
|
174
|
+
nonisolated func urlSession(_ s: URLSession, downloadTask job: URLSessionDownloadTask,
|
|
175
|
+
didWriteData chunk: Int64, totalBytesWritten written: Int64,
|
|
176
|
+
totalBytesExpectedToWrite expected: Int64) {
|
|
177
|
+
guard expected > 0 else { return }
|
|
178
|
+
let fraction = Double(written) / Double(expected)
|
|
179
|
+
Task { @MainActor in DownloadProgress.shared.update(job.taskIdentifier, fraction) }
|
|
269
180
|
}
|
|
270
181
|
```
|
|
271
182
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
-
|
|
288
|
-
- The
|
|
289
|
-
|
|
290
|
-
- If the user force-quits the app, background pushes stop until the next manual
|
|
291
|
-
launch.
|
|
292
|
-
- Use `apns-priority: 5`; high-priority pushes are for user-visible
|
|
293
|
-
notifications, not silent refresh.
|
|
294
|
-
|
|
295
|
-
### Handling Push with Async Work
|
|
183
|
+
UI updates go through the main actor. This method and the failure handler
|
|
184
|
+
above are `nonisolated` because they belong to the `@MainActor` downloader from
|
|
185
|
+
the parent skill, while the session calls them on its own queue.
|
|
186
|
+
|
|
187
|
+
## Silent push details
|
|
188
|
+
|
|
189
|
+
- `content-available: 1` is required; custom keys sit beside `aps` at the top
|
|
190
|
+
level.
|
|
191
|
+
- Leave out `alert`, `badge` and `sound`. Any visible key turns the push into a
|
|
192
|
+
user-facing notification with different handling.
|
|
193
|
+
- APNs treats these pushes as low priority with no delivery promise. Send no
|
|
194
|
+
more than two or three an hour. When several are held, the system may keep
|
|
195
|
+
only the newest and drop the rest.
|
|
196
|
+
- If the user force-quits the app, silent pushes stop until they open it
|
|
197
|
+
again by hand.
|
|
198
|
+
- Use priority `5`; priority `10` is meant for notifications the user sees.
|
|
199
|
+
- The app has about 30 seconds to call the completion handler. Missing that
|
|
200
|
+
shrinks its future background push budget.
|
|
296
201
|
|
|
297
202
|
```swift
|
|
298
|
-
func
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
return
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
Task {
|
|
310
|
-
do {
|
|
311
|
-
switch type {
|
|
312
|
-
case "new-message":
|
|
313
|
-
let conversationID = userInfo["conversation-id"] as? String
|
|
314
|
-
let fetched = try await MessageService.shared
|
|
315
|
-
.fetchMessages(for: conversationID)
|
|
316
|
-
completionHandler(fetched ? .newData : .noData)
|
|
317
|
-
|
|
318
|
-
case "config-update":
|
|
319
|
-
try await ConfigService.shared.refreshConfig()
|
|
320
|
-
completionHandler(.newData)
|
|
321
|
-
|
|
322
|
-
default:
|
|
323
|
-
completionHandler(.noData)
|
|
324
|
-
}
|
|
325
|
-
} catch {
|
|
326
|
-
completionHandler(.failed)
|
|
327
|
-
}
|
|
203
|
+
func handleSilentPush(_ payload: [AnyHashable: Any]) async -> UIBackgroundFetchResult {
|
|
204
|
+
switch payload["kind"] as? String {
|
|
205
|
+
case "album-changed":
|
|
206
|
+
return await LibrarySync.shared.syncChangedAlbums() ? .newData : .noData
|
|
207
|
+
case "purge-cache":
|
|
208
|
+
ThumbnailCache.shared.removeAll()
|
|
209
|
+
return .newData
|
|
210
|
+
default:
|
|
211
|
+
return .noData
|
|
328
212
|
}
|
|
329
213
|
}
|
|
330
214
|
```
|
|
331
215
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
budget.
|
|
216
|
+
The fetch-completion delegate method calls this and forwards the result to its
|
|
217
|
+
handler; a payload with no known kind reports `.noData`.
|
|
335
218
|
|
|
336
|
-
## SwiftUI
|
|
219
|
+
## SwiftUI `.backgroundTask`
|
|
337
220
|
|
|
338
|
-
SwiftUI
|
|
339
|
-
|
|
221
|
+
A SwiftUI app can declare the handler on the scene instead of registering it
|
|
222
|
+
by hand:
|
|
340
223
|
|
|
341
224
|
```swift
|
|
342
225
|
@main
|
|
343
|
-
struct
|
|
226
|
+
struct PhotosApp: App {
|
|
344
227
|
var body: some Scene {
|
|
345
|
-
WindowGroup {
|
|
346
|
-
.backgroundTask(.appRefresh("
|
|
347
|
-
await
|
|
348
|
-
|
|
349
|
-
scheduleAppRefresh()
|
|
228
|
+
WindowGroup { LibraryView() }
|
|
229
|
+
.backgroundTask(.appRefresh("org.sample.photos.refresh")) {
|
|
230
|
+
_ = await LibrarySync.shared.syncChangedAlbums()
|
|
231
|
+
LibrarySync.shared.scheduleRefresh()
|
|
350
232
|
}
|
|
351
233
|
}
|
|
352
234
|
}
|
|
353
235
|
```
|
|
354
236
|
|
|
355
|
-
|
|
356
|
-
Info.plist
|
|
357
|
-
|
|
237
|
+
The rest of the contract is unchanged: the identifier is still listed in
|
|
238
|
+
Info.plist, background modes are still on, the next run is still scheduled,
|
|
239
|
+
and the work must still stop cleanly when cancelled.
|
|
358
240
|
|
|
359
|
-
##
|
|
241
|
+
## Continued processing specifics
|
|
360
242
|
|
|
361
|
-
###
|
|
243
|
+
### Resources
|
|
362
244
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
(`com.apple.developer.background-tasks.continued-processing.gpu`) for GPU work:
|
|
245
|
+
Call `BGTaskScheduler.supportedResources` and request `.gpu` only when it is
|
|
246
|
+
listed. GPU use also depends on the Background GPU Access entitlement.
|
|
366
247
|
|
|
367
|
-
|
|
368
|
-
let supported = BGTaskScheduler.supportedResources
|
|
369
|
-
if supported.contains(.gpu) {
|
|
370
|
-
request.requiredResources = .gpu
|
|
371
|
-
}
|
|
372
|
-
```
|
|
248
|
+
### Submission strategy
|
|
373
249
|
|
|
374
|
-
|
|
250
|
+
`BGContinuedProcessingTaskRequest.SubmissionStrategy` has two cases:
|
|
375
251
|
|
|
376
|
-
|
|
377
|
-
|
|
252
|
+
| Strategy | Behaviour | Pick it when |
|
|
253
|
+
|----------|-----------|--------------|
|
|
254
|
+
| `.queue` | Accepted and started as soon as the system allows | The job can start a little later without harm |
|
|
255
|
+
| `.fail` | `submit` fails unless the task can start now | The user is waiting and a delayed start is useless |
|
|
378
256
|
|
|
379
|
-
|
|
380
|
-
|---|---|
|
|
381
|
-
| `.queue` | Task is queued and starts as soon as possible |
|
|
382
|
-
| `.fail` | Submission fails immediately if can't run now |
|
|
257
|
+
### Cancellation from the Live Activity
|
|
383
258
|
|
|
384
|
-
|
|
385
|
-
|
|
259
|
+
The user can stop the job from the system Live Activity. That arrives through
|
|
260
|
+
`expirationHandler`: cancel the work and delete partial output there; the
|
|
261
|
+
cancelled work then completes with `success: false`, as `handleExport` does.
|
|
386
262
|
|
|
387
|
-
###
|
|
263
|
+
### Progress drives survival
|
|
388
264
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
```swift
|
|
393
|
-
task.expirationHandler = {
|
|
394
|
-
// Clean up partial work
|
|
395
|
-
cleanupPartialExport()
|
|
396
|
-
task.setTaskCompleted(success: false)
|
|
397
|
-
}
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
### Progress Reporting
|
|
401
|
-
|
|
402
|
-
The system uses your `Progress` object to decide termination priority. Tasks
|
|
403
|
-
with no progress updates are terminated first under resource pressure:
|
|
404
|
-
|
|
405
|
-
```swift
|
|
406
|
-
// Report fine-grained progress
|
|
407
|
-
let progress = task.progress
|
|
408
|
-
progress.totalUnitCount = Int64(totalItems)
|
|
409
|
-
|
|
410
|
-
for (index, item) in items.enumerated() {
|
|
411
|
-
try Task.checkCancellation()
|
|
412
|
-
await process(item)
|
|
413
|
-
progress.completedUnitCount = Int64(index + 1)
|
|
414
|
-
}
|
|
415
|
-
```
|
|
265
|
+
When resources run short, the system reads each task's `Progress` to decide
|
|
266
|
+
what to end first. A task that never updates progress is the first to go, so
|
|
267
|
+
report small steps often.
|