@mmerterden/multi-agent-pipeline 20.7.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 (264) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +266 -267
  5. package/package.json +2 -2
  6. package/pipeline/scripts/_notices.mjs +1 -1
  7. package/pipeline/skills/.skill-manifest.json +68 -68
  8. package/pipeline/skills/shared/README.md +70 -70
  9. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  10. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  11. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  12. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  13. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  14. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  15. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  16. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  17. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  18. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  19. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  20. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  21. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  22. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  23. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  24. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  25. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  26. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  27. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  28. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  29. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  30. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  31. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  32. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  34. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  35. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  36. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  37. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  38. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  39. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  40. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  41. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  42. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  43. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  44. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  45. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  46. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  47. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  48. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  49. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  50. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  51. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  52. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  53. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  54. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  55. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  56. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  57. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  58. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  59. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  60. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  61. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  62. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  63. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  64. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  65. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  66. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  67. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  68. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  69. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  70. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  71. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  72. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  73. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  74. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  75. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  76. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  77. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  78. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  79. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  80. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  81. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  82. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  83. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  84. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  85. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  86. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  87. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  88. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  89. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  90. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  91. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  92. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  93. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  94. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  95. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  96. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  97. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  98. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  99. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  100. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  101. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  102. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  103. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  104. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  105. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  106. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  107. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  108. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  109. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  111. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  112. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  113. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  114. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  115. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  116. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  117. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  118. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  119. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  120. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  121. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  122. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  123. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  124. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  125. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  126. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  127. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  128. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  129. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  130. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  131. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  132. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  133. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  134. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  135. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  136. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  137. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  138. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  139. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  140. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  141. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  142. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  143. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  144. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  145. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  146. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  147. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  148. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  149. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  150. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  151. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  152. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  153. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  154. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  155. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  156. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  157. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  158. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  159. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  160. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  161. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  162. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  163. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  164. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  165. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  166. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  167. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  168. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  169. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  170. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  171. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  172. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  173. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  174. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  175. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  176. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  177. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  178. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  179. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  180. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  181. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  182. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  183. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  184. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  185. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  186. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  187. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  188. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  189. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  190. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  191. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  192. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  193. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  194. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  195. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  196. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  197. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  198. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  199. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  200. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  201. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  202. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  203. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  204. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  205. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  206. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  207. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  208. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  209. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  210. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  211. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  212. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  213. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  214. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  215. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  216. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  217. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  218. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  219. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  220. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  221. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  222. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  223. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  224. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  225. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  226. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  227. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  228. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  229. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  230. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  231. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  232. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  233. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  234. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  235. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  236. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  237. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  238. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  239. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  240. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  241. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  242. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  243. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  244. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  245. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  246. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  247. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  248. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  249. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  250. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  251. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  252. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  253. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  254. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  255. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  256. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  257. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  258. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  259. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  260. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  261. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  262. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  263. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  264. 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)