@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,31 +1,37 @@
1
- # FormatStyle & Locale-Aware Formatting
1
+ # FormatStyle, Locale and Layout Direction
2
2
 
3
- Comprehensive reference for locale-aware formatting in iOS 15+ using `FormatStyle`. Never hard-code date, number, or measurement formats -- these break in every locale except the one you tested. `ios-localization` owns `FormatStyle` guidance when the issue is locale-aware user-facing display: numbers, dates, currency, units, names, lists, calendars, separators, and locale preview/testing. Locale-aware formatting matters even in single-language apps; explicitly recommend testing or previewing user-facing output under multiple locales. Use the `swift-formatstyle` skill for broader standalone FormatStyle API design.
3
+ `FormatStyle` needs iOS 15 or later. This skill uses it for locale-aware
4
+ display: numbers, dates, currency, units, names, lists, calendars and
5
+ separators, plus previews and tests across locales. Custom `FormatStyle`
6
+ types, `ParseableFormatStyle`, parsing, `Date.IntervalFormatStyle`,
7
+ `URL.FormatStyle` and reusable formatter API design belong to
8
+ `swift-formatstyle`; here they only get locale risks and testing advice unless
9
+ someone asks for the implementation. Whatever the value, recommend previewing
10
+ it in several locales such as `en_US`, `de_DE`, `ar_SA` and `ja_JP`.
4
11
 
5
12
  ## Contents
6
13
 
