@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.
- package/CHANGELOG.md +9 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +266 -267
- package/package.json +2 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +68 -68
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,341 +1,365 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: swift-formatstyle
|
|
3
|
-
description: "
|
|
3
|
+
description: "Display formatting with FormatStyle (iOS 15+): integers, floating point, decimals, currency, percentages, dates, date ranges, relative and anchored relative dates, durations (Duration.TimeFormatStyle, Duration.UnitsFormatStyle, iOS 16+), measurements, person names, byte counts, lists, URLs (iOS 16+), parsing, custom FormatStyle conformances. Use when formatting values for display, replacing legacy Formatter subclasses, or writing a custom format style. String Catalogs, plurals and RTL review belong to ios-localization."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Swift FormatStyle
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
|
39
|
-
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
| `
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
| `
|
|
47
|
-
|
|
|
48
|
-
| `
|
|
49
|
-
| `PersonNameComponents` | `.name(style:)` | `name.formatted(.name(style: .short))` → `"Tom"` |
|
|
50
|
-
| `[String]` | `.list(type:width:)` | `["A","B","C"].formatted(.list(type: .and))` → `"A, B, and C"` |
|
|
51
|
-
| Byte count | `.byteCount(style:)` | `Int64(1_048_576).formatted(.byteCount(style: .memory))` → `"1 MB"` |
|
|
52
|
-
| `URL` | `.url` | `url.formatted(.url.scheme(.never).host().path())` |
|
|
10
|
+
`FormatStyle` describes how one kind of value becomes text, as a small typed
|
|
11
|
+
value you can chain and reuse, instead of configured `Formatter` objects. Foundation caches the
|
|
12
|
+
expensive machinery behind them, so a style can be written inline wherever a
|
|
13
|
+
value is shown. Availability: iOS 15 for the protocol and most styles, iOS 16
|
|
14
|
+
for the `Duration` and `URL` styles, iOS 18 for `Date.AnchoredRelativeFormatStyle`.
|
|
15
|
+
|
|
16
|
+
## Locale review is part of the job
|
|
17
|
+
|
|
18
|
+
Formatting output changes with the user's region even in an app that ships one
|
|
19
|
+
language. Whenever this skill is used, recommend previewing or testing at least
|
|
20
|
+
`en_US`, `de_DE`, `ar_SA` and `ja_JP`, and check:
|
|
21
|
+
|
|
22
|
+
- decimal and grouping separators,
|
|
23
|
+
- the numbering system (Arabic-Indic digits in `ar_SA`),
|
|
24
|
+
- calendar and date order,
|
|
25
|
+
- where the currency sign goes and which units people expect,
|
|
26
|
+
- right-to-left output, and any layout that depends on the string's length.
|
|
27
|
+
|
|
28
|
+
Work on String Catalogs, bundles, plural rules, translated copy and RTL layout
|
|
29
|
+
review belongs to `ios-localization`. "We are not adding languages" is not a
|
|
30
|
+
reason to skip that review.
|
|
31
|
+
|
|
32
|
+
## Quick reference
|
|
33
|
+
|
|
34
|
+
| Value | Style | Example output (en_US) |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| `Int`, `Double` | `.number` | `9.formatted(.number.precision(.fractionLength(2)))` gives "9.00" |
|
|
37
|
+
| Currency | `.currency(code:)` | 12.5 with `"USD"` gives "$12.50" |
|
|
38
|
+
| Percent | `.percent` | 0.4 gives "40%" |
|
|
39
|
+
| `Date` | `.dateTime` | `.dateTime.month().day().year()` gives "Mar 3, 2026" |
|
|
40
|
+
| Date range | `.interval` | `(opening..<closing).formatted(.interval)` |
|
|
41
|
+
| Relative date | `.relative(presentation:unitsStyle:)` | `.named` gives "yesterday" |
|
|
42
|
+
| `Duration` | `.time(pattern:)` | 3661 seconds as `.hourMinuteSecond` is "1:01:01" |
|
|
43
|
+
| `Duration` | `.units(allowed:width:)` | 90 seconds is "1 min, 30 sec" |
|
|
44
|
+
| `Measurement` | `.measurement(width:)` | "72°F" |
|
|
45
|
+
| `PersonNameComponents` | `.name(style:)` | "Ada Lovelace" |
|
|
46
|
+
| `[String]` | `.list(type:width:)` | three names with `.and` read "Mina, Omar, and Lea" |
|
|
47
|
+
| Byte count | `.byteCount(style:)` | one mebibyte (1,048,576 bytes) with `.memory` is "1 MB" |
|
|
48
|
+
| `URL` | `.url` | `.url.scheme(.never).host().path()` drops "https://" and the query |
|
|
53
49
|
|
|
54
50
|
## Numbers
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
let n = 1234567.formatted() // "1,234,567" (en_US)
|
|
59
|
-
|
|
60
|
-
// Precision
|
|
61
|
-
1234.5.formatted(.number.precision(.fractionLength(0...2))) // "1,234.5"
|
|
62
|
-
1234.5.formatted(.number.precision(.significantDigits(3))) // "1,230"
|
|
52
|
+
Plain `formatted()` already follows the user's locale: 2500000 shows as
|
|
53
|
+
"2,500,000" in `en_US` and "2.500.000" in `de_DE`.
|
|
63
54
|
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
```swift
|
|
56
|
+
let upToTwo = FloatingPointFormatStyle<Double>.number.precision(.fractionLength(0...2))
|
|
57
|
+
let threeSignificant = FloatingPointFormatStyle<Double>.number.precision(.significantDigits(3))
|
|
58
|
+
(7.0 / 3.0).formatted(upToTwo) // "2.33"
|
|
59
|
+
(7.0 / 3.0).formatted(threeSignificant) // "2.33"
|
|
60
|
+
|
|
61
|
+
let seats = 1834
|
|
62
|
+
seats.formatted(.number.rounded(rule: .down, increment: 100)) // "1,800"
|
|
63
|
+
seats.formatted(.number.grouping(.never)) // "1834"
|
|
64
|
+
seats.formatted(.number.sign(strategy: .always())) // "+1,834"
|
|
65
|
+
seats.formatted(.number.locale(Locale(identifier: "fr_FR"))) // "1 834"
|
|
66
|
+
|
|
67
|
+
let audience = 3_400_000
|
|
68
|
+
audience.formatted(.number.notation(.compactName)) // "3.4M"
|
|
69
|
+
0.00042.formatted(.number.notation(.scientific)) // "4.2E-4"
|
|
70
|
+
```
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
The styles behind these are `IntegerFormatStyle` and
|
|
73
|
+
`FloatingPointFormatStyle` (and `Decimal.FormatStyle`).
|
|
69
74
|
|
|
70
|
-
|
|
71
|
-
1_200_000.formatted(.number.notation(.compactName)) // "1.2M"
|
|
72
|
-
42.formatted(.number.notation(.scientific)) // "4.2E1"
|
|
75
|
+
## Currency
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
The ISO 4217 code decides the symbol and the usual number of fraction digits,
|
|
78
|
+
so yen shows no decimals:
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
```swift
|
|
81
|
+
18.75.formatted(.currency(code: "EUR")) // "€18.75" in en_US
|
|
82
|
+
30.0.formatted(.currency(code: "JPY")) // "¥30"
|
|
79
83
|
```
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
[FloatingPointFormatStyle](https://sosumi.ai/documentation/foundation/floatingpointformatstyle)
|
|
83
|
-
|
|
84
|
-
## Currency
|
|
85
|
+
To override precision, build the style explicitly:
|
|
85
86
|
|
|
86
87
|
```swift
|
|
87
|
-
|
|
88
|
-
29.99.formatted(.currency(code: "EUR")) // "€29.99"
|
|
89
|
-
29.99.formatted(.currency(code: "JPY")) // "¥30"
|
|
90
|
-
|
|
91
|
-
// Customize precision
|
|
92
|
-
let style = FloatingPointFormatStyle<Double>.Currency(code: "USD")
|
|
88
|
+
let noCents = FloatingPointFormatStyle<Double>.Currency(code: "USD")
|
|
93
89
|
.precision(.fractionLength(0))
|
|
94
|
-
|
|
90
|
+
let price = 1299.99
|
|
91
|
+
price.formatted(noCents) // "$1,300"
|
|
95
92
|
```
|
|
96
93
|
|
|
97
94
|
## Percentages
|
|
98
95
|
|
|
96
|
+
A floating-point value is a fraction; an integer is already a percentage.
|
|
97
|
+
|
|
99
98
|
```swift
|
|
100
|
-
0.
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
let completion = 0.8567
|
|
100
|
+
completion.formatted(.percent) // "85.67%"
|
|
101
|
+
completion.formatted(.percent.precision(.fractionLength(1))) // "85.7%"
|
|
102
|
+
let passRate = 42
|
|
103
|
+
passRate.formatted(.percent) // "42%"
|
|
103
104
|
```
|
|
104
105
|
|
|
105
106
|
## Dates
|
|
106
107
|
|
|
108
|
+
Build exactly the fields you want; the locale decides order and punctuation.
|
|
109
|
+
|
|
107
110
|
```swift
|
|
108
|
-
let
|
|
111
|
+
let launch = Date.now
|
|
112
|
+
launch.formatted(.dateTime.day().month().year()) // "Mar 3, 2026" (order comes from the locale)
|
|
113
|
+
launch.formatted(.dateTime.minute().hour()) // "4:05 PM"
|
|
114
|
+
let spelledOut = Date.FormatStyle.dateTime.day().month(.wide).weekday(.wide)
|
|
115
|
+
launch.formatted(spelledOut) // "Tuesday, March 3"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Predefined combinations:
|
|
109
119
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
120
|
+
```swift
|
|
121
|
+
launch.formatted(date: .long, time: .shortened) // "March 3, 2026 at 4:05 PM"
|
|
122
|
+
launch.formatted(date: .abbreviated, time: .omitted) // "Mar 3, 2026"
|
|
123
|
+
launch.formatted(.iso8601) // "2026-03-03T13:05:00Z"
|
|
124
|
+
```
|
|
114
125
|
|
|
115
|
-
|
|
116
|
-
now.formatted(date: .long, time: .shortened) // "April 22, 2026 at 4:30 PM"
|
|
117
|
-
now.formatted(date: .abbreviated, time: .omitted) // "Apr 22, 2026"
|
|
126
|
+
Relative to now:
|
|
118
127
|
|
|
119
|
-
|
|
120
|
-
now.
|
|
128
|
+
```swift
|
|
129
|
+
let dayBefore = Date.now.addingTimeInterval(-86_400)
|
|
130
|
+
dayBefore.formatted(.relative(presentation: .named)) // "yesterday"
|
|
131
|
+
let numericAgo = Date.RelativeFormatStyle(presentation: .numeric)
|
|
132
|
+
dayBefore.formatted(numericAgo) // "1 day ago"
|
|
133
|
+
```
|
|
121
134
|
|
|
122
|
-
|
|
123
|
-
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: .now)!
|
|
124
|
-
yesterday.formatted(.relative(presentation: .named)) // "yesterday"
|
|
125
|
-
yesterday.formatted(.relative(presentation: .numeric)) // "1 day ago"
|
|
135
|
+
Ranges and countdowns:
|
|
126
136
|
|
|
127
|
-
|
|
128
|
-
|
|
137
|
+
```swift
|
|
138
|
+
let slot = launch..<launch.addingTimeInterval(5_400)
|
|
139
|
+
let meetingSpan = Date.IntervalFormatStyle().day().month().minute().hour()
|
|
140
|
+
slot.formatted(meetingSpan) // "Mar 3, 4:05 - 5:35 PM" (ICU prints an en dash)
|
|
129
141
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// "2 days, 5 hours"
|
|
142
|
+
let untilDeparture = Date.now..<Date.now.addingTimeInterval(190_800)
|
|
143
|
+
let countdown = Date.ComponentsFormatStyle(style: .wide, fields: [.hour, .day])
|
|
144
|
+
untilDeparture.formatted(countdown) // "2 days, 5 hours"
|
|
133
145
|
```
|
|
134
146
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
[Date.IntervalFormatStyle](https://sosumi.ai/documentation/foundation/date/intervalformatstyle)
|
|
147
|
+
Related types: `Date.FormatStyle`, `Date.RelativeFormatStyle`,
|
|
148
|
+
`Date.IntervalFormatStyle`, `Date.ComponentsFormatStyle`.
|
|
138
149
|
|
|
139
|
-
###
|
|
150
|
+
### Relative to a chosen reference (iOS 18+)
|
|
140
151
|
|
|
141
|
-
`Date.AnchoredRelativeFormatStyle`
|
|
142
|
-
|
|
152
|
+
`Date.AnchoredRelativeFormatStyle` describes one fixed date, the anchor,
|
|
153
|
+
relative to whatever moment you format. Plain `.relative` always measures from
|
|
154
|
+
the real current time; the anchored style lets you choose the reference, which
|
|
155
|
+
suits timelines, widgets and tests.
|
|
143
156
|
|
|
144
|
-
|
|
157
|
+
```swift
|
|
158
|
+
let meetup = Date(timeIntervalSince1970: 1_800_000_000)
|
|
159
|
+
let style = Date.AnchoredRelativeFormatStyle(anchor: meetup, presentation: .named)
|
|
160
|
+
style.format(meetup.addingTimeInterval(86_400)) // "yesterday": the anchor was a day before the reference
|
|
161
|
+
style.format(meetup.addingTimeInterval(-3_600)) // "in 1 hour"
|
|
162
|
+
```
|
|
145
163
|
|
|
146
164
|
## Durations
|
|
147
165
|
|
|
148
|
-
`Duration` (iOS 16+)
|
|
166
|
+
A `Duration` (iOS 16+) can be shown as a clock reading with
|
|
167
|
+
`Duration.TimeFormatStyle` or as labeled units with `Duration.UnitsFormatStyle`.
|
|
149
168
|
|
|
150
|
-
|
|
151
|
-
|
|
169
|
+
```swift
|
|
170
|
+
let ride = Duration.seconds(7_384) // 2 h 3 min 4 s
|
|
171
|
+
ride.formatted(.time(pattern: .hourMinuteSecond)) // "2:03:04"
|
|
172
|
+
ride.formatted(.time(pattern: .hourMinute)) // "2:03"
|
|
173
|
+
ride.formatted(.time(pattern: .minuteSecond)) // "123:04" (minutes are not wrapped into hours)
|
|
152
174
|
|
|
153
|
-
|
|
175
|
+
let stopwatch = Duration.TimeFormatStyle.Pattern.minuteSecond(padMinuteToLength: 2, fractionalSecondsLength: 2)
|
|
176
|
+
Duration.milliseconds(3_750).formatted(.time(pattern: stopwatch)) // "00:03.75"
|
|
177
|
+
```
|
|
154
178
|
|
|
155
179
|
```swift
|
|
156
|
-
let
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
d.formatted(.time(pattern: .hourMinute)) // "1:01"
|
|
160
|
-
d.formatted(.time(pattern: .minuteSecond)) // "61:01"
|
|
180
|
+
let clock: Set<Duration.UnitsFormatStyle.Unit> = [.hours, .minutes, .seconds]
|
|
181
|
+
ride.formatted(.units(allowed: clock, width: .abbreviated)) // "2 hr, 3 min, 4 sec"
|
|
182
|
+
ride.formatted(.units(allowed: clock, width: .wide, maximumUnitCount: 1)) // "2 hours"
|
|
161
183
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
) // "00:03.75"
|
|
184
|
+
let rest = Duration.seconds(150)
|
|
185
|
+
rest.formatted(.units(allowed: [.minutes, .seconds], width: .wide)) // "2 minutes, 30 seconds"
|
|
186
|
+
rest.formatted(.units(allowed: [.minutes, .seconds], width: .narrow)) // "2m 30s"
|
|
166
187
|
```
|
|
167
188
|
|
|
168
|
-
|
|
189
|
+
`maximumUnitCount` caps how many units appear.
|
|
190
|
+
|
|
191
|
+
## Measurements
|
|
169
192
|
|
|
170
193
|
```swift
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
) // "
|
|
174
|
-
|
|
175
|
-
Duration.seconds(90).formatted(
|
|
176
|
-
.units(allowed: [.minutes, .seconds], width: .wide)
|
|
177
|
-
) // "1 minute, 30 seconds"
|
|
178
|
-
|
|
179
|
-
Duration.seconds(90).formatted(
|
|
180
|
-
.units(allowed: [.minutes, .seconds], width: .narrow)
|
|
181
|
-
) // "1m 30s"
|
|
182
|
-
|
|
183
|
-
// Limit unit count
|
|
184
|
-
Duration.seconds(3661).formatted(
|
|
185
|
-
.units(allowed: [.hours, .minutes, .seconds], width: .abbreviated, maximumUnitCount: 2)
|
|
186
|
-
) // "1 hr, 1 min"
|
|
194
|
+
let poolWater = Measurement<UnitTemperature>(value: 81, unit: .fahrenheit)
|
|
195
|
+
poolWater.formatted(.measurement(width: .wide)) // "81 degrees Fahrenheit"
|
|
196
|
+
poolWater.formatted(.measurement(width: .abbreviated)) // "81°F"
|
|
197
|
+
poolWater.formatted(.measurement(width: .narrow)) // "81°"
|
|
187
198
|
```
|
|
188
199
|
|
|
189
|
-
|
|
200
|
+
`usage:` lets the style convert to what the locale expects for that context:
|
|
190
201
|
|
|
191
202
|
```swift
|
|
192
|
-
let
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
temp.formatted(.measurement(width: .narrow)) // "72°"
|
|
196
|
-
|
|
197
|
-
let dist = Measurement(value: 5, unit: UnitLength.kilometers)
|
|
198
|
-
dist.formatted(.measurement(width: .abbreviated, usage: .road)) // "3.1 mi" (en_US)
|
|
203
|
+
let hike = Measurement<UnitLength>(value: 8, unit: .kilometers)
|
|
204
|
+
let roadSign = Measurement<UnitLength>.FormatStyle(width: .abbreviated, usage: .road)
|
|
205
|
+
hike.formatted(roadSign) // "5 mi" in en_US, "8 km" in de_DE
|
|
199
206
|
```
|
|
200
207
|
|
|
201
|
-
|
|
208
|
+
The type is `Measurement<Unit>.FormatStyle`.
|
|
209
|
+
|
|
210
|
+
## Person names
|
|
202
211
|
|
|
203
|
-
|
|
212
|
+
`PersonNameComponents` holds `givenName`, `familyName`, `middleName`,
|
|
213
|
+
`namePrefix`, `nickname` and `nameSuffix`.
|
|
204
214
|
|
|
205
215
|
```swift
|
|
206
|
-
var
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
name.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
name.formatted(.name(style: .short)) // "Tom"
|
|
217
|
-
name.formatted(.name(style: .abbreviated)) // "TC"
|
|
216
|
+
var person = PersonNameComponents()
|
|
217
|
+
person.namePrefix = "Dr."
|
|
218
|
+
person.givenName = "Ada"
|
|
219
|
+
person.familyName = "Lovelace"
|
|
220
|
+
person.nickname = "Addie"
|
|
221
|
+
|
|
222
|
+
person.formatted(.name(style: .long)) // "Dr. Ada Lovelace"
|
|
223
|
+
person.formatted(.name(style: .medium)) // "Ada Lovelace"
|
|
224
|
+
person.formatted(.name(style: .short)) // "Addie"
|
|
225
|
+
person.formatted(.name(style: .abbreviated)) // "AL"
|
|
218
226
|
```
|
|
219
227
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
Docs: [PersonNameComponents.FormatStyle](https://sosumi.ai/documentation/foundation/personnamecomponents/formatstyle)
|
|
228
|
+
The final order and form depend, in priority order, on the script of the name,
|
|
229
|
+
the user's preferences, the locale, and only then the style you asked for.
|
|
223
230
|
|
|
224
231
|
## Lists
|
|
225
232
|
|
|
226
233
|
```swift
|
|
227
|
-
["
|
|
228
|
-
// "
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
// "Alice, Bob, or Charlie"
|
|
234
|
+
let crew = ["Mina", "Omar", "Lea"]
|
|
235
|
+
crew.formatted(.list(type: .and)) // "Mina, Omar, and Lea"
|
|
236
|
+
crew.formatted(.list(type: .or)) // "Mina, Omar, or Lea"
|
|
237
|
+
crew.formatted(.list(type: .and, width: .narrow)) // "Mina, Omar, Lea"
|
|
232
238
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
// "1, 2, and 3"
|
|
236
|
-
|
|
237
|
-
// Narrow width
|
|
238
|
-
["A", "B", "C"].formatted(.list(type: .and, width: .narrow))
|
|
239
|
-
// "A, B, C"
|
|
239
|
+
let lanes = [3, 5, 8]
|
|
240
|
+
lanes.formatted(.list(memberStyle: IntegerFormatStyle<Int>(), type: .and)) // "3, 5, and 8"
|
|
240
241
|
```
|
|
241
242
|
|
|
242
|
-
|
|
243
|
+
The type is `ListFormatStyle`.
|
|
243
244
|
|
|
244
|
-
## Byte
|
|
245
|
+
## Byte counts
|
|
245
246
|
|
|
246
247
|
```swift
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
let attachment: Int64 = 1_048_576
|
|
249
|
+
var labels: [String] = []
|
|
250
|
+
for style in [ByteCountFormatStyle.Style.memory, .file, .binary] {
|
|
251
|
+
labels.append(attachment.formatted(.byteCount(style: style))) // "1 MB" each time
|
|
252
|
+
}
|
|
250
253
|
```
|
|
251
254
|
|
|
252
|
-
|
|
255
|
+
`.memory` and `.binary` count in powers of 1024; `.file` and `.decimal` count
|
|
256
|
+
in powers of 1000. All four use the same unit labels ("KB", "MB"), never
|
|
257
|
+
"KiB" or "MiB", so a different base only shows once the value is large enough
|
|
258
|
+
for the rounding to differ.
|
|
253
259
|
|
|
254
260
|
## URLs
|
|
255
261
|
|
|
256
262
|
```swift
|
|
257
|
-
let
|
|
258
|
-
|
|
259
|
-
// "
|
|
263
|
+
let guide = URL(string: "https://help.example.org/guides/setup?ref=home")!
|
|
264
|
+
guide.formatted() // the full string
|
|
265
|
+
guide.formatted(.url.scheme(.never).host().path()) // "help.example.org/guides/setup"
|
|
266
|
+
guide.formatted(.url.scheme(.always).host().path(.never)) // "https://help.example.org"
|
|
267
|
+
guide.formatted(.url.scheme(.never).host(.never).path()) // "/guides/setup"
|
|
268
|
+
```
|
|
260
269
|
|
|
261
|
-
|
|
262
|
-
// "example.com/path"
|
|
270
|
+
(Unwrap the literal URL with `guard let` in real code.)
|
|
263
271
|
|
|
264
|
-
|
|
265
|
-
// "https:///path"
|
|
266
|
-
```
|
|
272
|
+
## SwiftUI integration
|
|
267
273
|
|
|
268
|
-
|
|
274
|
+
Pass the value and the style to `Text` instead of building a string first:
|
|
269
275
|
|
|
270
|
-
|
|
276
|
+
```swift
|
|
277
|
+
Text(order.total, format: .currency(code: order.currencyCode))
|
|
278
|
+
Text(order.placedAt, format: .dateTime.day().month().hour().minute())
|
|
279
|
+
Text(workout.elapsed, format: .units(allowed: [.hours, .minutes], width: .abbreviated))
|
|
280
|
+
```
|
|
271
281
|
|
|
272
|
-
`Text`
|
|
282
|
+
`Text` then formats with the view's `\.locale` environment value, so previews
|
|
283
|
+
and in-app locale overrides are respected, and the value stays typed until the
|
|
284
|
+
moment it is drawn. For live values:
|
|
273
285
|
|
|
274
286
|
```swift
|
|
275
|
-
//
|
|
276
|
-
Text(
|
|
277
|
-
Text(
|
|
278
|
-
Text(duration, format: .units(allowed: [.minutes, .seconds]))
|
|
279
|
-
|
|
280
|
-
// Timer-style (live updating)
|
|
281
|
-
Text(.now, style: .timer)
|
|
282
|
-
Text(.now, style: .relative)
|
|
283
|
-
Text(timerInterval: start...end)
|
|
287
|
+
Text(.now, style: .timer) // counts up from the date
|
|
288
|
+
Text(sessionStart, style: .relative) // "2 min"
|
|
289
|
+
Text(timerInterval: sessionStart...sessionEnd) // counts down across the range
|
|
284
290
|
```
|
|
285
291
|
|
|
286
|
-
|
|
287
|
-
re-render only the formatted value and supports accessibility scaling.
|
|
292
|
+
`Text(timerInterval:)` needs iOS 16.
|
|
288
293
|
|
|
289
294
|
## Custom FormatStyle
|
|
290
295
|
|
|
291
|
-
Conform to `FormatStyle`
|
|
292
|
-
|
|
296
|
+
Conform to `FormatStyle` and implement `format(_:)`. Add `ParseableFormatStyle`
|
|
297
|
+
if the text must also be read back. `FormatStyle` inherits `Codable` and
|
|
298
|
+
`Hashable`, so keep stored properties simple value types.
|
|
293
299
|
|
|
294
300
|
```swift
|
|
295
|
-
struct
|
|
296
|
-
func format(_
|
|
297
|
-
switch
|
|
298
|
-
case
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return String(format: "%.1fK", Double(value) / 1_000)
|
|
302
|
-
default:
|
|
303
|
-
return String(format: "%.1fM", Double(value) / 1_000_000)
|
|
301
|
+
struct CompactCountStyle: FormatStyle {
|
|
302
|
+
func format(_ count: Int) -> String {
|
|
303
|
+
switch abs(count) {
|
|
304
|
+
case 1_000_000...: scaled(count, by: 1_000_000) + "M"
|
|
305
|
+
case 1_000...: scaled(count, by: 1_000) + "K"
|
|
306
|
+
default: count.formatted()
|
|
304
307
|
}
|
|
305
308
|
}
|
|
309
|
+
|
|
310
|
+
private func scaled(_ count: Int, by divisor: Double) -> String {
|
|
311
|
+
(Double(count) / divisor).formatted(.number.precision(.fractionLength(0...1)))
|
|
312
|
+
}
|
|
306
313
|
}
|
|
307
314
|
|
|
308
|
-
extension FormatStyle where Self ==
|
|
309
|
-
static var
|
|
315
|
+
extension FormatStyle where Self == CompactCountStyle {
|
|
316
|
+
static var compactCount: CompactCountStyle { CompactCountStyle() }
|
|
310
317
|
}
|
|
311
318
|
|
|
312
|
-
//
|
|
313
|
-
let followers = 12_500
|
|
314
|
-
Text(followers, format: .abbreviatedCount) // "12.5K"
|
|
319
|
+
let followers = 48_700.formatted(.compactCount) // "48.7K"
|
|
315
320
|
```
|
|
316
321
|
|
|
317
|
-
|
|
322
|
+
The static member in the constrained extension is what enables the
|
|
323
|
+
`.compactCount` dot syntax at call sites. (For real apps, `.notation(.compactName)`
|
|
324
|
+
already does this with localized suffixes; a custom style earns its place when
|
|
325
|
+
the rule is your own.)
|
|
326
|
+
|
|
327
|
+
## Common mistakes
|
|
318
328
|
|
|
319
329
|
| Mistake | Fix |
|
|
320
|
-
|
|
321
|
-
|
|
|
322
|
-
| String interpolation
|
|
323
|
-
|
|
|
324
|
-
|
|
|
325
|
-
|
|
|
326
|
-
|
|
|
327
|
-
|
|
|
328
|
-
|
|
329
|
-
## Review
|
|
330
|
-
|
|
331
|
-
- [ ] `FormatStyle`
|
|
332
|
-
- [ ] `Text(_:format:)`
|
|
333
|
-
- [ ] No hardcoded locale unless
|
|
334
|
-
- [ ] Duration
|
|
335
|
-
- [ ]
|
|
336
|
-
- [ ] Measurement
|
|
337
|
-
- [ ] Custom
|
|
330
|
+
| --- | --- |
|
|
331
|
+
| `NumberFormatter` or `DateFormatter` in new code | `FormatStyle` (iOS 15+); Foundation caches the underlying formatters |
|
|
332
|
+
| String interpolation of a value inside `Text` | `Text(value, format:)` |
|
|
333
|
+
| Hardcoded `.locale(...)` for display | Leave it out so the user's locale applies |
|
|
334
|
+
| `.time(pattern:)` where labels are wanted | `.units(allowed:width:)` |
|
|
335
|
+
| A `Formatter` created in `body` or inside a loop | An inline style; it is cheap and cached |
|
|
336
|
+
| `DateComponentsFormatter` for a `Duration` | `Duration.TimeFormatStyle` or `Duration.UnitsFormatStyle` |
|
|
337
|
+
| Measurements formatted without `usage:` | Pass `.road`, `.person`, `.asProvided` or another usage that fits |
|
|
338
|
+
|
|
339
|
+
## Review checklist
|
|
340
|
+
|
|
341
|
+
- [ ] `FormatStyle` rather than `Formatter` subclasses when targeting iOS 15+
|
|
342
|
+
- [ ] `Text(_:format:)` rather than preformatted strings
|
|
343
|
+
- [ ] No hardcoded locale unless the output is machine-facing (for example, sent to a server)
|
|
344
|
+
- [ ] `Duration` values go through the time or units duration style
|
|
345
|
+
- [ ] Currencies are identified by ISO 4217 code ("EUR"), never by symbol ("€")
|
|
346
|
+
- [ ] Measurement display passes `usage:`
|
|
347
|
+
- [ ] Custom styles keep the `Codable` and `Hashable` conformance that `FormatStyle` requires working (value-type properties only)
|
|
348
|
+
- [ ] Output checked in `en_US`, `de_DE`, `ar_SA` and `ja_JP`
|
|
338
349
|
|
|
339
350
|
## References
|
|
340
351
|
|
|
341
|
-
-
|
|
352
|
+
- [FormatStyle](https://developer.apple.com/documentation/foundation/formatstyle)
|
|
353
|
+
- [IntegerFormatStyle](https://developer.apple.com/documentation/foundation/integerformatstyle)
|
|
354
|
+
- [FloatingPointFormatStyle](https://developer.apple.com/documentation/foundation/floatingpointformatstyle)
|
|
355
|
+
- [Date.FormatStyle](https://developer.apple.com/documentation/foundation/date/formatstyle)
|
|
356
|
+
- [Date.RelativeFormatStyle](https://developer.apple.com/documentation/foundation/date/relativeformatstyle)
|
|
357
|
+
- [Date.IntervalFormatStyle](https://developer.apple.com/documentation/foundation/date/intervalformatstyle)
|
|
358
|
+
- [Date.AnchoredRelativeFormatStyle](https://developer.apple.com/documentation/foundation/date/anchoredrelativeformatstyle)
|
|
359
|
+
- [Duration.TimeFormatStyle](https://developer.apple.com/documentation/swift/duration/timeformatstyle)
|
|
360
|
+
- [Duration.UnitsFormatStyle](https://developer.apple.com/documentation/swift/duration/unitsformatstyle)
|
|
361
|
+
- [Measurement.FormatStyle](https://developer.apple.com/documentation/foundation/measurement/formatstyle)
|
|
362
|
+
- [PersonNameComponents.FormatStyle](https://developer.apple.com/documentation/foundation/personnamecomponents/formatstyle)
|
|
363
|
+
- [ListFormatStyle](https://developer.apple.com/documentation/foundation/listformatstyle)
|
|
364
|
+
- [ByteCountFormatStyle](https://developer.apple.com/documentation/foundation/bytecountformatstyle)
|
|
365
|
+
- [URL.FormatStyle](https://developer.apple.com/documentation/foundation/url/formatstyle)
|