@mmerterden/multi-agent-pipeline 20.6.0 → 20.8.0

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.
Files changed (326) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +328 -329
  5. package/package.json +2 -2
  6. package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
  7. package/pipeline/scripts/_notices.mjs +12 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  9. package/pipeline/skills/.skill-manifest.json +106 -106
  10. package/pipeline/skills/shared/README.md +71 -71
  11. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
  12. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  13. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  14. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  15. package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
  16. package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
  17. package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
  18. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  19. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  20. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  21. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  22. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  23. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  24. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  25. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  26. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  27. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  28. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  29. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  30. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  31. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  32. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  33. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  34. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  35. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  37. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  38. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  39. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  40. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  41. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  42. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  43. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  44. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  45. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  46. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  47. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  48. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  49. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  50. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  51. package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
  52. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  53. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  54. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  55. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
  56. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  57. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  58. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  59. package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
  60. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
  61. package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
  62. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  63. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  64. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  65. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  66. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  67. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  68. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  69. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  70. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  71. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  72. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  73. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  74. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  75. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  76. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  77. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  78. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  79. package/pipeline/skills/shared/external/council/SKILL.md +1 -0
  80. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  81. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  82. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  83. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
  84. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
  85. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  86. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  87. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  88. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  89. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  90. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  91. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  92. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  93. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  94. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  95. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  96. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  97. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  98. package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
  99. package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
  100. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
  101. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  102. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  103. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  104. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  105. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  106. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  107. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
  108. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
  109. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  110. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  111. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  112. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  113. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  114. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
  115. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  116. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  117. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  118. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  119. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
  120. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  121. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  122. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  123. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  124. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  125. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  126. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  127. package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
  128. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  129. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  130. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  131. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  132. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  133. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  134. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
  135. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
  136. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
  137. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
  138. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
  139. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
  140. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
  141. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
  142. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
  143. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
  144. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
  145. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
  146. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
  147. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
  148. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
  149. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
  150. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  151. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  152. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  153. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  154. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  155. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  156. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  157. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  158. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  159. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  160. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  161. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  162. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  163. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
  164. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  165. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  166. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  167. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  168. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  169. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  170. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  171. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  172. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  173. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  174. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  175. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  176. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  177. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  178. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  179. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  180. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  181. package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
  182. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  183. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  184. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  185. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  186. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
  187. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
  188. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  189. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  190. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  191. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
  192. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
  193. package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
  194. package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
  195. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  196. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  197. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  198. package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
  199. package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
  200. package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
  201. package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
  202. package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
  203. package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
  204. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
  205. package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
  206. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  207. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  208. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  209. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  210. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  211. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  212. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  213. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  214. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  215. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  216. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  217. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  218. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  219. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  220. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  221. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  222. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  223. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  224. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  225. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  226. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  227. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  228. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  229. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  230. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  231. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  232. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  233. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  234. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  235. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  236. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  237. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  238. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  239. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  240. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  241. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  242. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  243. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  244. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  245. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  246. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  247. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  248. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  249. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  250. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  251. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  252. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  253. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  254. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  255. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  256. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  257. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  258. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  259. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  260. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  261. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  262. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  263. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  264. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  265. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  266. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  267. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  268. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  269. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  270. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  271. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  272. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  273. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  274. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  275. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  276. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  277. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  278. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  279. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  280. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  281. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  282. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  283. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  284. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  285. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  286. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  287. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  288. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  289. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  290. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  291. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  292. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  293. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  294. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  295. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  296. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  297. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  298. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  299. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  300. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  301. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  302. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  303. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  304. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  305. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  306. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  307. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  308. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  309. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  310. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
  311. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
  312. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  313. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  314. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
  315. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  316. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  317. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  318. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
  319. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  320. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  321. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
  322. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
  323. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
  324. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  325. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  326. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,500 +1,388 @@
1
1
  ---
2
2
  name: background-processing
3
- description: "Schedule and execute background work on iOS using BGTaskScheduler. Use when registering BGAppRefreshTask for short background fetches, BGProcessingTask for long-running maintenance, BGContinuedProcessingTask (iOS 26+) for foreground-started work that continues in background, background URLSession downloads, or background push notifications. Covers Info.plist configuration, expiration handling, task completion, and debugging with simulated launches."
3
+ description: "iOS background work with BGTaskScheduler: BGAppRefreshTask short fetches, BGProcessingTask long maintenance, BGContinuedProcessingTask (iOS 26+) for user-started work, background URLSession transfers, silent pushes, Info.plist identifiers and modes, handler registration, expiration and completion, simulated launches for debugging. Use when adding, reviewing or debugging background work. Not for visible notification UI."
4
4
  metadata:
5
- source: "dpearson2699/swift-ios-skills (PolyForm Perimeter 1.0.0)"
5
+ source: multi-agent-pipeline
6
6
  ---
7
+
7
8
  # Background Processing
8
9
 