7
- - [Date Formatting](#date-formatting)
8
- - [Number Formatting](#number-formatting)
9
- - [Measurement Formatting](#measurement-formatting)
10
- - [Duration Formatting](#duration-formatting)
11
- - [PersonNameComponents Formatting](#personnamecomponents-formatting)
12
- - [ByteCountFormatStyle](#bytecountformatstyle)
13
- - [ListFormatStyle](#listformatstyle)
14
- - [Custom FormatStyle Implementation](#custom-formatstyle-implementation)
15
- - [Forcing a Specific Locale](#forcing-a-specific-locale)
16
- - [RTL Layout Deep Dive](#rtl-layout-deep-dive)
17
- - [`@ScaledMetric for Dynamic Type`](#scaledmetric-for-dynamic-type)
18
- - [Layout Testing with Accessibility Inspector](#layout-testing-with-accessibility-inspector)
19
- - [Quick Reference Table](#quick-reference-table)
20
-
21
- ## Migration from Legacy Formatters
22
-
23
- `FormatStyle` (iOS 15+) replaces the older `Formatter` subclasses. If you encounter legacy code, migrate to `FormatStyle`:
24
-
25
- | Legacy | Modern replacement |
26
- |--------|-------------------|
27
- | `DateFormatter` | `.formatted(.dateTime...)` or `Date.FormatStyle` |
28
- | `NumberFormatter` | `.formatted(.number...)` or `IntegerFormatStyle` / `FloatingPointFormatStyle` |
14
+ 1. [Replacing legacy formatters](#replacing-legacy-formatters)
15
+ 2. [Dates](#dates)
16
+ 3. [Numbers](#numbers)
17
+ 4. [Measurements](#measurements)
18
+ 5. [Durations](#durations)
19
+ 6. [Person names](#person-names)
20
+ 7. [Byte counts](#byte-counts)
21
+ 8. [Lists](#lists)
22
+ 9. [Custom styles](#custom-styles)
23
+ 10. [Pinning a locale](#pinning-a-locale)
24
+ 11. [Right-to-left in depth](#right-to-left-in-depth)
25
+ 12. [ScaledMetric and Dynamic Type](#scaledmetric-and-dynamic-type)
26
+ 13. [Checking layout with Accessibility Inspector](#checking-layout-with-accessibility-inspector)
27
+ 14. [Cheat sheet](#cheat-sheet)
28
+
29
+ ## Replacing legacy formatters
30
+
31
+ | Old formatter | FormatStyle replacement |
32
+ |---|---|
33
+ | `DateFormatter` | `.formatted(.dateTime...)`, `Date.FormatStyle` |
34
+ | `NumberFormatter` | `.formatted(.number...)`, `IntegerFormatStyle`, `FloatingPointFormatStyle` |
29
35
  | `DateComponentsFormatter` | `Duration.formatted(.units(...))` or `.time(pattern:)` |
30
36
  | `MeasurementFormatter` | `Measurement.formatted(.measurement(...))` |
31
37
  | `DateIntervalFormatter` | `(start..<end).formatted(date:time:)` |
@@ -33,612 +39,398 @@ Comprehensive reference for locale-aware formatting in iOS 15+ using `FormatStyl
33
39
  | `ByteCountFormatter` | `.formatted(.byteCount(style:))` |
34
40
  | `ListFormatter` | `.formatted(.list(type:))` |
35
41
 
36
- `FormatStyle` is value-type, `Sendable`, composable, and works directly in SwiftUI `Text` views. The legacy formatters are reference types that require manual locale and calendar configuration.
42
+ Format styles are value types, `Sendable` and composable, and SwiftUI `Text`
43
+ accepts them directly. The old formatters are classes whose locale and calendar
44
+ you had to configure and share by hand.
37
45
 
38
- ## Date Formatting
46
+ ## Dates
39
47
 
40
- ### Preset date and time styles
48
+ Presets:
41
49
 
42
50
  ```swift
43
- let date = Date.now
44
-
45
- // Date only
46
- date.formatted(date: .numeric, time: .omitted) // "1/15/2026" (US) / "15.01.2026" (DE)
47
- date.formatted(date: .abbreviated, time: .omitted) // "Jan 15, 2026" (US) / "15. Jan. 2026" (DE)
48
- date.formatted(date: .long, time: .omitted) // "January 15, 2026" (US) / "15. Januar 2026" (DE)
49
- date.formatted(date: .complete, time: .omitted) // "Thursday, January 15, 2026" (US)
50
-
51
- // Time only
52
- date.formatted(date: .omitted, time: .shortened) // "3:30 PM" (US) / "15:30" (DE)
53
- date.formatted(date: .omitted, time: .standard) // "3:30:45 PM" (US) / "15:30:45" (DE)
54
- date.formatted(date: .omitted, time: .complete) // includes time zone
55
-
56
- // Combined
57
- date.formatted(date: .long, time: .shortened) // "January 15, 2026 at 3:30 PM"
58
- date.formatted() // platform default
59
- ```
60
-
61
- ### Component-based date formatting
51
+ let pickup = Date.now
62
52
 
63
- Build custom date formats by composing components. The system reorders components for each locale.
53
+ pickup.formatted(date: .numeric, time: .omitted) // 6/3/2026
54
+ pickup.formatted(date: .abbreviated, time: .omitted) // Jun 3, 2026
55
+ pickup.formatted(date: .long, time: .omitted) // June 3, 2026
56
+ pickup.formatted(date: .complete, time: .omitted) // Wednesday, June 3, 2026
64
57
 
65
- ```swift
66
- // Month and day
67
- date.formatted(.dateTime.month().day()) // "Jan 15" (US) / "15 Jan" (UK)
68
-
69
- // Full date with weekday
70
- date.formatted(.dateTime.weekday(.wide).month(.wide).day().year())
71
- // "Thursday, January 15, 2026" (US) / "Donnerstag, 15. Januar 2026" (DE)
72
-
73
- // Month name styles
74
- date.formatted(.dateTime.month(.wide)) // "January"
75
- date.formatted(.dateTime.month(.abbreviated)) // "Jan"
76
- date.formatted(.dateTime.month(.narrow)) // "J"
77
- date.formatted(.dateTime.month(.twoDigits)) // "01"
78
-
79
- // Day styles
80
- date.formatted(.dateTime.day(.twoDigits)) // "15"
81
- date.formatted(.dateTime.day(.ordinalOfDayInMonth)) // "3" (third Thursday)
82
-
83
- // Year
84
- date.formatted(.dateTime.year(.defaultDigits)) // "2026"
85
- date.formatted(.dateTime.year(.twoDigits)) // "26"
86
-
87
- // Hour/minute
88
- date.formatted(.dateTime.hour().minute()) // "3:30 PM" (US, 12h) / "15:30" (DE, 24h)
89
- date.formatted(.dateTime.hour(.defaultDigits(amPM: .omitted)).minute()) // "3:30"
58
+ pickup.formatted(date: .omitted, time: .shortened) // 9:40 AM
59
+ pickup.formatted(date: .omitted, time: .standard) // 9:40:12 AM
60
+ pickup.formatted(date: .omitted, time: .complete) // adds the time zone
61
+ pickup.formatted() // platform default
90
62
  ```
91
63
 
92
- ### SwiftUI date display
64
+ Components: list what you want and the system arranges it for the locale.
93
65
 
94
66
  ```swift
95
- // Automatic format
96
- Text(event.date, format: .dateTime.month().day().year())
97
-
98
- // Date range
99
- Text(event.start...event.end) // "Jan 15 - Jan 20, 2026"
100
-
101
- // Relative (auto-updates)
102
- Text(event.date, style: .relative) // "2 hours ago", "in 3 days"
103
- Text(event.date, style: .timer) // counts up/down live
104
- Text(event.date, style: .offset) // "+2 hours" / "-3 days"
67
+ pickup.formatted(.dateTime.weekday(.abbreviated).day().month(.wide))
68
+ pickup.formatted(.dateTime.month(.narrow)) // "J"
69
+ pickup.formatted(.dateTime.month(.twoDigits).day(.twoDigits))
70
+ pickup.formatted(.dateTime.day(.ordinalOfDayInMonth)) // first Wednesday -> 1
71
+ pickup.formatted(.dateTime.year(.twoDigits))
72
+ pickup.formatted(.dateTime.year(.defaultDigits))
73
+ pickup.formatted(.dateTime.hour().minute()) // 12h or 24h per locale
74
+ pickup.formatted(.dateTime.hour(.defaultDigits(amPM: .omitted)).minute())
105
75
  ```
106
76
 
107
- ### Relative date formatting
108
-
109
- ```swift
110
- // Relative (named style)
111
- let relative = date.formatted(.relative(presentation: .named))
112
- // "yesterday", "today", "tomorrow", "last Friday", "in 2 weeks"
113
-
114
- // Relative (numeric style)
115
- let relativeNum = date.formatted(.relative(presentation: .numeric))
116
- // "1 day ago", "in 2 days", "3 weeks ago"
117
-
118
- // Relative with specific units
119
- let relativeCustom = date.formatted(.relative(presentation: .named, unitsStyle: .wide))
120
- ```
77
+ `month(_:)` takes `.wide`, `.abbreviated`, `.narrow`, `.twoDigits`. `day(_:)`
78
+ takes `.twoDigits` or `.ordinalOfDayInMonth`, which counts which occurrence of
79
+ that weekday the date is in the month.
121
80
 
122
- ### Date ranges and intervals
81
+ In SwiftUI:
123
82
 
124
83
  ```swift
125
- let start = Date.now
126
- let end = Calendar.current.date(byAdding: .day, value: 5, to: start)!
127
-
128
- // Range formatting
129
- (start..<end).formatted(date: .abbreviated, time: .omitted)
130
- // "Jan 15 - 20, 2026" (smart about shared month/year)
131
-
132
- // Duration of interval
133
- (start..<end).formatted(.components(style: .wide))
134
- // "5 days"
84
+ Text(pickup, format: .dateTime.day().month())
85
+ Text(pickup...pickup.addingTimeInterval(7_200)) // a range
86
+ Text(pickup, style: .relative) // updates itself
87
+ Text(pickup, style: .timer) // counts live
88
+ Text(pickup, style: .offset)
135
89
  ```
136
90
 
137
- ### ISO 8601 (for APIs, not for display)
91
+ Relative phrasing:
138
92
 
139
93
  ```swift
140
- // For serialization to APIs -- NOT for user-facing display
141
- date.formatted(.iso8601) // "2026-01-15T15:30:45Z"
142
- date.formatted(.iso8601.dateSeparator(.dash).timeSeparator(.colon))
94
+ let lastSync = Date.now.addingTimeInterval(-86_400)
95
+ lastSync.formatted(.relative(presentation: .named)) // "yesterday"
96
+ lastSync.formatted(.relative(presentation: .numeric)) // "1 day ago"
97
+ lastSync.formatted(.relative(presentation: .numeric, unitsStyle: .wide))
143
98
  ```
144
99
 
145
- ## Number Formatting
146
-
147
- ### Integer and decimal
100
+ Ranges:
148
101
 
149
102
  ```swift
150
- let value = 1234567
151
-
152
- value.formatted() // "1,234,567" (US) / "1.234.567" (DE) / "1 234 567" (FR)
153
- value.formatted(.number.grouping(.never)) // "1234567"
154
- value.formatted(.number.precision(.significantDigits(3))) // "1,230,000"
155
-
156
- let decimal = 3.14159
157
- decimal.formatted(.number.precision(.fractionLength(2))) // "3.14"
158
- decimal.formatted(.number.precision(.fractionLength(0...3))) // "3.142"
159
-
160
- // Notation
161
- value.formatted(.number.notation(.compactName)) // "1.2M" (US) / "1,2 Mio." (DE)
162
- value.formatted(.number.notation(.scientific)) // "1.234567E6"
103
+ let checkIn = Date.now
104
+ let checkOut = checkIn.addingTimeInterval(4 * 86_400)
105
+ (checkIn..<checkOut).formatted(date: .abbreviated, time: .omitted)
106
+ // shared month and year are written once: "Jun 3 - 7, 2026"
107
+ (checkIn..<checkOut).formatted(.components(style: .wide)) // "4 days"
163
108
  ```
164
109
 
165
- ### Rounding
110
+ ISO 8601 is for the wire, never for people:
166
111
 
167
112
  ```swift
168
- let num = 3.456
169
-
170
- // Round to 2 fraction digits
171
- num.formatted(.number.precision(.fractionLength(2)).rounded(rule: .up)) // "3.46"
172
- num.formatted(.number.precision(.fractionLength(2)).rounded(rule: .down)) // "3.45"
173
- num.formatted(.number.precision(.fractionLength(2)).rounded(rule: .toNearestOrEven)) // "3.46"
113
+ pickup.formatted(.iso8601.dateSeparator(.dash).timeSeparator(.colon))
174
114
  ```
175
115
 
176
- ### Percent
116
+ ## Numbers
177
117
 
178
- ```swift
179
- let ratio = 0.856
180
-
181
- ratio.formatted(.percent) // "86%" (US) / "86 %" (FR)
182
- ratio.formatted(.percent.precision(.fractionLength(1))) // "85.6%"
183
-
184
- // Integer percentage
185
- let score = 92
186
- score.formatted(.percent) // "9,200%" -- probably not what you want!
187
- // For integer percentages, divide first:
188
- (Double(score) / 100).formatted(.percent) // "92%"
189
- ```
190
-
191
- ### Currency
192
-
193
- Always specify the currency code explicitly. The locale controls formatting (symbol position, decimal separator), but the currency code determines the currency.
118
+ Grouping follows the locale: 2,500,000 in the US, 2.500.000 in Germany,
119
+ 2 500 000 in France.
194
120
 
195
121
  ```swift
196
- let price = Decimal(29.99)
122
+ 2_500_000.formatted() // locale grouping
123
+ 2_500_000.formatted(.number.grouping(.never)) // 2500000
197
124
 
198
- // Explicit currency code (recommended)
199
- price.formatted(.currency(code: "USD")) // "$29.99" (US) / "29,99 $US" (FR) / "US$29.99" (AU)
200
- price.formatted(.currency(code: "EUR")) // "EUR29.99" (US) / "29,99 EUR" (DE) / "29,99 EUR" (FR)
201
- price.formatted(.currency(code: "JPY")) // "JPY30" (no decimals for yen)
125
+ 0.004218.formatted(.number.precision(.significantDigits(3))) // 0.00422
126
+ 72.5.formatted(.number.precision(.fractionLength(2))) // 72.50
127
+ 72.5.formatted(.number.precision(.fractionLength(0...3))) // 72.5
202
128
 
203
- // Narrow symbol (when space is limited)
204
- price.formatted(.currency(code: "USD").presentation(.narrow)) // "$29.99" even in non-US locales
129
+ 4_300_000.formatted(.number.notation(.compactName)) // "4.3M", German "4,3 Mio."
130
+ 6_022.0.formatted(.number.notation(.scientific))
205
131
 
206
- // In SwiftUI
207
- Text(price, format: .currency(code: order.currencyCode))
132
+ 18.6.formatted(.number.rounded(rule: .up, increment: 1)) // 19
133
+ 18.6.formatted(.number.rounded(rule: .down, increment: 1)) // 18
134
+ 18.5.formatted(.number.rounded(rule: .toNearestOrEven, increment: 1)) // 18
208
135
  ```
209
136
 
210
- **Important:** Use `Decimal` (not `Double`) for monetary values to avoid floating-point precision errors.
137
+ Without `increment:` (or a `.precision` of zero fraction digits) the rule only
138
+ applies at the style's default precision, so `18.6` still prints "18.6".
211
139
 
212
- ### Ordinal numbers
140
+ Percent multiplies by 100. The integer `92` with `.percent` prints
141
+ "9,200%". Convert to a fraction first:
213
142
 
214
143
  ```swift
215
- let position = 3
216
- position.formatted(.number.notation(.ordinal)) // "3rd" (EN) / "3." (DE) / "3e" (FR)
144
+ let batteryLevel = 92
145
+ (Double(batteryLevel) / 100).formatted(.percent) // "92%"
146
+ 0.1275.formatted(.percent.precision(.fractionLength(1))) // "12.8%"
217
147
  ```
218
148
 
219
- ## Measurement Formatting
220
-
221
- The system auto-converts units based on locale (metric vs imperial) unless you opt out.
222
-
223
- ### Length / distance
149
+ Currency: always pass `code:`. The code picks the currency; the locale only
150
+ decides where the symbol goes and which separators are used.
224
151
 
225
152
  ```swift
226
- let distance = Measurement(value: 5, unit: UnitLength.kilometers)
227
-
228
- distance.formatted(.measurement(width: .wide))
229
- // US: "3.1 miles" (auto-converts to imperial!)
230
- // DE: "5 Kilometer"
231
- // JP: "5 km" (with .abbreviated)
232
-
233
- distance.formatted(.measurement(width: .abbreviated)) // "3.1 mi" (US) / "5 km" (DE)
234
- distance.formatted(.measurement(width: .narrow)) // "3.1mi" (US) / "5km" (DE)
235
-
236
- // Prevent auto-conversion (keep original unit)
237
- distance.formatted(.measurement(width: .wide, usage: .asProvided))
238
- // US: "5 kilometers" (keeps km even in US locale)
153
+ let fare = Decimal(124_950) / 100
154
+ fare.formatted(.currency(code: "USD")) // $1,249.50 in en_US
155
+ fare.formatted(.currency(code: "EUR")) // de_DE: "1.249,50" then the euro sign
156
+ Decimal(3200).formatted(.currency(code: "JPY")) // no decimals for yen
157
+ fare.formatted(.currency(code: "CAD").presentation(.narrow)) // "$" when space is tight
158
+ Text(fare, format: .currency(code: "GBP"))
239
159
  ```
240
160
 
241
- ### Weight / mass
242
-
243
- ```swift
244
- let weight = Measurement(value: 75, unit: UnitMass.kilograms)
161
+ Money belongs in `Decimal`, not `Double`, so binary rounding never shows up in
162
+ a price.
245
163
 
246
- weight.formatted(.measurement(width: .wide))
247
- // US: "165.3 pounds" / DE: "75 Kilogramm"
248
-
249
- weight.formatted(.measurement(width: .abbreviated, usage: .personWeight))
250
- // Uses locale-appropriate unit for body weight
251
- ```
252
-
253
- ### Temperature
164
+ Ordinals: the iOS 26.4 SDK has no ordinal `FormatStyle` (`.number.notation`
165
+ offers only `.automatic`, `.compactName` and `.scientific`), so this is the one
166
+ case that still uses `NumberFormatter`:
254
167
 
255
168
  ```swift
256
- let temp = Measurement(value: 22, unit: UnitTemperature.celsius)
257
-
258
- temp.formatted(.measurement(width: .abbreviated))
259
- // US: "72 F" (auto-converts!) / FR: "22 C" / DE: "22 C"
260
-
261
- // Weather-specific (ensures locale-correct unit)
262
- temp.formatted(.measurement(width: .abbreviated, usage: .weather))
169
+ let ordinals = NumberFormatter()
170
+ ordinals.numberStyle = .ordinal
171
+ ordinals.string(from: 3) // "3rd", German "3.", French "3e"
263
172
  ```
264
173
 
265
- ### Speed
174
+ ## Measurements
266
175
 
267
- ```swift
268
- let speed = Measurement(value: 100, unit: UnitSpeed.kilometersPerHour)
269
- speed.formatted(.measurement(width: .abbreviated))
270
- // US: "62.1 mph" / DE: "100 km/h"
271
- ```
272
-
273
- ### Volume
176
+ Units convert to the locale's system (metric or imperial) unless you opt out.
274
177
 
275
178
  ```swift
276
- let volume = Measurement(value: 500, unit: UnitVolume.milliliters)
277
- volume.formatted(.measurement(width: .abbreviated, usage: .drink))
278
- // US: "16.9 fl oz" / DE: "500 ml"
279
- ```
179
+ let hike = Measurement(value: 12.4, unit: UnitLength.kilometers)
180
+ hike.formatted(.measurement(width: .wide)) // "7.7 miles" in en_US
181
+ hike.formatted(.measurement(width: .abbreviated)) // "7.7 mi"
182
+ hike.formatted(.measurement(width: .narrow)) // "7.7mi"
183
+ hike.formatted(.measurement(width: .wide, usage: .asProvided)) // keeps km
280
184
 
281
- ## Duration Formatting
185
+ let luggage = Measurement(value: 23, unit: UnitMass.kilograms)
186
+ luggage.formatted(.measurement(width: .abbreviated, usage: .personWeight))
282
187
 
283
- ### Time pattern
188
+ let outside = Measurement(value: 28, unit: UnitTemperature.celsius)
189
+ outside.formatted(.measurement(width: .abbreviated, usage: .weather)) // F in en_US
284
190
 
285
- ```swift
286
- let dur = Duration.seconds(3661) // 1 hour, 1 minute, 1 second
191
+ let windSpeed = Measurement(value: 40, unit: UnitSpeed.kilometersPerHour)
192
+ windSpeed.formatted(.measurement(width: .abbreviated)) // mph in en_US
287
193
 
288
- dur.formatted(.time(pattern: .hourMinuteSecond)) // "1:01:01"
289
- dur.formatted(.time(pattern: .hourMinute)) // "1:01"
290
- dur.formatted(.time(pattern: .minuteSecond)) // "61:01"
194
+ let bottle = Measurement(value: 500, unit: UnitVolume.milliliters)
195
+ bottle.formatted(.measurement(width: .abbreviated, usage: .liquid)) // "1.1 pt" in en_US
291
196
  ```
292
197
 
293
- ### Units style (iOS 16+)
294
-
295
- ```swift
296
- dur.formatted(.units(allowed: [.hours, .minutes], width: .wide))
297
- // "1 hour, 1 minute" (EN) / "1 Stunde, 1 Minute" (DE)
198
+ Usage hints such as `.personWeight`, `.weather` and `.liquid` (iOS 16) choose
199
+ the unit people expect for that kind of value.
298
200
 
299
- dur.formatted(.units(allowed: [.hours, .minutes], width: .abbreviated))
300
- // "1 hr, 1 min" (EN) / "1 Std., 1 Min." (DE)
201
+ ## Durations
301
202
 
302
- dur.formatted(.units(allowed: [.hours, .minutes], width: .narrow))
303
- // "1h 1m"
304
-
305
- // Maximum unit count
306
- dur.formatted(.units(allowed: [.hours, .minutes, .seconds],
307
- width: .abbreviated,
308
- maximumUnitCount: 2))
309
- // "1 hr, 1 min" (drops seconds)
203
+ ```swift
204
+ let workout = Duration.seconds(3_725)
205
+ workout.formatted(.time(pattern: .hourMinuteSecond)) // "1:02:05"
206
+ workout.formatted(.time(pattern: .hourMinute)) // "1:02"
207
+ workout.formatted(.time(pattern: .minuteSecond)) // "62:05", minutes may pass 59
208
+
209
+ // iOS 16+
210
+ workout.formatted(.units(allowed: [.hours, .minutes], width: .wide)) // "1 hour, 2 minutes"
211
+ workout.formatted(.units(allowed: [.hours, .minutes, .seconds], width: .abbreviated))
212
+ workout.formatted(.units(width: .narrow, maximumUnitCount: 1)) // "1h"
310
213
  ```
311
214
 
312
- ## PersonNameComponents Formatting
215
+ Widths are `.wide`, `.abbreviated` and `.narrow`; `maximumUnitCount:` drops the
216
+ smaller units.
313
217
 
314
- Respects locale conventions for name ordering (given-family vs family-given).
218
+ ## Person names
315
219
 
316
220
  ```swift
317
- var name = PersonNameComponents()
318
- name.givenName = "John"
319
- name.familyName = "Appleseed"
320
- name.namePrefix = "Dr."
321
- name.nickname = "Johnny"
322
-
323
- name.formatted(.name(style: .long)) // "Dr. John Appleseed" (US) / "Appleseed John" (JP)
324
- name.formatted(.name(style: .medium)) // "John Appleseed" (US) / "Appleseed John" (JP)
325
- name.formatted(.name(style: .short)) // "John" (US) / "Appleseed" (JP)
326
- name.formatted(.name(style: .abbreviated)) // "JA" (initials)
327
-
328
- // In SwiftUI
329
- Text(name, format: .name(style: .medium))
221
+ var guest = PersonNameComponents()
222
+ guest.namePrefix = "Dr."
223
+ guest.givenName = "Hana"
224
+ guest.familyName = "Sato"
225
+
226
+ guest.formatted(.name(style: .long)) // includes the prefix; order follows locale
227
+ guest.formatted(.name(style: .medium))
228
+ guest.formatted(.name(style: .short)) // "Hana" in en_US
229
+ guest.formatted(.name(style: .abbreviated)) // initials
230
+ Text(guest, format: .name(style: .medium))
330
231
  ```
331
232
 
332
- ## ByteCountFormatStyle
333
-
334
- Format file sizes with locale-appropriate units.
233
+ ## Byte counts
335
234
 
336
235
  ```swift
337
- let bytes: Int64 = 1_536_000
338
-
339
- bytes.formatted(.byteCount(style: .file)) // "1.5 MB"
340
- bytes.formatted(.byteCount(style: .memory)) // "1.46 MB" (uses 1024-based)
341
- bytes.formatted(.byteCount(style: .binary)) // "1.46 MB"
342
-
343
- // Specific allowed units
344
- bytes.formatted(.byteCount(style: .file, allowedUnits: [.kb])) // "1,536 kB"
236
+ let backupSize: Int64 = 3_400_000_000
237
+ backupSize.formatted(.byteCount(style: .file))
238
+ backupSize.formatted(.byteCount(style: .memory)) // 1024-based
239
+ backupSize.formatted(.byteCount(style: .binary))
240
+ Int64(5_120).formatted(.byteCount(style: .file, allowedUnits: [.kb]))
345
241
  ```
346
242
 
347
- ## ListFormatStyle
348
-
349
- Join arrays into grammatically correct lists.
243
+ ## Lists
350
244
 
351
245
  ```swift
352
- let fruits = ["Apples", "Oranges", "Bananas"]
353
-
354
- fruits.formatted(.list(type: .and))
355
- // EN: "Apples, Oranges, and Bananas"
356
- // FR: "Apples, Oranges et Bananas"
357
- // AR: "Apples وOranges وBananas"
246
+ let toppings = ["olives", "basil", "garlic"]
247
+ toppings.formatted(.list(type: .and)) // "olives, basil, and garlic"; French uses "et"
248
+ toppings.formatted(.list(type: .or))
358
249
 
359
- fruits.formatted(.list(type: .or))
360
- // EN: "Apples, Oranges, or Bananas"
250
+ let tips: [Decimal] = [5, 10, 15]
251
+ tips.formatted(.list(memberStyle: .currency(code: "USD"), type: .or))
361
252
 
362
- // With member formatting
363
- let prices = [Decimal(1.99), Decimal(2.49), Decimal(3.99)]
364
- prices.formatted(.list(memberStyle: .currency(code: "USD"), type: .and))
365
- // "$1.99, $2.49, and $3.99"
366
-
367
- // Two items
368
- ["Red", "Blue"].formatted(.list(type: .and))
369
- // "Red and Blue" (no Oxford comma for two items)
253
+ ["olives", "basil"].formatted(.list(type: .and)) // "olives and basil", no serial comma
370
254
  ```
371
255
 
372
- ## Custom FormatStyle Implementation
256
+ ## Custom styles
373
257
 
374
- Create a reusable `FormatStyle` for domain-specific formatting.
258
+ Designing custom and parseable styles is `swift-formatstyle` territory. The
259
+ shape, for recognition:
375
260
 
376
261
  ```swift
377
- struct AbbreviatedCountStyle: FormatStyle {
262
+ struct ScoreFormat: FormatStyle {
378
263
  func format(_ value: Int) -> String {
264
+ let number = Double(value)
265
+ let style = FloatingPointFormatStyle<Double>.number.precision(.fractionLength(0...1))
379
266
  switch value {
380
- case ..<1_000:
381
- return "\(value)"
382
- case 1_000..<1_000_000:
383
- let k = Double(value) / 1_000.0
384
- return k.formatted(.number.precision(.fractionLength(0...1))) + "K"
385
- case 1_000_000..<1_000_000_000:
386
- let m = Double(value) / 1_000_000.0
387
- return m.formatted(.number.precision(.fractionLength(0...1))) + "M"
388
- default:
389
- let b = Double(value) / 1_000_000_000.0
390
- return b.formatted(.number.precision(.fractionLength(0...1))) + "B"
267
+ case 1_000_000_000...: return (number / 1_000_000_000).formatted(style) + "B"
268
+ case 1_000_000...: return (number / 1_000_000).formatted(style) + "M"
269
+ case 1_000...: return (number / 1_000).formatted(style) + "K"
270
+ default: return value.formatted()
391
271
  }
392
272
  }
393
273
  }
394
274
 
395
- extension FormatStyle where Self == AbbreviatedCountStyle {
396
- static var abbreviatedCount: AbbreviatedCountStyle { .init() }
275
+ extension FormatStyle where Self == ScoreFormat {
276
+ static var score: ScoreFormat { ScoreFormat() }
397
277
  }
398
278
 
399
- // Usage
400
- let followers = 12_500
401
- followers.formatted(.abbreviatedCount) // "12.5K"
402
-
403
- // In SwiftUI
404
- Text(followers, format: .abbreviatedCount)
279
+ let shown = 48_200.formatted(.score) // "48.2K"
280
+ Text(48_200, format: .score)
405
281
  ```
406
282
 
407
- ### Custom ParseableFormatStyle (for input parsing)
408
-
409
- ```swift
410
- struct AbbreviatedCountStyle: ParseableFormatStyle {
411
- var parseStrategy: AbbreviatedCountParseStrategy { .init() }
412
-
413
- func format(_ value: Int) -> String { /* same as above */ }
414
- }
415
-
416
- struct AbbreviatedCountParseStrategy: ParseStrategy {
417
- func parse(_ value: String) throws -> Int {
418
- let cleaned = value.uppercased().trimmingCharacters(in: .whitespaces)
419
- if cleaned.hasSuffix("K") {
420
- guard let num = Double(cleaned.dropLast()) else { throw parseError }
421
- return Int(num * 1_000)
422
- }
423
- // ... handle M, B, plain numbers
424
- guard let num = Int(cleaned) else { throw parseError }
425
- return num
426
- }
427
-
428
- private var parseError: some Error {
429
- DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Invalid count"))
430
- }
431
- }
432
- ```
283
+ A parseable variant adds `ParseableFormatStyle` with a `parseStrategy` whose
284
+ `parse(_:) throws` reads the suffix back and throws on malformed input, for
285
+ example `DecodingError.dataCorrupted`. Send any real implementation work there.
433
286
 
434
- ## Forcing a Specific Locale
287
+ ## Pinning a locale
435
288
 
436
- Occasionally you need a specific locale (server APIs, fixed-format exports). Use `.locale()` modifier:
289
+ `.locale(_:)` forces a locale, which is right for serialization or for a
290
+ document that must always look one way, such as a German invoice PDF:
437
291
 
438
292
  ```swift
439
- // Force US format for API serialization (not user display)
440
- let usPrice = price.formatted(.currency(code: "USD").locale(Locale(identifier: "en_US")))
441
-
442
- // Force a date format for an API
443
- let apiDate = date.formatted(.iso8601) // Prefer ISO 8601 for APIs
444
-
445
- // Force German format for a German-language PDF export
446
- let deDate = date.formatted(.dateTime.month(.wide).day().year().locale(Locale(identifier: "de_DE")))
293
+ let invoiceDate = Date.now.formatted(.dateTime.day().month().year().locale(Locale(identifier: "de_DE")))
294
+ let payloadDate = Date.now.formatted(.iso8601) // preferred for APIs
447
295
  ```
448
296
 
449
- **Warning:** Never force a locale for user-facing UI. Always let the system locale drive user-visible formatting.
450
-
451
- ## RTL Layout Deep Dive
452
-
453
- ### How SwiftUI auto-mirrors
297
+ Never pin a locale in user-facing UI.
454
298
 
455
- SwiftUI respects `layoutDirection` from the environment. When the user's language is RTL:
299
+ ## Right-to-left in depth
456
300
 
457
- 1. **HStack**: Children render right-to-left
458
- 2. **Leading/Trailing**: `.leading` = right side, `.trailing` = left side
459
- 3. **Padding**: `.padding(.leading, 16)` applies to right side
460
- 4. **NavigationStack**: Back button appears on trailing (left) side
461
- 5. **Lists**: Disclosure chevrons point left
462
- 6. **ScrollView**: Horizontal scrolling starts from the right
463
- 7. **Text alignment**: Default alignment follows reading direction
301
+ SwiftUI takes `layoutDirection` from the environment. Under RTL, `.leading` is
302
+ the right edge, horizontal `ScrollView` content starts at the right, list
303
+ chevrons point left and the back button sits on the left.
464
304
 
465
- ### Image flipping
305
+ Flip: `chevron.forward`, `arrow.right` and custom directional assets, using
306
+ `.flipsForRightToLeftLayoutDirection(true)`.
466
307
 
467
- ```swift
468
- // Directional images SHOULD flip
469
- Image(systemName: "chevron.forward")
470
- .flipsForRightToLeftLayoutDirection(true)
471
-
472
- Image(systemName: "arrow.right")
473
- .flipsForRightToLeftLayoutDirection(true)
474
-
475
- Image("progress-arrow")
476
- .flipsForRightToLeftLayoutDirection(true)
477
-
478
- // These should NOT flip:
479
- // - Logos and brand marks
480
- // - Photos and illustrations
481
- // - Clock faces (clockwise is universal)
482
- // - Music notation
483
- // - Checkmarks
484
- // - Mathematical symbols (+, -, =)
485
- // - Media playback controls (play triangle always points right)
486
-
487
- // SF Symbols with .rtl variant auto-flip (e.g., text.alignleft has text.alignright)
488
- // Check SF Symbols app for RTL variants
489
- ```
308
+ Do not flip: logos and brand marks, photos and illustrations, clock faces,
309
+ music notation, checkmarks, mathematical symbols, media playback controls.
490
310
 
491
- ### Environment-based testing
311
+ SF Symbols that ship an RTL variant switch on their own; the SF Symbols app
312
+ shows which do.
492
313
 
493
314
  ```swift
494
- // Preview with RTL
495
- #Preview("Arabic RTL") {
496
- ContentView()
315
+ #Preview("Seat map, Hebrew") {
316
+ SeatMapView()
497
317
  .environment(\.layoutDirection, .rightToLeft)
498
- .environment(\.locale, Locale(identifier: "ar"))
318
+ .environment(\.locale, Locale(identifier: "he"))
499
319
  }
500
320
 
501
- // Preview with both directions side by side
502
- #Preview("LTR vs RTL") {
503
- HStack(spacing: 0) {
504
- ContentView()
505
- .environment(\.layoutDirection, .leftToRight)
506
- .frame(maxWidth: .infinity)
507
- Divider()
508
- ContentView()
509
- .environment(\.layoutDirection, .rightToLeft)
510
- .environment(\.locale, Locale(identifier: "ar"))
511
- .frame(maxWidth: .infinity)
321
+ #Preview("Both directions") {
322
+ HStack {
323
+ FareRow().environment(\.layoutDirection, .leftToRight)
324
+ FareRow().environment(\.layoutDirection, .rightToLeft)
512
325
  }
513
326
  }
514
327
  ```
515
328
 
516
- ### Semantic content attributes (UIKit interop)
517
-
518
- When mixing UIKit views via `UIViewRepresentable`, set semantic content attribute:
329
+ UIKit views inside a `UIViewRepresentable` read `semanticContentAttribute`:
330
+ `.forceLeftToRight`, `.forceRightToLeft`, or `.unspecified`, the default, which
331
+ follows the system.
519
332
 
520
333
  ```swift
521
- class MyUIView: UIView {
522
- override var semanticContentAttribute: UISemanticContentAttribute {
523
- // .forceLeftToRight for phone numbers, code
524
- // .forceRightToLeft to force RTL
525
- // .unspecified to follow system (default)
526
- .unspecified
334
+ struct CardNumberField: UIViewRepresentable {
335
+ func makeUIView(context: Context) -> UITextField {
336
+ let field = UITextField()
337
+ field.semanticContentAttribute = .forceLeftToRight
338
+ return field
527
339
  }
340
+ func updateUIView(_ uiView: UITextField, context: Context) {}
528
341
  }
529
342
  ```
530
343
 
531
- ### Bidirectional text
532
-
533
- When mixing LTR and RTL text (e.g., English brand names in Arabic text), Unicode bidirectional algorithm handles it automatically. For edge cases:
344
+ Mixed-direction text follows the Unicode bidirectional algorithm. For edge
345
+ cases like a phone number inside Arabic text, prefix a left-to-right mark
346
+ `\u{200E}`, or set `.environment(\.layoutDirection, .leftToRight)` on that one
347
+ view.
534
348
 
535
- ```swift
536
- // Force LTR for specific content within RTL context
537
- Text("\u{200E}+1 (555) 123-4567") // LTR mark before phone number
538
-
539
- // Or use environment override on a specific view
540
- Text(phoneNumber)
541
- .environment(\.layoutDirection, .leftToRight)
542
- ```
349
+ | Problem | Fix |
350
+ |---|---|
351
+ | `.padding(.left)` | `.padding(.leading)` |
352
+ | `.offset(x:)` to push content one way | Alignment or trailing padding |
353
+ | `.multilineTextAlignment(.left)` | `.multilineTextAlignment(.leading)` |
354
+ | Rounding top-left and top-right corners | Round leading and trailing corners |
355
+ | "Swipe right to archive" | "Swipe toward the leading edge", or rely on system swipe actions |
543
356
 
544
- ### Common RTL pitfalls
357
+ ## ScaledMetric and Dynamic Type
545
358
 
546
- | Issue | Wrong | Correct |
547
- |-------|-------|---------|
548
- | Fixed position | `.padding(.left, 16)` | `.padding(.leading, 16)` |
549
- | Absolute offset | `.offset(x: -20)` for "move left" | Use alignment or `.padding(.trailing)` |
550
- | Text alignment | `.multilineTextAlignment(.left)` | `.multilineTextAlignment(.leading)` |
551
- | Corner radius | Only rounding top-left/top-right | Round leading/trailing corners |
552
- | Swipe gestures | "Swipe right to delete" | "Swipe to leading edge" -- or use system gestures |
553
-
554
- ## `@ScaledMetric` for Dynamic Type
555
-
556
- Use `@ScaledMetric` to make custom spacing, icon sizes, and padding scale with the user's Dynamic Type setting.
359
+ `@ScaledMetric(relativeTo:)` scales your own numbers, such as spacing, icon
360
+ size and padding, together with the text style you name.
557
361
 
558
362
  ```swift
559
- struct ProfileRow: View {
560
- @ScaledMetric(relativeTo: .body) private var avatarSize = 44.0
561
- @ScaledMetric(relativeTo: .body) private var spacing = 12.0
363
+ struct ContactRow: View {
364
+ @ScaledMetric(relativeTo: .body) private var portraitSide: CGFloat = 36
365
+ @ScaledMetric(relativeTo: .body) private var gap: CGFloat = 10
562
366
 
563
367
  var body: some View {
564
- HStack(spacing: spacing) {
565
- AvatarView()
566
- .frame(width: avatarSize, height: avatarSize)
567
- VStack(alignment: .leading) {
568
- Text(name).font(.headline)
569
- Text(subtitle).font(.subheadline)
570
- }
368
+ HStack(spacing: gap) {
369
+ Image(systemName: "person.crop.circle")
370
+ .resizable()
371
+ .frame(width: portraitSide, height: portraitSide)
372
+ Text("Mira Lopez")
571
373
  }
572
374
  }
573
375
  }
574
376
  ```
575
377
 
576
- ### relativeTo parameter
577
-
578
- `@ScaledMetric` scales proportionally to a text style. Choose the text style that the metric logically accompanies:
579
-
580
- | Text style | Base size | Use for |
581
- |------------|-----------|---------|
582
- | `.body` | 17pt | General spacing, icons next to body text |
583
- | `.caption` | 12pt | Small icons, fine spacing |
584
- | `.title` | 28pt | Large icons, hero spacing |
585
- | `.largeTitle` | 34pt | Hero images, splash elements |
378
+ Which style to scale against:
586
379
 
587
- ### Testing Dynamic Type
380
+ | `relativeTo` | Base size | Good for |
381
+ |---|---|---|
382
+ | `.body` | 17 pt | General spacing |
383
+ | `.caption` | 12 pt | Small icons |
384
+ | `.title` | 28 pt | Large icons |
385
+ | `.largeTitle` | 34 pt | Hero elements |
588
386
 
589
387
  ```swift
590
- // Preview with large text
591
- #Preview("Accessibility XXL") {
592
- ContentView()
593
- .dynamicTypeSize(.accessibility3)
388
+ #Preview("Accessibility 3") {
389
+ ContactRow().dynamicTypeSize(.accessibility3)
594
390
  }
595
391
 
596
- // Preview matrix
597
- #Preview("Dynamic Type Sizes") {
392
+ #Preview("All sizes") {
598
393
  ScrollView {
599
394
  ForEach(DynamicTypeSize.allCases, id: \.self) { size in
600
- ContentView()
601
- .dynamicTypeSize(size)
602
- .padding()
603
- .border(Color.gray)
395
+ ContactRow().dynamicTypeSize(size)
604
396
  }
605
397
  }
606
398
  }
607
399
  ```
608
400
 
609
- ## Layout Testing with Accessibility Inspector
610
-
611
- Accessibility Inspector (Xcode > Open Developer Tool > Accessibility Inspector) provides:
612
-
613
- 1. **Audit**: Scans running app for accessibility issues including truncated text
614
- 2. **Inspection**: Shows exact font sizes and Dynamic Type response
615
- 3. **Settings**: Override Dynamic Type size, Bold Text, Reduce Motion on device without changing system settings
616
-
617
- ### Quick test workflow
618
-
619
- 1. Launch app in Simulator
620
- 2. Open Accessibility Inspector, target the Simulator
621
- 3. Use the Settings panel to set Dynamic Type to "Accessibility XXL"
622
- 4. Navigate through every screen -- look for truncated text, overlapping elements, broken layouts
623
- 5. Switch to RTL (set language to Arabic in scheme options)
624
- 6. Repeat navigation -- check all alignment and reading order
625
-
626
- ## Quick Reference Table
627
-
628
- | Data type | FormatStyle | Example output (US) |
629
- |-----------|-------------|---------------------|
630
- | `Date` | `.dateTime.month().day().year()` | "Jan 15, 2026" |
631
- | `Date` range | `(start..<end).formatted(date:time:)` | "Jan 15 - 20, 2026" |
632
- | `Date` relative | `.relative(presentation: .named)` | "yesterday" |
633
- | `Int` | `.number` | "1,234,567" |
634
- | `Int` ordinal | `.number.notation(.ordinal)` | "3rd" |
635
- | `Int` compact | `.number.notation(.compactName)` | "1.2M" |
636
- | `Double` | `.number.precision(.fractionLength(2))` | "3.14" |
637
- | `Double` | `.percent` | "85.6%" |
638
- | `Decimal` | `.currency(code: "USD")` | "$29.99" |
639
- | `Measurement` | `.measurement(width: .abbreviated)` | "5 km" / "3.1 mi" |
640
- | `Duration` | `.time(pattern: .hourMinuteSecond)` | "1:01:01" |
641
- | `Duration` | `.units(width: .abbreviated)` | "1 hr, 1 min" |
642
- | `PersonNameComponents` | `.name(style: .medium)` | "John Doe" |
643
- | `Int64` (bytes) | `.byteCount(style: .file)` | "1.5 MB" |
644
- | `[String]` | `.list(type: .and)` | "A, B, and C" |
401
+ ## Checking layout with Accessibility Inspector
402
+
403
+ Open it from Xcode > Open Developer Tool > Accessibility Inspector.
404
+
405
+ - Audit finds problems, truncated text among them.
406
+ - Inspection shows font sizes and whether an element reacts to Dynamic Type.
407
+ - Settings overrides Dynamic Type, Bold Text and Reduce Motion without touching
408
+ system settings.
409
+
410
+ Routine:
411
+
412
+ 1. Run the app in Simulator and point the inspector at it.
413
+ 2. Set Dynamic Type to Accessibility XXL.
414
+ 3. Visit every screen and look for truncation and overlap.
415
+ 4. Switch the scheme's App Language to Arabic.
416
+ 5. Visit the screens again for alignment and reading order.
417
+
418
+ ## Cheat sheet
419
+
420
+ | Value | Style | en_US sample |
421
+ |---|---|---|
422
+ | `Date` | `.dateTime.month().day().year()` | Jun 3, 2026 |
423
+ | Date range | `(start..<end).formatted(date:time:)` | Jun 3 - 7, 2026 |
424
+ | Relative date | `.relative(presentation: .named)` | yesterday |
425
+ | `Int` | `.number` | 2,500,000 |
426
+ | `Int` | `NumberFormatter` with `numberStyle = .ordinal` | 3rd |
427
+ | `Int` | `.number.notation(.compactName)` | 4.3M |
428
+ | `Double` | `.number.precision(.fractionLength(2))` | 72.50 |
429
+ | `Double` | `.percent` | 92% (for 0.92) |
430
+ | `Decimal` | `.currency(code: "USD")` | $1,249.50 |
431
+ | `Measurement` | `.measurement(width: .abbreviated)` | 7.7 mi |
432
+ | `Duration` | `.time(pattern: .hourMinuteSecond)` | 1:02:05 |
433
+ | `Duration` | `.units(width: .abbreviated)` | 1 hr, 2 min, 5 sec |
434
+ | `PersonNameComponents` | `.name(style: .medium)` | Hana Sato |
435
+ | `Int64` | `.byteCount(style: .file)` | 3.4 GB |
436
+ | `[String]` | `.list(type: .and)` | olives, basil, and garlic |