@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,490 +1,414 @@
1
1
  ---
2
2
  name: ios-networking
3
- description: "Build, review, or improve networking code in iOS/macOS apps using URLSession with async/await, structured concurrency, and modern Swift patterns. Use when working with REST APIs, downloading files, uploading data, WebSocket connections, pagination, retry logic, request middleware, caching, background transfers, or network reachability monitoring. Also use when handling HTTP requests, API clients, network error handling, or data fetching in Swift apps."
3
+ description: "URLSession with async/await and structured concurrency on iOS and macOS: HTTP requests, REST API clients, response validation, error handling, retry with backoff, middleware and token refresh, pagination, caching, downloads, uploads, background transfers, WebSockets, NWPathMonitor reachability, Network.framework, ATS. Use when building or reviewing networking code, API clients or data fetching. Not for pinning (swift-security) or Keychain internals."
4
4
  metadata:
5
- source: "dpearson2699/swift-ios-skills (PolyForm Perimeter 1.0.0)"
5
+ source: multi-agent-pipeline
6
6
  ---
7
7
 
8
8
  # iOS Networking
9
9
 
10
- Modern networking patterns for iOS 26+ using URLSession with async/await and
11
- structured concurrency. All examples target Swift 6.3. No third-party
12
- dependencies required -- URLSession covers the vast majority of networking
13
- needs.
14
-
15
- ## Contents
16
-
17
- - [Core URLSession async/await](#core-urlsession-asyncawait)
18
- - [API Client Architecture](#api-client-architecture)
19
- - [Error Handling](#error-handling)
20
- - [Pagination](#pagination)
21
- - [Network Reachability](#network-reachability)
22
- - [Configuring URLSession](#configuring-urlsession)
23
- - [App Transport Security (ATS)](#app-transport-security-ats)
24
- - [Common Mistakes](#common-mistakes)
25
- - [Review Checklist](#review-checklist)
26
- - [References](#references)
10
+ URLSession handles nearly every networking job an app has, and it needs no
11
+ third-party package to do it well. Baseline for this skill: iOS 26, Swift 6.3.
27
12
 
28
13
  ## Core URLSession async/await
29
14
 
30
- URLSession gained native async/await overloads in iOS 15. Prefer these for
31
- foreground data, upload, download, and streaming work. Background URLSession
32
- transfers are the main exception: they still use task/delegate APIs so the
33
- system can deliver events after suspension or relaunch.
15
+ The async overloads (iOS 15 and later) are the default for foreground work:
16
+ data, upload, download and streaming. The one exception is a background
17
+ session. The system delivers its events after the app was suspended or even
18
+ relaunched, so it keeps the task-plus-delegate API.
34
19
 
35
- ### Data Requests
20
+ ### Data requests
36
21
 
37
22
  ```swift
38
- // Basic GET
39
- let (data, response) = try await URLSession.shared.data(from: url)
40
-
41
- // With a configured URLRequest
42
- var request = URLRequest(url: url)
43
- request.httpMethod = "POST"
44
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
45
- request.httpBody = try JSONEncoder().encode(payload)
46
- request.timeoutInterval = 30
47
- request.cachePolicy = .reloadIgnoringLocalCacheData
48
-
49
- let (data, response) = try await URLSession.shared.data(for: request)
23
+ let (forecast, _) = try await URLSession.shared.data(from: forecastURL)
24
+
25
+ var order = URLRequest(url: ordersURL)
26
+ order.httpMethod = "POST"
27
+ order.addValue("application/json", forHTTPHeaderField: "Content-Type")
28
+ order.httpBody = try JSONEncoder().encode(newOrder)
29
+ order.timeoutInterval = 30
30
+ order.cachePolicy = .reloadIgnoringLocalCacheData
31
+ let (confirmation, reply) = try await session.data(for: order)
50
32
  ```
51
33
 
52
- ### Response Validation
34
+ ### Response validation
53
35
 
54
- Always validate the HTTP status code before decoding. URLSession does not
55
- throw for 4xx/5xx responses -- it only throws for transport-level failures.
36
+ A 404 or a 503 is not a thrown error. URLSession throws only when the
37
+ transport fails (no route, TLS failure, cancellation), so check the status
38
+ yourself before touching the body:
56
39
 
57
40
  ```swift
58
- guard let httpResponse = response as? HTTPURLResponse else {
41
+ guard let status = (response as? HTTPURLResponse)?.statusCode else {
59
42
  throw NetworkError.invalidResponse
60
43
  }
61
-
62
- guard (200..<300).contains(httpResponse.statusCode) else {
63
- throw NetworkError.httpError(
64
- statusCode: httpResponse.statusCode,
65
- data: data
66
- )
44
+ if !(200...299).contains(status) {
45
+ throw NetworkError.httpStatus(code: status, body: data, message: nil)
67
46
  }
68
47
  ```
69
48
 
70
- ### JSON Decoding with Codable
49
+ ### Decoding with Codable
71
50
 
72
51
  ```swift
73
- func fetch<T: Decodable>(_ type: T.Type, from url: URL) async throws -> T {
74
- let (data, response) = try await URLSession.shared.data(from: url)
75
-
76
- guard let httpResponse = response as? HTTPURLResponse,
77
- (200..<300).contains(httpResponse.statusCode) else {
78
- throw NetworkError.invalidResponse
79
- }
80
-
52
+ func load<Model: Decodable>(_ kind: Model.Type, at address: URL) async throws -> Model {
53
+ let (bytes, reply) = try await session.data(from: address)
54
+ try validate(reply, body: bytes)
81
55
  let decoder = JSONDecoder()
82
56
  decoder.dateDecodingStrategy = .iso8601
83
57
  decoder.keyDecodingStrategy = .convertFromSnakeCase
84
- return try decoder.decode(T.self, from: data)
58
+ return try decoder.decode(kind, from: bytes)
85
59
  }
86
60
  ```
87
61
 
88
- ### Downloads and Uploads
62
+ ### Downloads and uploads
89
63
 
90
- Use `download(for:)` for large files -- it streams to disk instead of
91
- loading the entire payload into memory.
64
+ `download(for:)` streams the body to a file instead of memory, so use it for
65
+ anything large. The returned file is temporary: move or copy it right away.
92
66
 
93
67
  ```swift
94
- // Download to a temporary file
95
- let (localURL, response) = try await URLSession.shared.download(for: request)
68
+ let (tempFile, _) = try await session.download(for: manualRequest)
69
+ let target = URL.documentsDirectory.appending(path: "manual.pdf")
70
+ try FileManager.default.moveItem(at: tempFile, to: target)
96
71
 
97
- // Move or copy the returned temporary file promptly.
98
- let destination = documentsDirectory.appendingPathComponent("file.zip")
99
- try FileManager.default.moveItem(at: localURL, to: destination)
72
+ let (ack, _) = try await session.upload(for: syncRequest, from: jsonData)
73
+ let (receipt, _) = try await session.upload(for: videoRequest, fromFile: videoURL)
100
74
  ```
101
75
 
102
- For delegate-based `URLSessionDownloadDelegate`, move or open the temporary
103
- file before `urlSession(_:downloadTask:didFinishDownloadingTo:)` returns.
104
-
105
- Background sessions are delegate-driven transfer queues. Use task creation
106
- APIs such as `downloadTask(with:)` and file-backed `uploadTask(with:fromFile:)`,
107
- then handle `URLSessionDelegate` / task delegate callbacks. Do not use async
108
- convenience APIs such as `data(for:)`, `download(for:)`, or `upload(for:)` as
109
- the durable background-session pattern.
110
-
111
- ```swift
112
- // Upload data
113
- let (data, response) = try await URLSession.shared.upload(for: request, from: bodyData)
76
+ A `URLSessionDownloadDelegate` gets the same kind of temporary file and has
77
+ to relocate or open it before `urlSession(_:downloadTask:didFinishDownloadingTo:)` returns.
114
78
 
115
- // Upload from file
116
- let (data, response) = try await URLSession.shared.upload(for: request, fromFile: fileURL)
117
- ```
79
+ A background session is a delegate-driven transfer queue. Its work is
80
+ `downloadTask(with:)` plus `uploadTask(with:fromFile:)` for uploads from disk,
81
+ reported through session and task delegate callbacks. The async conveniences
82
+ `data(for:)`, `download(for:)` and `upload(for:)` are not a durable
83
+ background pattern. See [background and WebSocket guide](references/background-websocket.md).
118
84
 
119
85
  ### Streaming with AsyncBytes
120
86
 
121
- Use `bytes(for:)` for streaming responses, progress tracking, or
122
- line-delimited data (e.g., server-sent events).
87
+ `bytes(for:)` hands you the body as it arrives: useful for progress, for
88
+ newline-delimited JSON and for server-sent events.
123
89
 
124
90
  ```swift
125
- let (bytes, response) = try await URLSession.shared.bytes(for: request)
126
-
127
- for try await line in bytes.lines {
128
- // Process each line as it arrives (e.g., SSE stream)
129
- handleEvent(line)
91
+ let (stream, _) = try await session.bytes(for: request)
92
+ for try await line in stream.lines {
93
+ handle(line)
130
94
  }
131
95
  ```
132
96
 
133
- ## API Client Architecture
97
+ ## API client architecture
134
98
 
135
- ### Protocol-Based Client
99
+ ### Protocol-based client
136
100
 
137
- Define a protocol for testability. This lets you swap implementations in
138
- tests without mocking URLSession directly.
101
+ Put the client behind a protocol. Tests then substitute a double instead of
102
+ mocking URLSession.
139
103
 
140
104
  ```swift
141
105
  protocol APIClientProtocol: Sendable {
142
- func fetch<T: Decodable & Sendable>(
143
- _ type: T.Type,
144
- endpoint: Endpoint
145
- ) async throws -> T
146
-
147
- func send<T: Decodable & Sendable>(
148
- _ type: T.Type,
149
- endpoint: Endpoint,
150
- body: some Encodable & Sendable
151
- ) async throws -> T
106
+ func fetch<Reply: Decodable & Sendable>(_ endpoint: Endpoint, as kind: Reply.Type) async throws -> Reply
107
+ func send<Reply: Decodable & Sendable>(_ endpoint: Endpoint, json payload: some Encodable & Sendable,
108
+ as kind: Reply.Type) async throws -> Reply
109
+ func perform(_ endpoint: Endpoint) async throws
110
+ func upload<Reply: Decodable & Sendable>(_ endpoint: Endpoint, bytes: Data,
111
+ as kind: Reply.Type) async throws -> Reply
152
112
  }
153
- ```
154
113
 
155
- ```swift
156
114
  struct Endpoint: Sendable {
157
- let path: String
158
- var method: String = "GET"
115
+ enum HTTPMethod: String, Sendable { case get, post, put, patch, delete }
116
+
117
+ var path: String
118
+ var method: HTTPMethod = .get
159
119
  var queryItems: [URLQueryItem] = []
160
120
  var headers: [String: String] = [:]
161
-
162
- func url(relativeTo baseURL: URL) -> URL {
163
- guard let components = URLComponents(
164
- url: baseURL.appendingPathComponent(path),
165
- resolvingAgainstBaseURL: true
166
- ) else {
167
- preconditionFailure("Invalid URL components for path: \(path)")
168
- }
169
- var mutableComponents = components
170
- if !queryItems.isEmpty {
171
- mutableComponents.queryItems = queryItems
121
+ var body: Data? = nil
122
+ var cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy
123
+ var timeoutInterval: TimeInterval = 30
124
+
125
+ func urlRequest(relativeTo baseURL: URL) throws -> URLRequest {
126
+ guard var parts = URLComponents(url: baseURL.appending(path: path),
127
+ resolvingAgainstBaseURL: true) else {
128
+ throw NetworkError.invalidURL
172
129
  }
173
- guard let url = mutableComponents.url else {
174
- preconditionFailure("Failed to construct URL from components")
175
- }
176
- return url
130
+ if !queryItems.isEmpty { parts.queryItems = queryItems }
131
+ guard let url = parts.url else { throw NetworkError.invalidURL }
132
+ var request = URLRequest(url: url, cachePolicy: cachePolicy, timeoutInterval: timeoutInterval)
133
+ request.httpMethod = method.rawValue.uppercased()
134
+ request.httpBody = body
135
+ request.allHTTPHeaderFields = headers
136
+ return request
177
137
  }
178
138
  }
179
139
  ```
180
140
 
181
- The client accepts a `baseURL`, optional custom `URLSession`, `JSONDecoder`,
182
- and an array of `RequestMiddleware` interceptors. Each method builds a
183
- `URLRequest` from the endpoint, applies middleware, executes the request,
184
- validates the status code, and decodes the result. See
185
- [references/urlsession-patterns.md](references/urlsession-patterns.md) for the complete `APIClient` implementation
186
- with convenience methods, request builder, and test setup.
141
+ A bad URL is a thrown `NetworkError.invalidURL`, never a crash; nothing in
142
+ this skill force-unwraps.
187
143
 
188
- Production clients should receive an injected, configured `URLSession` instead
189
- of calling `URLSession.shared` internally. Configure `URLSessionConfiguration`
190
- with request/resource timeouts, cache policy or `URLCache`,
191
- `waitsForConnectivity`, data-cost policy, and delegates when authentication
192
- challenges, redirects, metrics, pinning, or background transfer handling matter.
144
+ The concrete `APIClient` is built from a base URL, an injected `URLSession`
145
+ and an ordered list of `RequestMiddleware`; it owns a snake_case, ISO 8601
146
+ `JSONDecoder` and `JSONEncoder`. Every method runs
147
+ the same pipeline: build the request, apply middleware, execute, validate,
148
+ decode. The full implementation, a fluent request builder and tests are in
149
+ [URLSession patterns](references/urlsession-patterns.md).
193
150
 
194
- ### Lightweight Closure-Based Client
151
+ Inject a configured session into production clients rather than reaching for
152
+ `URLSession.shared` inside them. The configuration is where request and
153
+ resource timeouts, cache policy or a `URLCache`, `waitsForConnectivity`,
154
+ cellular and Low Data Mode policy live, plus a delegate when you need auth
155
+ challenges, redirect control, metrics, pinning or background transfers.
195
156
 
196
- For apps using the MV pattern, use closure-based clients for testability
197
- and SwiftUI preview support. See [references/lightweight-clients.md](references/lightweight-clients.md) for
198
- the full pattern (struct of async closures, injected via init).
157
+ ### Lightweight closure-based client
199
158
 
200
- ### Request Middleware / Interceptors
159
+ MV-style SwiftUI apps often do better with a struct of async closures passed
160
+ in through `init`: trivial to stub in previews and tests. See
161
+ [lightweight clients](references/lightweight-clients.md).
201
162
 
202
- Middleware transforms requests before they are sent. Use this for
203
- authentication, logging, analytics headers, and similar cross-cutting
204
- concerns.
163
+ ### Request middleware
164
+
165
+ Middleware rewrites a request before it goes out. Authentication, logging,
166
+ tracing and analytics headers all fit here.
205
167
 
206
168
  ```swift
207
169
  protocol RequestMiddleware: Sendable {
208
- func prepare(_ request: URLRequest) async throws -> URLRequest
170
+ func adapt(_ request: inout URLRequest) async throws
209
171
  }
210
- ```
211
172
 
212
- ```swift
213
- struct AuthMiddleware: RequestMiddleware {
214
- let tokenProvider: @Sendable () async throws -> String
173
+ struct BearerTokenMiddleware: RequestMiddleware {
174
+ let currentToken: @Sendable () async throws -> String
215
175
 
216
- func prepare(_ request: URLRequest) async throws -> URLRequest {
217
- var request = request
218
- let token = try await tokenProvider()
219
- request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
220
- return request
176
+ func adapt(_ request: inout URLRequest) async throws {
177
+ let token = try await currentToken()
178
+ request.addValue("Bearer " + token, forHTTPHeaderField: "Authorization")
221
179
  }
222
180
  }
223
181
  ```
224
182
 
225
- ### Token Refresh Flow
183
+ ### Token refresh
226
184
 
227
- Handle 401 responses by refreshing the token and retrying once.
185
+ On a 401, refresh once and repeat the call once. A second 401 is a real
186
+ failure, not a loop.
228
187
 
229
188
  ```swift
230
- func fetchWithTokenRefresh<T: Decodable & Sendable>(
231
- _ type: T.Type,
232
- endpoint: Endpoint,
233
- tokenStore: TokenStore
234
- ) async throws -> T {
189
+ func fetchRefreshingOnce<Reply: Decodable & Sendable>(_ endpoint: Endpoint, as kind: Reply.Type) async throws -> Reply {
235
190
  do {
236
- return try await fetch(type, endpoint: endpoint)
237
- } catch NetworkError.httpError(statusCode: 401, _) {
238
- try await tokenStore.refreshToken()
239
- return try await fetch(type, endpoint: endpoint)
191
+ return try await client.fetch(endpoint, as: kind)
192
+ } catch NetworkError.httpStatus(code: 401, _, _) {
193
+ try await credentials.refresh()
194
+ return try await client.fetch(endpoint, as: kind)
240
195
  }
241
196
  }
242
197
  ```
243
198
 
244
- ## Error Handling
199
+ ## Error handling
245
200
 
246
- ### Structured Error Types
201
+ ### Structured error type
202
+
203
+ One error enum is shared by this file and every reference:
247
204
 
248
205
  ```swift
249
- enum NetworkError: Error, Sendable {
206
+ enum NetworkError: Error, LocalizedError {
207
+ case invalidURL
250
208
  case invalidResponse
251
- case httpError(statusCode: Int, data: Data)
252
- case decodingFailed(Error)
209
+ case httpStatus(code: Int, body: Data, message: String?)
210
+ case decodingFailed(any Error)
253
211
  case noConnection
254
212
  case timedOut
255
213
  case cancelled
256
-
257
- /// Map a URLError to a typed NetworkError
258
- static func from(_ urlError: URLError) -> NetworkError {
259
- switch urlError.code {
260
- case .notConnectedToInternet, .networkConnectionLost:
261
- return .noConnection
262
- case .timedOut:
263
- return .timedOut
264
- case .cancelled:
265
- return .cancelled
266
- default:
267
- return .httpError(statusCode: -1, data: Data())
214
+ case transport(URLError)
215
+
216
+ static func from(_ error: URLError) -> NetworkError {
217
+ switch error.code {
218
+ case .notConnectedToInternet, .networkConnectionLost: .noConnection
219
+ case .timedOut: .timedOut
220
+ case .cancelled: .cancelled
221
+ default: .transport(error)
268
222
  }
269
223
  }
270
224
  }
271
225
  ```
272
226
 
273
- ### Key URLError Cases
227
+ `Error` already refines `Sendable`, so the enum is Sendable without saying
228
+ so. Unknown transport failures keep their `URLError` in `.transport` instead
229
+ of posing as a fake HTTP status. `errorDescription` returns `nil` for
230
+ `.cancelled` so cancellation never surfaces as a message; the full
231
+ implementation is in [URLSession patterns](references/urlsession-patterns.md#error-types).
274
232
 
275
- | URLError Code | Meaning | Action |
233
+ ### URLError codes worth handling
234
+
235
+ | Code | What happened | Response |
276
236
  |---|---|---|
277
- | `.notConnectedToInternet` | Device offline | Show offline UI, queue for retry |
278
- | `.networkConnectionLost` | Connection dropped mid-request | Retry with backoff |
279
- | `.timedOut` | Server did not respond in time | Retry once, then show error |
280
- | `.cancelled` | Task was cancelled | No action needed; do not show error |
281
- | `.cannotFindHost` | DNS failure | Check URL, show error |
282
- | `.secureConnectionFailed` | TLS handshake failed | Check cert pinning, ATS config |
283
- | `.userAuthenticationRequired` | Authentication required to access a resource | Trigger auth flow |
237
+ | `.notConnectedToInternet` | Device is offline | Offline UI, queue for later |
238
+ | `.networkConnectionLost` | The connection broke partway through | Back off, then try again |
239
+ | `.timedOut` | Server too slow | Retry once, then report |
240
+ | `.cancelled` | Task was cancelled | Do nothing, show nothing |
241
+ | `.cannotFindHost` | DNS lookup failed | Check the host, report |
242
+ | `.secureConnectionFailed` | TLS handshake failed | Check ATS and pinning setup |
243
+ | `.userAuthenticationRequired` | Resource needs credentials | Start sign-in |
244
+
245
+ ### Server error bodies
284
246
 
285
- ### Decoding Server Error Bodies
247
+ Many APIs return a JSON body with the failure. The client decodes it into
248
+ `message` when it can; a caller can also decode it on demand:
286
249
 
287
250
  ```swift
288
- struct APIErrorResponse: Decodable, Sendable {
289
- let code: String
290
- let message: String
291
- }
251
+ struct APIErrorBody: Decodable, Sendable {
252
+ let code: String?
253
+ let message: String?
292
254
 
293
- func decodeAPIError(from data: Data) -> APIErrorResponse? {
294
- try? JSONDecoder().decode(APIErrorResponse.self, from: data)
255
+ static func decode(from data: Data) -> APIErrorBody? {
256
+ try? JSONDecoder().decode(APIErrorBody.self, from: data)
257
+ }
295
258
  }
296
259
 
297
- // Usage in catch block
298
- catch NetworkError.httpError(let statusCode, let data) {
299
- if let apiError = decodeAPIError(from: data) {
300
- showError("Server error: \(apiError.message)")
301
- } else {
302
- showError("HTTP \(statusCode)")
303
- }
260
+ do { try await client.perform(archiveEndpoint) }
261
+ catch NetworkError.httpStatus(let status, let body, let message) {
262
+ banner = message ?? APIErrorBody.decode(from: body)?.message ?? "Request failed (\(status))"
304
263
  }
305
264
  ```
306
265
 
307
- ### Retry with Exponential Backoff
266
+ ### Retry with exponential backoff
308
267
 
309
- Use structured concurrency for retries. Respect task cancellation between
310
- attempts. Skip retries for cancellation and 4xx client errors (except 429).
268
+ Retry inside structured concurrency so cancellation stops the loop. Never
269
+ retry cancellation or a 4xx, with 429 as the exception; 5xx and transient
270
+ transport errors are fair game.
311
271
 
312
272
  ```swift
313
273
  func withRetry<T: Sendable>(
314
274
  maxAttempts: Int = 3,
315
275
  initialDelay: Duration = .seconds(1),
276
+ maxDelay: Duration = .seconds(30),
277
+ shouldRetry: @Sendable (any Error) -> Bool = isTransient,
316
278
  operation: @Sendable () async throws -> T
317
279
  ) async throws -> T {
318
- var lastError: Error?
280
+ precondition(maxAttempts > 0, "maxAttempts must be at least 1")
319
281
  for attempt in 0..<maxAttempts {
282
+ try Task.checkCancellation()
320
283
  do {
321
284
  return try await operation()
322
285
  } catch {
323
- lastError = error
324
- if error is CancellationError { throw error }
325
- if case NetworkError.httpError(let code, _) = error,
326
- (400..<500).contains(code), code != 429 { throw error }
327
- if attempt < maxAttempts - 1 {
328
- try await Task.sleep(for: initialDelay * Int(pow(2.0, Double(attempt))))
329
- }
286
+ guard attempt < maxAttempts - 1, shouldRetry(error) else { throw error }
287
+ let capped = min(initialDelay * (1 << attempt), maxDelay)
288
+ try await Task.sleep(for: capped + capped * Double.random(in: 0...0.1))
330
289
  }
331
290
  }
332
- throw lastError!
291
+ preconditionFailure("the last attempt always returns or throws")
333
292
  }
334
293
  ```
335
294
 
295
+ There is no sleep after the last attempt: its error is rethrown at once. The
296
+ `isTransient` predicate is in
297
+ [URLSession patterns](references/urlsession-patterns.md#retries-that-back-off-exponentially).
298
+
336
299
  ## Pagination
337
300
 
338
- Build cursor-based or offset-based pagination with `AsyncSequence`.
339
- Always check `Task.isCancelled` between pages. See
340
- [references/urlsession-patterns.md](references/urlsession-patterns.md) for complete `CursorPaginator` and
341
- offset-based implementations.
301
+ Model paging as an `AsyncSequence` of pages, cursor-based or offset-based,
302
+ and call `try Task.checkCancellation()` (or test `Task.isCancelled`) before
303
+ every page request. Both paginators are written out in
304
+ [URLSession patterns](references/urlsession-patterns.md#paging-by-cursor).
342
305
 
343
- ## Network Reachability
306
+ ## Network reachability
344
307
 
345
- Use `NWPathMonitor` from the Network framework -- not third-party
346
- Reachability libraries. On current OS targets it conforms to `AsyncSequence`;
347
- wrap `pathUpdateHandler` only for compatibility or custom projections.
308
+ Use `NWPathMonitor`, not a third-party Reachability port. On current SDKs the
309
+ monitor is itself an `AsyncSequence`; wrapping `pathUpdateHandler` in your own
310
+ stream is only for older targets or for a custom projection.
348
311
 
349
312
  ```swift
350
- import Network
351
-
352
- func observeNetworkStatus() async {
353
- let monitor = NWPathMonitor()
354
-
355
- for await path in monitor {
356
- handle(path.status)
357
- }
313
+ for await path in NWPathMonitor() {
314
+ isOnline = path.status == .satisfied
315
+ useLowBandwidth = path.isConstrained || path.isExpensive
358
316
  }
359
317
  ```
360
318
 
361
- Check `path.isExpensive` (cellular) and `path.isConstrained` (Low Data
362
- Mode) to adapt behavior (reduce image quality, skip prefetching).
319
+ `isExpensive` means cellular or a hotspot; `isConstrained` means Low Data
320
+ Mode. Use them to lower image quality or skip prefetching.
363
321
 
364
- Use Network.framework for low-level TCP, UDP, listeners, Bonjour, path
365
- monitoring, or WebSocket protocol work -- not ordinary REST APIs. For iOS 26
366
- `NetworkConnection<QUIC>`, `openStream(...)` and `inboundStreams(...)` are
367
- async throwing APIs; see [references/network-framework.md#quic-multiplexed-streams](references/network-framework.md#quic-multiplexed-streams).
322
+ Network.framework is for TCP and UDP, listeners, Bonjour, path monitoring and
323
+ WebSocket protocol work. It is not the tool for an ordinary REST API. On
324
+ iOS 26 `NetworkConnection<QUIC>` adds multiplexed streams, and both
325
+ `openStream(...)` and `inboundStreams(...)` are async and throwing. See
326
+ [Network framework guide](references/network-framework.md) and its
327
+ [QUIC stream notes](references/network-framework.md#quic-multiplexed-streams).
368
328
 
369
329
  ## Configuring URLSession
370
330
 
371
- Create a configured session for production code. `URLSession.shared` is
372
- acceptable only for simple, one-off requests.
331
+ `URLSession.shared` suits a quick one-off call. Anything that ships deserves a
332
+ configured session:
373
333
 
374
334
  ```swift
375
- let configuration = URLSessionConfiguration.default
376
- configuration.timeoutIntervalForRequest = 30
377
- configuration.timeoutIntervalForResource = 300
378
- configuration.waitsForConnectivity = true
379
- configuration.requestCachePolicy = .returnCacheDataElseLoad
380
- configuration.httpAdditionalHeaders = [
335
+ let config = URLSessionConfiguration.default
336
+ config.timeoutIntervalForRequest = 30
337
+ config.timeoutIntervalForResource = 300
338
+ config.waitsForConnectivity = true
339
+ config.requestCachePolicy = .returnCacheDataElseLoad
340
+ config.httpAdditionalHeaders = [
381
341
  "Accept": "application/json",
382
- "Accept-Language": Locale.preferredLanguages.first ?? "en"
342
+ "Accept-Language": Locale.preferredLanguages.prefix(3).joined(separator: ", "),
383
343
  ]
384
-
385
- let session = URLSession(configuration: configuration)
344
+ let session = URLSession(configuration: config)
386
345
  ```
387
346
 
388
- `waitsForConnectivity = true` is valuable -- it makes the session wait for
389
- a network path instead of failing immediately when offline. Combine with
390
- `urlSession(_:taskIsWaitingForConnectivity:)` delegate callback for UI
391
- feedback.
347
+ With `waitsForConnectivity`, a request made while offline waits for a usable
348
+ path instead of failing immediately. Implement
349
+ `urlSession(_:taskIsWaitingForConnectivity:)` on the delegate to tell the user.
392
350
 
393
351
  ## App Transport Security (ATS)
394
352
 
395
- ATS enforces HTTPS for all connections by default. Do not disable it.
396
- ATS is URL Loading System policy, so it covers `URLSession` rather than making
397
- lower-level `Network.framework` connections secure automatically. When using
398
- Network.framework, configure secure TLS parameters and trust handling correctly
399
- for that protocol stack.
400
-
401
- Use domain-specific ATS exceptions only as a last resort.
402
-
403
- **Rules:**
404
- - Never set `NSAllowsArbitraryLoads` to `true` in production unless there is no narrower option.
405
- - ATS exceptions require justification and may trigger additional App Store review.
406
- - Use exception domains only for third-party servers you cannot upgrade to HTTPS.
407
- - `NSAllowsLocalNetworking` is acceptable for local device communication (Bonjour, IoT).
408
- - Prefer ATS `NSPinnedDomains` for declarative pinning when possible. Raw bytes
409
- from `SecKeyCopyExternalRepresentation` are not sufficient for SPKI pinning;
410
- correct SPKI pinning hashes Subject Public Key Info and belongs in `swift-security`.
411
-
412
- ## Common Mistakes
413
-
414
- **DON'T:** Use `URLSession.shared` with custom configuration needs.
415
- **DO:** Create a configured `URLSession` with appropriate timeouts, caching,
416
- and delegate for production code.
417
-
418
- **DON'T:** Force-unwrap `URL(string:)` with dynamic input.
419
- **DO:** Use `URL(string:)` with proper error handling. Force-unwrap is
420
- acceptable only for compile-time-constant strings.
421
-
422
- **DON'T:** Decode JSON on the main thread for large payloads.
423
- **DO:** Keep decoding on the calling context of the URLSession call, which
424
- is off-main by default. Only hop to `@MainActor` to update UI state.
425
-
426
- **DON'T:** Ignore cancellation in long-running network tasks.
427
- **DO:** Check `Task.isCancelled` or call `try Task.checkCancellation()` in
428
- loops (pagination, streaming, retry). Use `.task` in SwiftUI for automatic
429
- cancellation.
430
-
431
- **DON'T:** Use Alamofire or Moya when URLSession async/await handles the
432
- need.
433
- **DO:** Use URLSession directly. With async/await, the ergonomic gap that
434
- justified third-party libraries no longer exists. Reserve third-party
435
- libraries for genuinely missing features (e.g., image caching).
436
-
437
- **DON'T:** Mock URLSession directly in tests.
438
- **DO:** Use `URLProtocol` subclass for transport-level mocking, or use
439
- protocol-based clients that accept a test double.
440
-
441
- **DON'T:** Use `data(for:)` for large file downloads.
442
- **DO:** Use `download(for:)` which streams to disk and avoids memory spikes.
443
-
444
- **DON'T:** Fire network requests from `body` or view initializers.
445
- **DO:** Use `.task` or `.task(id:)` to trigger network calls.
446
-
447
- **DON'T:** Hardcode authentication tokens in requests.
448
- **DO:** Inject tokens via middleware so they are centralized and refreshable.
449
-
450
- **DON'T:** Ignore HTTP status codes and decode blindly.
451
- **DO:** Validate status codes before decoding. A 200 with invalid JSON and
452
- a 500 with an error body require different handling.
453
-
454
- ## Review Checklist
455
-
456
- - [ ] Foreground transfers use async/await; background sessions use delegate/task APIs
457
- - [ ] Error handling covers URLError cases (.notConnectedToInternet, .timedOut, .cancelled)
458
- - [ ] Requests are cancellable (respect Task cancellation via `.task` modifier or stored Task references)
459
- - [ ] Authentication tokens injected via middleware, not hardcoded
460
- - [ ] Response HTTP status codes validated before decoding
461
- - [ ] Large downloads use `download(for:)` not `data(for:)`
462
- - [ ] Network calls happen off `@MainActor` (only UI updates on main)
463
- - [ ] URLSession configured with appropriate timeouts and caching
464
- - [ ] Production clients inject configured sessions instead of using `URLSession.shared`
465
- - [ ] Background transfers use task/delegate APIs, not async convenience APIs
466
- - [ ] Retry logic excludes cancellation and 4xx client errors
467
- - [ ] Pagination checks `Task.isCancelled` between pages
468
- - [ ] Sensitive tokens stored in Keychain (not UserDefaults or plain files)
469
- - [ ] No force-unwrapped URLs from dynamic input
470
- - [ ] Server error responses decoded and surfaced to users
471
- - [ ] Network.framework code configures TLS/trust explicitly and keeps deep pinning work in `swift-security`
472
- - [ ] `NetworkConnection<QUIC>` stream APIs are treated as async throwing
473
- - [ ] Ensure network response model types conform to Sendable; use @MainActor for UI-updating completion paths
353
+ - ATS requires HTTPS by default. Leave it on.
354
+ - ATS is policy of the URL Loading System. It governs URLSession, not
355
+ Network.framework connections; with Network.framework you configure TLS
356
+ and trust evaluation yourself.
357
+ - A per-domain exception is the last resort, only for a third-party host
358
+ that cannot move to HTTPS. Exceptions need a written justification and
359
+ can draw extra App Review scrutiny.
360
+ - `NSAllowsArbitraryLoads = true` does not belong in a production build
361
+ unless nothing narrower exists.
362
+ - `NSAllowsLocalNetworking` is fine for talking to local devices (Bonjour,
363
+ IoT accessories).
364
+ - Prefer declarative pinning with `NSPinnedDomains`. Hashing the raw bytes
365
+ from `SecKeyCopyExternalRepresentation` is not SPKI pinning; a correct pin
366
+ hashes the Subject Public Key Info. Trust code belongs to `swift-security`.
367
+
368
+ ## Common mistakes
369
+
370
+ 1. `URLSession.shared` where configuration matters. Build a session with
371
+ timeouts, caching and a delegate.
372
+ 2. Force-unwrapping `URL(string:)`. Handle `nil`, whether the string is
373
+ dynamic or a literal; a thrown `invalidURL` costs one line.
374
+ 3. Decoding big payloads on the main actor. The awaited call and the decode
375
+ stay off the main actor; hop to `@MainActor` only to publish UI state.
376
+ 4. Ignoring cancellation in paging, streaming and retry loops. Test
377
+ `Task.isCancelled` or use `try Task.checkCancellation()`, and start work
378
+ from SwiftUI `.task` so it is cancelled with the view.
379
+ 5. Adding Alamofire or Moya for what async URLSession already does. Keep
380
+ libraries for features the SDK lacks, such as image caching.
381
+ 6. Mocking URLSession in tests. Stub the transport with a `URLProtocol`
382
+ subclass or swap the client protocol for a double.
383
+ 7. `data(for:)` for large files. `download(for:)` avoids the memory spike.
384
+ 8. Starting requests in a view's `body` or `init`. Use `.task` or
385
+ `.task(id:)`.
386
+ 9. Pasting tokens into individual requests. Middleware centralises them and
387
+ makes refresh possible.
388
+ 10. Decoding before checking the status. A 200 with malformed JSON and a 500
389
+ with an error body need different handling.
390
+
391
+ ## Review checklist
392
+
393
+ - [ ] Foreground work uses async/await; background sessions use tasks and delegates, not async conveniences
394
+ - [ ] `.notConnectedToInternet`, `.timedOut` and `.cancelled` are handled
395
+ - [ ] Requests are cancellable through `.task` or a stored `Task`
396
+ - [ ] Auth headers come from middleware; tokens live in the Keychain, never UserDefaults or plain files
397
+ - [ ] Status code checked before decoding; server error bodies decoded and shown
398
+ - [ ] Large downloads use `download(for:)`
399
+ - [ ] Networking and decoding run off `@MainActor`; only UI updates hop to main
400
+ - [ ] Session has deliberate timeouts and caching and is injected, not `URLSession.shared`
401
+ - [ ] Retry skips cancellation and 4xx other than 429
402
+ - [ ] Paginators check cancellation between pages
403
+ - [ ] No force-unwrapped URLs or casts
404
+ - [ ] Network.framework code sets TLS and trust explicitly; pinning detail deferred to `swift-security`
405
+ - [ ] `NetworkConnection<QUIC>` stream calls are awaited and can throw
406
+ - [ ] Response models are `Sendable`; code that updates UI is `@MainActor`
474
407
 
475
408
  ## References
476
409
 
477
- - See [references/urlsession-patterns.md](references/urlsession-patterns.md) for complete API client
478
- implementation, multipart uploads, download progress, URLProtocol
479
- mocking, retry/backoff, certificate pinning, request logging, and
480
- pagination implementations.
481
- - See [references/background-websocket.md](references/background-websocket.md) for background URLSession
482
- configuration, background downloads/uploads, WebSocket patterns with
483
- structured concurrency, and reconnection strategies.
484
- - See [references/lightweight-clients.md](references/lightweight-clients.md) for the lightweight closure-based
485
- client pattern (struct of async closures, injected via init for testability
486
- and preview support).
487
- - See [references/network-framework.md](references/network-framework.md) for Network.framework (NWConnection,
488
- NWListener, NWBrowser, NWPathMonitor) and low-level TCP/UDP/WebSocket patterns.
489
- - See [references/file-storage-patterns.md](references/file-storage-patterns.md) for file system directory
490
- selection, FileProtectionType, backup exclusion, and storage pressure handling.
410
+ - [URLSession patterns](references/urlsession-patterns.md): full API client, request builder, multipart upload, download progress, cursor and offset pagination, URLProtocol mocking, retry with jitter, pinning guidance, request logging, caching, SSE, session factory.
411
+ - [background and WebSocket guide](references/background-websocket.md): background session setup, background downloads and uploads, app delegate event handling, WebSocket with structured concurrency, reconnection, typed messages, auth.
412
+ - [lightweight clients](references/lightweight-clients.md): struct-of-closures client injected through `init` for tests and previews.
413
+ - [Network framework guide](references/network-framework.md): `NWConnection`, `NWListener`, `NWBrowser`, `NWPathMonitor`, raw TCP, UDP and WebSocket, iOS 26 `NetworkConnection`.
414
+ - [file storage patterns](references/file-storage-patterns.md): directory choice, `FileProtectionType`, backup exclusion, low-storage handling.