9
- Register, schedule, and execute background work on iOS using the BackgroundTasks
10
- framework, background URLSession, and background push notifications.
10
+ iOS gives an app background time only on its own terms. You describe the work,
11
+ the system chooses the moment, or skips it entirely, and your code has to finish
12
+ or stop cleanly whenever it is told to. This skill covers the BackgroundTasks
13
+ framework, background `URLSession` transfers and silent pushes.
11
14
 
12
- ## Contents
15
+ Covers: background `URLSession` downloads and uploads. Not for designing
16
+ visible notification UI.
13
17
 
14
- - [Info.plist Configuration](#infoplist-configuration)
15
- - [BGTaskScheduler Registration](#bgtaskscheduler-registration)
16
- - [BGAppRefreshTask Patterns](#bgapprefreshtask-patterns)
17
- - [BGProcessingTask Patterns](#bgprocessingtask-patterns)
18
- - [BGContinuedProcessingTask (iOS 26+)](#bgcontinuedprocessingtask-ios-26)
19
- - [Background URLSession Downloads](#background-urlsession-downloads)
20
- - [Background Push Triggers](#background-push-triggers)
21
- - [Common Mistakes](#common-mistakes)
22
- - [Review Checklist](#review-checklist)
23
- - [References](#references)
18
+ ## Configuration
24
19
 
25
- ## Info.plist Configuration
20
+ **Permitted identifiers.** Every task identifier goes into the Info.plist
21
+ array `BGTaskSchedulerPermittedIdentifiers`. A missing entry makes
22
+ `submit(_:)` throw `BGTaskScheduler.Error.Code.notPermitted`. An entry may end
23
+ in a wildcard, such as `org.sample.photos.export.*`, for per-job identifiers.
26
24
 
27
- Every task identifier **must** be declared in `Info.plist` under
28
- `BGTaskSchedulerPermittedIdentifiers`, or `submit(_:)` throws
29
- `BGTaskScheduler.Error.Code.notPermitted`.
25
+ **Background modes.** `UIBackgroundModes` needs `fetch` for app refresh tasks
26
+ and `processing` for processing tasks. In Xcode: target, Signing &
27
+ Capabilities, Background Modes, then tick "Background fetch" and "Background
28
+ processing".
30
29
 
31
30
  ```xml
32
31
  <key>BGTaskSchedulerPermittedIdentifiers</key>
33
32
  <array>
34
- <string>com.example.app.refresh</string>
35
- <string>com.example.app.db-cleanup</string>
36
- <string>com.example.app.export.*</string>
33
+ <string>org.sample.photos.refresh</string>
34
+ <string>org.sample.photos.cleanup</string>
35
+ <string>org.sample.photos.export.*</string>
37
36
  </array>
38
- ```
39
-
40
- Also enable the required `UIBackgroundModes`:
41
-
42
- ```xml
43
37
  <key>UIBackgroundModes</key>
44
38
  <array>
45
- <string>fetch</string> <!-- Required for BGAppRefreshTask -->
46
- <string>processing</string> <!-- Required for BGProcessingTask -->
39
+ <string>fetch</string>
40
+ <string>processing</string>
47
41
  </array>
48
42
  ```
49
43
 
50
- In Xcode: target > Signing & Capabilities > Background Modes > enable "Background fetch" and "Background processing".
51
-
52
- ## BGTaskScheduler Registration
44
+ ## Registering handlers
53
45
 
54
- Register handlers **before** app launch completes. In UIKit, register in
55
- `application(_:didFinishLaunchingWithOptions:)`; in SwiftUI, register in `App.init()`.
56
-
57
- ### UIKit Registration
46
+ Refresh and processing handlers must be registered before the app finishes
47
+ launching: in `application(_:didFinishLaunchingWithOptions:)` for UIKit, or in
48
+ the `App` initializer for SwiftUI. Registering later, for instance from a
49
+ view's `.task`, is too late for the system to deliver a launch. Continued
50
+ processing tasks are the one exception, covered below. Register each
51
+ identifier once: a second registration of the same identifier kills the app.
58
52
 
59
53
  ```swift
60
54
  import BackgroundTasks
55
+ import SwiftUI
61
56
 
62
- @main
63
- class AppDelegate: UIResponder, UIApplicationDelegate {
64
- func application(
65
- _ application: UIApplication,
66
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
67
- ) -> Bool {
68
- BGTaskScheduler.shared.register(
69
- forTaskWithIdentifier: "com.example.app.refresh",
70
- using: nil // nil = default background queue
71
- ) { task in
72
- self.handleAppRefresh(task: task as! BGAppRefreshTask)
73
- }
74
-
75
- BGTaskScheduler.shared.register(
76
- forTaskWithIdentifier: "com.example.app.db-cleanup",
77
- using: nil
78
- ) { task in
79
- self.handleDatabaseCleanup(task: task as! BGProcessingTask)
80
- }
81
-
82
- return true
83
- }
57
+ enum TaskID {
58
+ static let refresh = "org.sample.photos.refresh"
59
+ static let cleanup = "org.sample.photos.cleanup"
84
60
  }
85
- ```
86
-
87
- ### SwiftUI Registration
88
-
89
- ```swift
90
- import SwiftUI
91
- import BackgroundTasks
92
61
 
93
62
  @main
94
- struct MyApp: App {
63
+ struct PhotosApp: App {
95
64
  init() {
96
- BGTaskScheduler.shared.register(
97
- forTaskWithIdentifier: "com.example.app.refresh",
98
- using: nil
99
- ) { task in
100
- BackgroundTaskManager.shared.handleAppRefresh(
101
- task: task as! BGAppRefreshTask
102
- )
65
+ BGTaskScheduler.shared.register(forTaskWithIdentifier: TaskID.refresh, using: nil) { task in
66
+ guard let refresh = task as? BGAppRefreshTask else { return }
67
+ LibrarySync.shared.handleRefresh(refresh)
68
+ }
69
+ BGTaskScheduler.shared.register(forTaskWithIdentifier: TaskID.cleanup, using: nil) { task in
70
+ guard let cleanup = task as? BGProcessingTask else { return }
71
+ LibrarySync.shared.handleCleanup(cleanup)
103
72
  }
104
73
  }
105
74
 
106
75
  var body: some Scene {
107
- WindowGroup { ContentView() }
76
+ WindowGroup {
77
+ LibraryView()
78
+ }
108
79
  }
109
80
  }
110
81
  ```
111
82
 
112
- ## BGAppRefreshTask Patterns
83
+ Passing `nil` for `using:` runs the handler on a default background queue.
113
84
 
114
- Short-lived tasks (~30 seconds) for fetching small data updates. The system
115
- decides when to launch based on usage patterns. Review notes should say
116
- `earliestBeginDate` is a lower-bound hint and the system may run the task later.
85
+ ## App refresh tasks
86
+
87
+ A refresh task gets roughly 30 seconds for a small update. The system picks
88
+ the moment based on how the user uses the app. `earliestBeginDate` only says
89
+ "not before"; the task may run much later or not at all, and reviews should
90
+ treat it that way.
117
91
 
118
92
  ```swift
119
- func scheduleAppRefresh() {
120
- let request = BGAppRefreshTaskRequest(
121
- identifier: "com.example.app.refresh"
122
- )
123
- request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
124
- // earliestBeginDate is a lower-bound hint; the system may delay launch.
125
- do {
126
- try BGTaskScheduler.shared.submit(request)
127
- } catch {
128
- print("Could not schedule app refresh: \(error)")
93
+ final class LibrarySync: Sendable {
94
+ static let shared = LibrarySync()
95
+
96
+ func scheduleRefresh() {
97
+ let request = BGAppRefreshTaskRequest(identifier: TaskID.refresh)
98
+ request.earliestBeginDate = .now.addingTimeInterval(15 * 60)
99
+ if (try? BGTaskScheduler.shared.submit(request)) == nil {
100
+ Log.background.error("Refresh request was rejected")
101
+ }
129
102
  }
130
- }
131
103
 
132
- func handleAppRefresh(task: BGAppRefreshTask) {
133
- // Schedule the next refresh before doing work
134
- scheduleAppRefresh()
135
-
136
- let fetchTask = Task {
137
- do {
138
- let data = try await APIClient.shared.fetchLatestFeed()
139
- await FeedStore.shared.update(with: data)
140
- task.setTaskCompleted(success: true)
141
- } catch {
142
- task.setTaskCompleted(success: false)
143
- }
104
+ func handleRefresh(_ task: BGAppRefreshTask) {
105
+ scheduleRefresh()
106
+ perform(task) { try await self.fetchNewAlbums() }
144
107
  }
108
+ }
145
109
 
146
- // CRITICAL: Handle expiration -- system can revoke time at any moment
110
+ func perform(_ task: BGTask, _ job: @escaping @Sendable () async throws -> Void) {
111
+ nonisolated(unsafe) let task = task
112
+ let work = Task {
113
+ let finished = (try? await job()) != nil
114
+ task.setTaskCompleted(success: finished && !Task.isCancelled)
115
+ }
147
116
  task.expirationHandler = {
148
- fetchTask.cancel()
117
+ work.cancel()
149
118
  task.setTaskCompleted(success: false)
150
119
  }
151
120
  }
152
121
  ```
153
122
 
154
- ## BGProcessingTask Patterns
123
+ `Log.background` stands for an `os.Logger` owned by the app, and the
124
+ processing example below reuses `perform`. Two habits matter: schedule the
125
+ next run first, so a crash or expiry does not break the chain, and always
126
+ install an `expirationHandler`, because the system can take the time back at
127
+ any moment.
155
128
 
156
- Long-running tasks (minutes) for maintenance, data processing, or cleanup.
157
- Runs only when device is idle and (optionally) charging. Review notes should say
158
- `earliestBeginDate` is a lower-bound hint and the system may run the task later.
129
+ `BGTask` is not `Sendable`, yet both the work `Task` and the expiration
130
+ handler need it, and the handler runs on a queue the framework picks. The
131
+ `nonisolated(unsafe)` local copy marks that sharing as deliberate; without it
132
+ Swift 6 rejects the `Task` closure with "passing closure as a 'sending'
133
+ parameter risks causing data races".
134
+
135
+ ## Processing tasks
136
+
137
+ Processing tasks can run for minutes and suit maintenance, indexing and
138
+ cleanup. They start only while the device is idle, and optionally only on
139
+ power.
159
140
 
160
141
  ```swift
161
- func scheduleProcessingTask() {
162
- let request = BGProcessingTaskRequest(
163
- identifier: "com.example.app.db-cleanup"
164
- )
142
+ func scheduleCleanup() {
143
+ let request = BGProcessingTaskRequest(identifier: TaskID.cleanup)
165
144
  request.requiresNetworkConnectivity = false
166
145
  request.requiresExternalPower = true
167
- request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60)
168
- // earliestBeginDate is a lower-bound hint; the system may delay launch.
169
- do {
170
- try BGTaskScheduler.shared.submit(request)
171
- } catch {
172
- print("Could not schedule processing task: \(error)")
173
- }
146
+ request.earliestBeginDate = .now.addingTimeInterval(3600)
147
+ try? BGTaskScheduler.shared.submit(request)
174
148
  }
175
149
 
176
- func handleDatabaseCleanup(task: BGProcessingTask) {
177
- scheduleProcessingTask()
178
-
179
- let cleanupTask = Task {
180
- do {
181
- try await DatabaseManager.shared.purgeExpiredRecords()
182
- try await DatabaseManager.shared.rebuildIndexes()
183
- task.setTaskCompleted(success: true)
184
- } catch {
185
- task.setTaskCompleted(success: false)
186
- }
187
- }
188
-
189
- task.expirationHandler = {
190
- cleanupTask.cancel()
191
- task.setTaskCompleted(success: false)
192
- }
150
+ func handleCleanup(_ task: BGProcessingTask) {
151
+ scheduleCleanup()
152
+ perform(task) { try await purgeOrphanedThumbnails() }
193
153
  }
194
154
  ```
195
155
 
196
- ## BGContinuedProcessingTask (iOS 26+)
197
-
198
- A task initiated in the foreground by a user action that continues running in the
199
- background. The system displays progress via a Live Activity. Conforms to
200
- `ProgressReporting`.
156
+ ## Continued processing tasks (iOS 26+)
201
157
 
202
- **Availability:** iOS 26.0+, iPadOS 26.0+
158
+ `BGContinuedProcessingTask` needs iOS or iPadOS 26.0 and is for work the user
159
+ starts on purpose, such as an export, that should keep going after they leave
160
+ the app. It differs from the other two types:
203
161
 
204
- Unlike `BGAppRefreshTask` and `BGProcessingTask`, this task starts immediately
205
- from the foreground. The system can terminate it under resource pressure,
206
- prioritizing tasks that report minimal progress first. Set `expirationHandler` for user or system cancellation, cancel in-flight work, and clean up partial output before reporting completion.
162
+ - It starts right away from the foreground instead of waiting for the system.
163
+ - The system shows its progress to the user as a Live Activity; the task
164
+ adopts `ProgressReporting`.
165
+ - Under resource pressure the system may end it, and tasks reporting little
166
+ progress go first.
167
+ - The user or the system can cancel it; the `expirationHandler` must stop the
168
+ work and remove partial output before completing.
169
+ - Its handler is exempt from the register-at-launch rule. `submit` expects
170
+ an identifier that already has a handler, so register the job's identifier
171
+ right before submitting it.
207
172
 
208
173
  ```swift
209
- import BackgroundTasks
210
-
211
- func startExport() {
212
- // Register the task handler at app launch, not here.
213
- // BGTaskScheduler requires registration before app launch completes.
214
- let jobID = UUID().uuidString
174
+ func startExport(of album: Album) {
175
+ let identifier = "org.sample.photos.export.\(UUID().uuidString)"
176
+ BGTaskScheduler.shared.register(forTaskWithIdentifier: identifier, using: nil) { task in
177
+ guard let export = task as? BGContinuedProcessingTask else { return }
178
+ handleExport(export)
179
+ }
215
180
  let request = BGContinuedProcessingTaskRequest(
216
- identifier: "com.example.app.export.\(jobID)",
217
- title: "Exporting Photos",
218
- subtitle: "Processing 247 items"
181
+ identifier: identifier,
182
+ title: "Exporting \(album.name)",
183
+ subtitle: "Preparing"
219
184
  )
220
- // Use a permitted base wildcard identifier: com.example.app.export.*
221
- // earliestBeginDate is ignored for continued processing requests.
222
- // .queue: begin as soon as possible if can't run immediately
223
- // .fail: fail submission if can't run immediately
224
- request.strategy = .queue
225
-
226
- do {
227
- try BGTaskScheduler.shared.submit(request)
228
- } catch {
229
- print("Could not submit continued processing task: \(error)")
185
+ request.strategy = .fail
186
+ if BGTaskScheduler.supportedResources.contains(.gpu) {
187
+ request.requiredResources = .gpu
188
+ }
189
+ guard (try? BGTaskScheduler.shared.submit(request)) != nil else {
190
+ showExportUnavailable()
191
+ return
230
192
  }
231
193
  }
232
194
 
233
- func performExport(task: BGContinuedProcessingTask) async {
234
- let items = await PhotoLibrary.shared.itemsToExport()
235
- let progress = task.progress
236
- progress.totalUnitCount = Int64(items.count)
237
-
238
- for (index, item) in items.enumerated() {
239
- if Task.isCancelled { break }
240
-
241
- await PhotoExporter.shared.export(item)
242
- progress.completedUnitCount = Int64(index + 1)
243
-
244
- // Update the user-facing title/subtitle
245
- task.updateTitle(
246
- "Exporting Photos",
247
- subtitle: "\(index + 1) of \(items.count) complete"
248
- )
195
+ func handleExport(_ task: BGContinuedProcessingTask) {
196
+ nonisolated(unsafe) let task = task
197
+ let work = Task { await runExport(task, items: ExportQueue.shared.pending) }
198
+ task.expirationHandler = {
199
+ work.cancel()
200
+ ExportQueue.shared.removePartialFiles()
249
201
  }
202
+ }
250
203
 
204
+ func runExport(_ task: BGContinuedProcessingTask, items: [PhotoAsset]) async {
205
+ task.progress.totalUnitCount = Int64(items.count)
206
+ var done: Int64 = 0
207
+ for asset in items where !Task.isCancelled {
208
+ await render(asset)
209
+ done += 1
210
+ task.progress.completedUnitCount = done
211
+ task.updateTitle(task.title, subtitle: "Photo \(done) of \(items.count)")
212
+ }
251
213
  task.setTaskCompleted(success: !Task.isCancelled)
252
214
  }
253
215
  ```
254
216
 
255
- For GPU work, check support and enable Background GPU Access (`com.apple.developer.background-tasks.continued-processing.gpu`):
217
+ When the task expires, the cancelled `Task` leaves the loop and
218
+ `runExport` reports `success: false`, so completion still happens exactly once. The
219
+ `nonisolated(unsafe)` copy in `handleExport` is there for the same reason as in
220
+ `perform`.
256
221
 
257
- ```swift
258
- let supported = BGTaskScheduler.supportedResources
259
- if supported.contains(.gpu) {
260
- request.requiredResources = .gpu
261
- }
262
- ```
222
+ - Each job gets its own identifier under a permitted wildcard base, which
223
+ also keeps every registration unique.
224
+ - `earliestBeginDate` is ignored for this request type.
225
+ - `strategy` is `.queue` (start as soon as allowed) or `.fail` (reject the
226
+ submission if it cannot start now).
227
+ - GPU work needs both the `supportedResources` check and the entitlement for
228
+ background GPU use (key ending in `continued-processing.gpu`).
229
+
230
+ Strategy choice, cancellation and progress ranking are expanded in the
231
+ [patterns reference](references/background-task-patterns.md).
263
232
 
264
- ## Background URLSession Downloads
233
+ ## Background URLSession
265
234
 
266
- Use `URLSessionConfiguration.background` for downloads that continue even after
267
- the app is suspended or terminated. The system handles the transfer out of
268
- process.
235
+ A session built with `URLSessionConfiguration.background(withIdentifier:)`
236
+ hands transfers to a system daemon, so they keep running while the app is
237
+ suspended or even terminated.
269
238
 
270
239
  ```swift
271
- class DownloadManager: NSObject, URLSessionDownloadDelegate {
272
- static let shared = DownloadManager()
240
+ @MainActor
241
+ final class EpisodeDownloader: NSObject, URLSessionDownloadDelegate {
242
+ static let shared = EpisodeDownloader()
273
243
 
274
- private lazy var session: URLSession = {
244
+ var finishEvents: (() -> Void)?
245
+
246
+ lazy var session: URLSession = {
275
247
  let config = URLSessionConfiguration.background(
276
- withIdentifier: "com.example.app.background-download"
248
+ withIdentifier: "org.sample.podcasts.downloads"
277
249
  )
278
- config.isDiscretionary = true
279
250
  config.sessionSendsLaunchEvents = true
280
- return URLSession(configuration: config, delegate: self, delegateQueue: nil)
251
+ config.isDiscretionary = true
252
+ return URLSession(
253
+ configuration: config,
254
+ delegate: self,
255
+ delegateQueue: nil
256
+ )
281
257
  }()
282
258
 
283
- func startDownload(from url: URL) {
284
- let task = session.downloadTask(with: url)
285
- task.earliestBeginDate = Date(timeIntervalSinceNow: 60)
286
- task.resume()
287
- }
288
-
289
- func urlSession(
290
- _ session: URLSession,
291
- downloadTask: URLSessionDownloadTask,
292
- didFinishDownloadingTo location: URL
293
- ) {
294
- // Move file from tmp before this method returns
295
- let dest = FileManager.default.urls(
296
- for: .documentDirectory, in: .userDomainMask
297
- )[0].appendingPathComponent("download.dat")
298
- try? FileManager.default.moveItem(at: location, to: dest)
259
+ func fetch(_ remote: URL, notBefore date: Date? = nil) {
260
+ let job = session.downloadTask(with: remote)
261
+ job.earliestBeginDate = date
262
+ job.resume()
299
263
  }
300
264
 
301
- func urlSession(
302
- _ session: URLSession,
303
- task: URLSessionTask,
304
- didCompleteWithError error: (any Error)?
305
- ) {
306
- if let error { print("Download failed: \(error)") }
265
+ nonisolated func urlSession(_ s: URLSession, downloadTask job: URLSessionDownloadTask, didFinishDownloadingTo tmp: URL) {
266
+ let name = job.response?.suggestedFilename ?? "episode-\(job.taskIdentifier)"
267
+ let target = URL.documentsDirectory.appending(path: name)
268
+ try? FileManager.default.moveItem(at: tmp, to: target)
307
269
  }
308
- }
309
- ```
310
-
311
- Handle app relaunch - store and invoke the system completion handler:
312
-
313
- ```swift
314
- // In AppDelegate:
315
- func application(
316
- _ application: UIApplication,
317
- handleEventsForBackgroundURLSession identifier: String,
318
- completionHandler: @escaping () -> Void
319
- ) {
320
- backgroundSessionCompletionHandler = completionHandler
321
- }
322
270
 
323
- // In URLSessionDelegate - call stored handler when events finish:
324
- func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
325
- Task { @MainActor in
326
- self.backgroundSessionCompletionHandler?()
327
- self.backgroundSessionCompletionHandler = nil
271
+ nonisolated func urlSession(_ s: URLSession, task job: URLSessionTask, didCompleteWithError error: (any Error)?) {
272
+ guard let error else { return }
273
+ Log.background.error("Transfer failed: \(error)")
328
274
  }
329
- }
330
- ```
331
-
332
- ## Background Push Triggers
333
-
334
- Silent push notifications wake your app briefly to fetch new content. Set
335
- `content-available: 1` in the push payload.
336
-
337
- ```json
338
- { "aps": { "content-available": 1 }, "custom-data": "new-messages" }
339
- ```
340
275
 
341
- Send the APNs request with `apns-push-type: background` and
342
- `apns-priority: 5`. Background push delivery is low priority and not
343
- guaranteed; keep sends infrequent, generally no more than two or three per
344
- hour.
345
-
346
- Handle in AppDelegate:
347
-
348
- ```swift
349
- func application(
350
- _ application: UIApplication,
351
- didReceiveRemoteNotification userInfo: [AnyHashable: Any],
352
- fetchCompletionHandler completionHandler:
353
- @escaping (UIBackgroundFetchResult) -> Void
354
- ) {
355
- Task {
356
- do {
357
- let hasNew = try await MessageStore.shared.fetchNewMessages()
358
- completionHandler(hasNew ? .newData : .noData)
359
- } catch {
360
- completionHandler(.failed)
276
+ nonisolated func urlSessionDidFinishEvents(forBackgroundURLSession s: URLSession) {
277
+ Task { @MainActor in
278
+ self.finishEvents?()
279
+ self.finishEvents = nil
361
280
  }
362
281
  }
363
282
  }
364
283
  ```
365
284
 
366
- Enable "Remote notifications" in Background Modes and register:
367
-
368
- ```swift
369
- UIApplication.shared.registerForRemoteNotifications()
370
- ```
371
-
372
- ## Common Mistakes
285
+ - The class is `@MainActor`, so the lazy session and the stored handler are
286
+ safe to touch from UIKit callbacks. The session calls its delegate on its
287
+ own queue, hence the `nonisolated` delegate methods.
288
+ - `isDiscretionary` lets the system wait for good conditions. With
289
+ `sessionSendsLaunchEvents` on, finished transfers launch the app again.
290
+ - A delegate is required (`delegateQueue: nil` is fine). The async and
291
+ completion-handler task APIs do not work with background sessions.
292
+ - The temporary file disappears when `didFinishDownloadingTo` returns, so move
293
+ it inside that method.
294
+ - Failures arrive in `urlSession(_:task:didCompleteWithError:)`.
373
295
 
374
- ### 1. Missing Info.plist identifiers
296
+ When the system relaunches the app for session events, keep the handler it
297
+ gives you and call it once the delegate reports the events are done:
375
298
 
376
299
  ```swift
377
- // DON'T: Submit a task whose identifier isn't in BGTaskSchedulerPermittedIdentifiers
378
- let request = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh")
379
- try BGTaskScheduler.shared.submit(request) // Throws .notPermitted
380
-
381
- // DO: Add every identifier to Info.plist BGTaskSchedulerPermittedIdentifiers
382
- // <string>com.example.app.refresh</string>
383
- ```
384
-
385
- ### 2. Not calling setTaskCompleted(success:)
386
-
387
- ```swift
388
- // DON'T: Return without marking completion -- system penalizes future scheduling
389
- func handleRefresh(task: BGAppRefreshTask) {
390
- Task {
391
- let data = try await fetchData()
392
- await store.update(data)
393
- // Missing: task.setTaskCompleted(success:)
394
- }
395
- }
396
-
397
- // DO: Always call setTaskCompleted on every code path
398
- func handleRefresh(task: BGAppRefreshTask) {
399
- let work = Task {
400
- do {
401
- let data = try await fetchData()
402
- await store.update(data)
403
- task.setTaskCompleted(success: true)
404
- } catch {
405
- task.setTaskCompleted(success: false)
406
- }
407
- }
408
- task.expirationHandler = {
409
- work.cancel()
410
- task.setTaskCompleted(success: false)
411
- }
300
+ func application(_ app: UIApplication,
301
+ handleEventsForBackgroundURLSession id: String,
302
+ completionHandler done: @escaping () -> Void) {
303
+ EpisodeDownloader.shared.finishEvents = done
304
+ _ = EpisodeDownloader.shared.session
412
305
  }
413
306
  ```
414
307
 
415
- ### 3. Ignoring the expiration handler
308
+ ## Background pushes
416
309
 
417
- ```swift
418
- // DON'T: Assume your task will run to completion
419
- func handleCleanup(task: BGProcessingTask) {
420
- Task { await heavyWork() }
421
- // No expirationHandler -- system terminates ungracefully
422
- }
310
+ A silent push wakes the app for a short time. The payload carries
311
+ `content-available: 1` plus your own keys. On the APNs request, set the
312
+ push type header to `background` and the priority header to `5`.
423
313
 
424
- // DO: Set expirationHandler to cancel work and mark completed
425
- func handleCleanup(task: BGProcessingTask) {
426
- let work = Task { await heavyWork() }
427
- task.expirationHandler = {
428
- work.cancel()
429
- task.setTaskCompleted(success: false)
430
- }
314
+ ```json
315
+ {
316
+ "aps": { "content-available": 1 },
317
+ "album-id": "A41"
431
318
  }
432
319
  ```
433
320
 
434
- ### 4. Scheduling too frequently
435
-
436
- ```swift
437
- // DON'T: Request refresh every minute -- system throttles aggressively
438
- request.earliestBeginDate = Date(timeIntervalSinceNow: 60)
439
-
440
- // DO: Use reasonable intervals (15+ minutes for refresh)
441
- request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
442
- // earliestBeginDate is a hint -- the system chooses actual launch time
443
- ```
444
-
445
- ### 5. Over-relying on background time
321
+ Delivery is low priority and never guaranteed. Keep it to two or three per hour
322
+ at most. Enable Remote notifications under Background Modes and call
323
+ `UIApplication.shared.registerForRemoteNotifications()`.
446
324
 
447
325
  ```swift
448
- // DON'T: Start a 10-minute operation assuming it will finish
449
- func handleRefresh(task: BGAppRefreshTask) {
450
- Task { await tenMinuteSync() }
451
- }
452
-
453
- // DO: Design work to be incremental and cancellable
454
- func handleRefresh(task: BGAppRefreshTask) {
455
- let work = Task {
456
- for batch in batches {
457
- try Task.checkCancellation()
458
- await processBatch(batch)
459
- await saveBatchProgress(batch)
460
- }
461
- task.setTaskCompleted(success: true)
326
+ func application(_ app: UIApplication,
327
+ didReceiveRemoteNotification payload: [AnyHashable: Any],
328
+ fetchCompletionHandler reply: @escaping (UIBackgroundFetchResult) -> Void) {
329
+ guard let albumID = payload["album-id"] as? String else {
330
+ return reply(.noData)
462
331
  }
463
- task.expirationHandler = {
464
- work.cancel()
465
- task.setTaskCompleted(success: false)
332
+ Task {
333
+ let outcome: Result<Bool, any Error> = await LibrarySync.shared.refreshAlbum(albumID)
334
+ switch outcome {
335
+ case .success(true): reply(.newData)
336
+ case .success(false): reply(.noData)
337
+ case .failure: reply(.failed)
338
+ }
466
339
  }
467
340
  }
468
341
  ```
469
342
 
470
- ## Review Checklist
471
-
472
- - [ ] All task identifiers listed in `BGTaskSchedulerPermittedIdentifiers`
473
- - [ ] Required `UIBackgroundModes` enabled (`fetch`, `processing`)
474
- - [ ] Tasks registered before app launch completes
475
- - [ ] `setTaskCompleted(success:)` called on every code path
476
- - [ ] `expirationHandler` set and cancels in-flight work
477
- - [ ] Next task scheduled inside the handler (re-schedule pattern)
478
- - [ ] `earliestBeginDate` uses reasonable intervals and is treated as a hint
479
- - [ ] Background URLSession uses delegate (not async/closures)
480
- - [ ] Background URLSession file moved in `didFinishDownloadingTo` before return
481
- - [ ] `handleEventsForBackgroundURLSession` stores and calls completion handler
482
- - [ ] Background push payload includes `content-available: 1`
483
- - [ ] Background push APNs request uses `apns-push-type: background` and `apns-priority: 5`
484
- - [ ] `fetchCompletionHandler` called promptly with correct result
485
- - [ ] BGContinuedProcessingTask reports progress via `ProgressReporting`
486
- - [ ] Work is incremental and cancellation-safe (`Task.checkCancellation()`)
487
- - [ ] No blocking synchronous work in task handlers
343
+ ## Common mistakes
344
+
345
+ - **Identifier missing from Info.plist.** `submit` throws `.notPermitted`.
346
+ - **Skipping `setTaskCompleted(success:)`.** The system schedules the app less
347
+ often afterwards. Complete on every path, errors included.
348
+ - **No expiration handler.** The task is killed abruptly instead of stopping
349
+ cleanly. Cancel the work and complete from the handler.
350
+ - **Asking too often.** A refresh every minute gets throttled hard. Use 15
351
+ minutes or more, and remember `earliestBeginDate` is a hint.
352
+ - **Too much work per run.** A ten-minute job inside a refresh task will not
353
+ finish. Split work into small cancellable batches, call
354
+ `try Task.checkCancellation()` between them and save progress.
355
+
356
+ ## Review checklist
357
+
358
+ - [ ] Every identifier is in `BGTaskSchedulerPermittedIdentifiers`
359
+ - [ ] `UIBackgroundModes` has `fetch` and/or `processing` as needed
360
+ - [ ] Refresh and processing handlers are registered before launch completes;
361
+ continued processing handlers are registered before their `submit`
362
+ - [ ] `setTaskCompleted(success:)` runs on every path
363
+ - [ ] `expirationHandler` cancels in-flight work
364
+ - [ ] The handler schedules the next task
365
+ - [ ] `earliestBeginDate` is sensible and read as a lower bound only
366
+ - [ ] Background `URLSession` uses a delegate, not async or closure APIs
367
+ - [ ] Downloaded files are moved inside `didFinishDownloadingTo`
368
+ - [ ] `handleEventsForBackgroundURLSession` stores its handler and it is called later
369
+ - [ ] Silent push payload has `content-available: 1`
370
+ - [ ] APNs headers: push type `background`, priority `5`
371
+ - [ ] `fetchCompletionHandler` is called promptly with the right result
372
+ - [ ] Continued processing tasks report progress through `ProgressReporting`
373
+ - [ ] Work is incremental and checks cancellation (`Task.checkCancellation()`)
374
+ - [ ] Handlers never block on synchronous work
488
375
 
489
376
  ## References
490
377
 
491
- - See [references/background-task-patterns.md](references/background-task-patterns.md) for extended patterns, background
492
- URLSession edge cases, debugging with simulated launches, and background push
493
- best practices.
494
- - [BGTaskScheduler](https://sosumi.ai/documentation/backgroundtasks/bgtaskscheduler)
495
- - [BGAppRefreshTask](https://sosumi.ai/documentation/backgroundtasks/bgapprefreshtask)
496
- - [BGProcessingTask](https://sosumi.ai/documentation/backgroundtasks/bgprocessingtask)
497
- - [BGContinuedProcessingTask](https://sosumi.ai/documentation/backgroundtasks/bgcontinuedprocessingtask) (iOS 26+)
498
- - [BGContinuedProcessingTaskRequest](https://sosumi.ai/documentation/backgroundtasks/bgcontinuedprocessingtaskrequest) (iOS 26+)
499
- - [Using background tasks to update your app](https://sosumi.ai/documentation/uikit/using-background-tasks-to-update-your-app)
500
- - [Performing long-running tasks on iOS and iPadOS](https://sosumi.ai/documentation/backgroundtasks/performing-long-running-tasks-on-ios-and-ipados)
378
+ - [Background task patterns](references/background-task-patterns.md):
379
+ debugging, error codes, checkpointing, URLSession extras, push limits,
380
+ SwiftUI `.backgroundTask`, continued-processing details
381
+ - Apple documentation:
382
+ [scheduler](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler),
383
+ [refresh task](https://developer.apple.com/documentation/backgroundtasks/bgapprefreshtask),
384
+ [processing task](https://developer.apple.com/documentation/backgroundtasks/bgprocessingtask),
385
+ [continued processing task](https://developer.apple.com/documentation/backgroundtasks/bgcontinuedprocessingtask) (iOS 26+),
386
+ [continued processing request](https://developer.apple.com/documentation/backgroundtasks/bgcontinuedprocessingtaskrequest) (iOS 26+),
387
+ [keeping content fresh with background tasks](https://developer.apple.com/documentation/uikit/using-background-tasks-to-update-your-app),
388
+ [long-running work](https://developer.apple.com/documentation/backgroundtasks/performing-long-running-tasks-on-ios-and-ipados)