From 794010b644d2a7ad95010b68ecbe001a815dae6e Mon Sep 17 00:00:00 2001 From: brett ohland Date: Tue, 5 Jul 2022 10:27:39 -0600 Subject: [PATCH] The Xcode 14 Update - Adds Xcode 14 information (#4) - Adds URL style - Adds Duration style - Updates Byte count styles with new information - Updates Verbatim Date format style with new information - Updates measurement style to reference byte count format style - Splits site into individual pages to solve site crashing on mobie (#5) - Updates minimum requirements to reference Xcode 13 and 14 support - Adds version badges --- config.toml | 2 +- content/_includes/attributedStrings.md | 2 - content/_includes/bytes.md | 122 ++++++--- content/_includes/datetime.md | 46 ++-- content/_includes/duration.md | 283 ++++++++++++++++++++ content/_includes/measurement.md | 18 +- content/_includes/quiz.md | 205 ++++++++++---- content/_includes/requirements.md | 29 +- content/_includes/swiftui.md | 2 - content/_includes/url.md | 185 +++++++++++++ content/_includes/verbatimDate.md | 86 +++++- content/_index.md | 260 +----------------- content/attributed-string-output/_index.md | 16 ++ content/byte-count-style/_index.md | 34 +++ content/custom-styles/_index.md | 27 ++ content/date-range-styles/_index.md | 51 ++++ content/date-styles/_index.md | 93 +++++++ content/duration-styles/_index.md | 14 + content/list-style/_index.md | 31 +++ content/measurement-style/_index.md | 30 +++ content/numeric-styles/_index.md | 77 ++++++ content/person-name-style/_index.md | 31 +++ content/swiftui/_index.md | 21 ++ content/url-style/_index.md | 31 +++ data/menu/main.yaml | 79 +++--- layouts/_default/rss.xml | 2 +- layouts/shortcodes/xcode13-badge.html | 1 + layouts/shortcodes/xcode14-badge.html | 1 + sections/attributedStrings.md | 2 - sections/bytes.md | 120 ++++++--- sections/datetime.md | 46 ++-- sections/duration.md | 296 +++++++++++++++++++++ sections/measurement.md | 22 +- sections/quiz.md | 205 ++++++++++---- sections/requirements.md | 29 +- sections/swiftui.md | 2 - sections/url.md | 187 +++++++++++++ sections/verbatimDate.md | 87 +++++- static/custom.css | 52 ++++ 39 files changed, 2258 insertions(+), 569 deletions(-) create mode 100644 content/_includes/duration.md create mode 100644 content/_includes/url.md create mode 100644 content/attributed-string-output/_index.md create mode 100644 content/byte-count-style/_index.md create mode 100644 content/custom-styles/_index.md create mode 100644 content/date-range-styles/_index.md create mode 100644 content/date-styles/_index.md create mode 100644 content/duration-styles/_index.md create mode 100644 content/list-style/_index.md create mode 100644 content/measurement-style/_index.md create mode 100644 content/numeric-styles/_index.md create mode 100644 content/person-name-style/_index.md create mode 100644 content/swiftui/_index.md create mode 100644 content/url-style/_index.md create mode 100644 layouts/shortcodes/xcode13-badge.html create mode 100644 layouts/shortcodes/xcode14-badge.html create mode 100644 sections/duration.md create mode 100644 sections/url.md diff --git a/config.toml b/config.toml index 8f6e740..e332d1f 100644 --- a/config.toml +++ b/config.toml @@ -53,7 +53,7 @@ enableRobotsTXT = true # (Optional, default true) Show page navigation links at the bottom of each # docs page (bundle menu only). - # geekdocNextPrev = false + geekdocNextPrev = false # (Optional, default true) Show a breadcrumb navigation bar at the top of each docs page. # You can also specify this parameter per page in front matter. diff --git a/content/_includes/attributedStrings.md b/content/_includes/attributedStrings.md index 89bd3e6..5972221 100644 --- a/content/_includes/attributedStrings.md +++ b/content/_includes/attributedStrings.md @@ -1,8 +1,6 @@ --- sitemap_ignore: true --- -## Attributed String Output - {{< hint type=tip title=TL;DR >}} Some of the built-in styles will output `AttributedString` values instead. diff --git a/content/_includes/bytes.md b/content/_includes/bytes.md index 99930fb..9123422 100644 --- a/content/_includes/bytes.md +++ b/content/_includes/bytes.md @@ -1,9 +1,18 @@ --- sitemap_ignore: true --- + +{{< hint type=warning >}} + +**This style is broken in certain ways in Xcode 13**. Providing any unit above gigabyte will cause a crash with the message: "Fatal error: invalid Units value". + +Experiment for yourself to see if you can make this work for you, but you may want to fall back to the older `ByteCountFormatter` in this case. + +{{< /hint >}} + ### Available Options -The format style has four possible options: +Both format styles have the following options available: | Option | Description | | ------------------------- | ----------------------------------------------------- | @@ -33,66 +42,105 @@ The format style has four possible options: | `.zb` | As zetabytes (1,000,000,000,000,000,000,000 bytes) | | `.ybOrHigher` | As yottabytes (1,000,000,000,000,000,000,000,000 bytes) | -{{< hint type=warning >}} - -Currently, using any value above `.gb` will cause a crash with the message: "Fatal error: invalid Units value". (Feedback FB10031442 has been submitted to Apple.) +{{< hint type=important >}} -If you're looking to convert between two units, you can use the `Measurement` API and convert between `UnitInformationStorage` values. +You can provide a `Set` for the `units` parameter instead of an individual unit. Doing so will cause the style to use the unit that will provide the smallest number of digits. {{< /hint >}} -
let terabyte: Int64 = 1_000_000_000_000
+

 

+ +
Swift
+
+// MARK: - Int64
+
+let terabyte: Int64 = 1_000_000_000_000
 
-let formatter = ByteCountFormatter()
-formatter.countStyle = .binary
-formatter.allowedUnits
-formatter.includesActualByteCount
-formatter.countStyle = .file
+var integerByteCountStyle = ByteCountFormatStyle()
+integerByteCountStyle.style = .file
+integerByteCountStyle.allowedUnits = [.gb, .tb]
+integerByteCountStyle.includesActualByteCount = true
 
-terabyte.formatted(.byteCount(style: .binary))  // "931.32 GB"
+integerByteCountStyle.format(terabyte) // "1 TB (1,000,000,000,000 bytes)"
+terabyte.formatted(integerByteCountStyle) // "1 TB (1,000,000,000,000 bytes)"
+
+terabyte.formatted(.byteCount(style: .binary)) // "931.32 GB"
 terabyte.formatted(.byteCount(style: .decimal)) // "1 TB"
-terabyte.formatted(.byteCount(style: .file))    // "1 TB"
-terabyte.formatted(.byteCount(style: .memory))  // "931.32 GB"
+terabyte.formatted(.byteCount(style: .file)) // "1 TB"
+terabyte.formatted(.byteCount(style: .memory)) // "931.32 GB"
+
+terabyte.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
+terabyte.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
+terabyte.formatted(.byteCount(style: .file, allowedUnits: .kb)) // "976,562,500 kB"
+terabyte.formatted(.byteCount(style: .file, allowedUnits: .mb)) // "953,674.3 MB"
+terabyte.formatted(.byteCount(style: .file, allowedUnits: .gb)) // "931.32 GB"
+terabyte.formatted(.byteCount(style: .file, allowedUnits: .tb)) // "0.91 TB"
+terabyte.formatted(.byteCount(style: .file, allowedUnits: .pb)) // "0 PB"
+terabyte.formatted(.byteCount(style: .file, allowedUnits: .zb)) // "0 PB"
+terabyte.formatted(.byteCount(style: .file, allowedUnits: .ybOrHigher)) // 0 PB
+
+Int64(1_000).formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 kB"
+Int64(1_000_000).formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 MB"
+
+Int64.zero.formatted(.byteCount(style: .file, spellsOutZero: true)) // "Zero kB"
+Int64.zero.formatted(.byteCount(style: .file, spellsOutZero: false)) // "0 bytes"
+
+Int64(1_000).formatted(.byteCount(style: .file, includesActualByteCount: true)) // "1 kB (1,000 bytes)"
+
+// MARK: - Measurement
 
-terabyte.formatted(.byteCount(style: .memory, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
-terabyte.formatted(.byteCount(style: .memory, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
-terabyte.formatted(.byteCount(style: .memory, allowedUnits: .kb))    // "1,000,000,000 kB"
-terabyte.formatted(.byteCount(style: .memory, allowedUnits: .mb))    // "1,000,000 MB"
+let terabyteMeasurement = Measurement(value: 1, unit: UnitInformationStorage.terabytes)
 
-// .gb, .tb, .pb, .eb, .zb, and .ybOrHigher cause a FatalError (Feedback FB10031442)
-// terabyte.formatted(.byteCount(style: .file, allowedUnits: .gb))
+terabyteMeasurement.formatted(.byteCount(style: .binary)) // "931.32 GB"
+terabyteMeasurement.formatted(.byteCount(style: .decimal)) // "1 TB"
+terabyteMeasurement.formatted(.byteCount(style: .file)) // "1 TB"
+terabyteMeasurement.formatted(.byteCount(style: .memory)) // "931.32 GB"
 
-Int64(0).formatted(.byteCount(style: .file, allowedUnits: .mb, spellsOutZero: true))   // "Zero bytes"
-Int64(0).formatted(.byteCount(style: .file, allowedUnits: .mb, spellsOutZero: false))  // "0 MB"
+terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
+terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
+terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .kb)) // "976,562,500 kB"
+terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .mb)) // "953,674.3 MB"
+terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .gb)) // "931.32 GB"
+terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .tb)) // "0.91 TB"
+terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .pb)) // "0 PB"
+terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .zb)) // "0 PB"
+terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .ybOrHigher)) // 0 PB
 
-terabyte.formatted(.byteCount(style: .file, allowedUnits: .mb, includesActualByteCount: true))  // "1,000,000 MB (1,000,000,000,000 bytes)"
-terabyte.formatted(.byteCount(style: .file, allowedUnits: .mb, includesActualByteCount: false)) // "1,000,000 MB"
-terabyte.formatted(.byteCount(style: .file, allowedUnits: .all, spellsOutZero: true, includesActualByteCount: true)) // "1 TB (1,000,000,000,000 bytes)"
+let kilobyteMeasurement = Measurement(value: 1, unit: UnitInformationStorage.kilobytes) +let megabyteMeasurement = Measurement(value: 1, unit: UnitInformationStorage.megabytes) + +kilobyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 kB" +megabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 MB" + +let zeroMeasurement = Measurement(value: 0, unit: UnitInformationStorage.bytes) + +zeroMeasurement.formatted(.byteCount(style: .file, spellsOutZero: true)) // "Zero kB" +zeroMeasurement.formatted(.byteCount(style: .file, spellsOutZero: false)) // "0 bytes" + +megabyteMeasurement.formatted(.byteCount(style: .file, includesActualByteCount: true)) // "1 MB (1,000,000 bytes)"
### Customizing the locale You can set the locale by appending the `locale()` method onto the end of the format style. -
let franceLocale = Locale(identifier: "fr_FR")
+
Swift
+let franceLocale = Locale(identifier: "fr_FR")
 
 terabyte.formatted(.byteCount(style: .binary).locale(franceLocale)) // "931,32 Go"
 terabyte.formatted(.byteCount(style: .decimal).locale(franceLocale)) // "1To"
 terabyte.formatted(.byteCount(style: .file).locale(franceLocale)) // "1To"
 terabyte.formatted(.byteCount(style: .memory).locale(franceLocale)) // "931,32 Go"
 
-let inFrench = ByteCountFormatStyle(
-    style: .memory,
-    allowedUnits: .all,
-    spellsOutZero: false,
-    includesActualByteCount: true,
-    locale: Locale(identifier: "fr_FR")
-)
-
-inFrench.format(terabyte) // "931,32 Go (1 000 000 000 000 octets)"
-terabyte.formatted(inFrench) // "931,32 Go (1 000 000 000 000 octets)"
+terabyteMeasurement.formatted(.byteCount(style: .binary).locale(franceLocale)) // "931,32 Go" +terabyteMeasurement.formatted(.byteCount(style: .decimal).locale(franceLocale)) // "1To" +terabyteMeasurement.formatted(.byteCount(style: .file).locale(franceLocale)) // "1To" +terabyteMeasurement.formatted(.byteCount(style: .memory).locale(franceLocale)) // "931,32 Go"

Attributed String Output

You can output attributed strings by appending the `attributed` method onto the end of the format style. -
terabyte.formatted(.byteCount(style: .binary).attributed)
+
Swift
+terabyte.formatted(.byteCount(style: .binary).attributed)
+
+terabyteMeasurement.formatted(.byteCount(style: .binary).attributed)
diff --git a/content/_includes/datetime.md b/content/_includes/datetime.md index 669c186..0c6a540 100644 --- a/content/_includes/datetime.md +++ b/content/_includes/datetime.md @@ -82,11 +82,25 @@ The order of the symbols in the final string are controlled by the date's `Local {{< /hint >}} -### Customization +## Customization Each symbol has customization options. -#### Day +- [Day](#day) +- [Day of Year](#day-of-year) +- [Era](#era) +- [Hour](#hour) +- [Minute](#minute) +- [Month](#month) +- [Quarter](#quarter) +- [Second](#second) +- [Fractional Second](#fractional-second) +- [Time Zone](#time-zone) +- [Week](#week) +- [Weekday](#weekday) +- [Year](#year) + +### Day
twosday.formatted(.dateTime.day(.twoDigits)) // "22"
 twosday.formatted(.dateTime.day(.ordinalOfDayInMonth)) // "4"
@@ -100,7 +114,7 @@ twosday.formatted(Date<
 twosday.formatted(Date.FormatStyle().day(.julianModified())) // "2459633"
 twosday.formatted(Date.FormatStyle().day(.julianModified(minimumLength: 8))) // "02459633"
-#### Day of Year +### Day of Year
twosday.formatted(.dateTime.dayOfYear(.defaultDigits)) // "53"
 twosday.formatted(.dateTime.dayOfYear(.threeDigits)) // "053"
@@ -109,7 +123,7 @@ twosday.formatted(.formatted(Date.FormatStyle().dayOfYear(.defaultDigits)) // "53"
 twosday.formatted(Date.FormatStyle().dayOfYear(.threeDigits)) // "053"
 twosday.formatted(Date.FormatStyle().dayOfYear(.twoDigits)) // "53"
-#### Era +### Era
twosday.formatted(.dateTime.era(.abbreviated)) // "AD"
 twosday.formatted(.dateTime.era(.narrow)) // "A"
@@ -119,7 +133,7 @@ twosday.formatted(Date<
 twosday.formatted(Date.FormatStyle().era(.narrow)) // "A"
 twosday.formatted(Date.FormatStyle().era(.wide)) // "Anno Domini"
-#### Hour +### Hour Each of the following methods accepts an `AMPMStyle`. @@ -172,7 +186,7 @@ twosday.formatted(Date< twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .abbreviated))) // "02 AM" twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .omitted))) // "02" -#### Minute +### Minute
twosday.formatted(.dateTime.minute(.twoDigits)) // "22"
 twosday.formatted(.dateTime.minute(.defaultDigits)) // "22"
@@ -180,7 +194,7 @@ twosday.formatted(.formatted(Date.FormatStyle().minute(.twoDigits)) // "22"
 twosday.formatted(Date.FormatStyle().minute(.defaultDigits)) // "22"
-#### Month +### Month
twosday.formatted(.dateTime.month(.defaultDigits)) // "2"
 twosday.formatted(.dateTime.month(.twoDigits)) // "02"
@@ -194,7 +208,7 @@ twosday.formatted(Date<
 twosday.formatted(Date.FormatStyle().month(.abbreviated)) // "Feb"
 twosday.formatted(Date.FormatStyle().month(.narrow)) // "F"
-#### Quarter +### Quarter
twosday.formatted(.dateTime.quarter(.narrow)) // "1"
 twosday.formatted(.dateTime.quarter(.abbreviated)) // "Q1"
@@ -208,7 +222,7 @@ twosday.formatted(Date<
 twosday.formatted(Date.FormatStyle().quarter(.twoDigits)) // "01"
 twosday.formatted(Date.FormatStyle().quarter(.oneDigit)) // "1"
-#### Second +### Second
twosday.formatted(.dateTime.second(.twoDigits)) // "22"
 twosday.formatted(.dateTime.second(.defaultDigits)) // "22"
@@ -216,7 +230,7 @@ twosday.formatted(.formatted(Date.FormatStyle().second(.twoDigits)) // "22"
 twosday.formatted(Date.FormatStyle().second(.defaultDigits)) // "22"
-#### Fractional Second +### Fractional Second
twosday.formatted(Date.FormatStyle().secondFraction(.fractional(2))) // "00"
 twosday.formatted(Date.FormatStyle().secondFraction(.milliseconds(1))) // "8542000"
@@ -224,7 +238,7 @@ twosday.formatted(Date<
 twosday.formatted(.dateTime.secondFraction(.fractional(2))) // "00"
 twosday.formatted(.dateTime.secondFraction(.milliseconds(1))) // "8542000"
-#### Time Zone +### Time Zone
twosday.formatted(.dateTime.timeZone(.exemplarLocation)) // "Edmonton"
 twosday.formatted(.dateTime.timeZone(.genericLocation)) // "Edmonton Time"
@@ -252,7 +266,7 @@ twosday.formatted(Date<
 twosday.formatted(Date.FormatStyle().timeZone(.localizedGMT(.short))) // "GMT-7"
 twosday.formatted(Date.FormatStyle().timeZone(.localizedGMT(.long))) // "GMT-07:00"
-#### Week +### Week
twosday.formatted(.dateTime.week(.defaultDigits)) // "9"
 twosday.formatted(.dateTime.week(.twoDigits)) // "09"
@@ -262,7 +276,7 @@ twosday.formatted(Date<
 twosday.formatted(Date.FormatStyle().week(.twoDigits)) // "09"
 twosday.formatted(Date.FormatStyle().week(.weekOfMonth)) // "9"
-#### Weekday +### Weekday
twosday.formatted(.dateTime.weekday(.abbreviated)) // "Tue"
 twosday.formatted(.dateTime.weekday(.twoDigits)) // "3"
@@ -278,7 +292,7 @@ twosday.formatted(Date<
 twosday.formatted(Date.FormatStyle().weekday(.wide)) // "Tuesday"
 twosday.formatted(Date.FormatStyle().weekday(.narrow)) // "T"
-#### Year +### Year
twosday.formatted(.dateTime.year(.twoDigits)) // "22"
 twosday.formatted(.dateTime.year(.defaultDigits)) // "2022"
@@ -296,7 +310,7 @@ twosday.formatted(Date<
 twosday.formatted(Date.FormatStyle().year(.relatedGregorian())) // "2022"
 twosday.formatted(Date.FormatStyle().year(.relatedGregorian(minimumLength: 2))) // "22"
-### Setting the Locale +## Setting the Locale You can set the Locale by appending the `.locale()` method onto the last Symbol. @@ -310,7 +324,7 @@ You can output an `AttributedString` by appending the `attributed` method onto t
twosday.formatted(.dateTime.attributed)
 twosday.formatted(Date.FormatStyle().attributed)
-### Parsing Dates From Strings +## Parsing Dates From Strings `Date.FormatStyle` conforms to `ParseableFormatStyle` and can be set up to parse `Date` objects from a `String`. diff --git a/content/_includes/duration.md b/content/_includes/duration.md new file mode 100644 index 0000000..ccec1ee --- /dev/null +++ b/content/_includes/duration.md @@ -0,0 +1,283 @@ +--- +sitemap_ignore: true +--- + +There are two format styles available for the `Duration` type: + +

Time Style {{< xcode14-badge >}}

+ +Time format style allows you to output your `Duration` as a combination of hours, minutes, and seconds, which is set by the `pattern` parameter on the style. + +You can either initialize a new instance of `Duration.TimeFormatStyle`, or use the `.time(pattern:)` extension on FormatStyle. + +
Swift
+Duration.seconds(1_000).formatted() // "0:16:40"
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinute)) // "0:17"
+Duration.TimeFormatStyle(pattern: .hourMinute).format(Duration.seconds(1_000)) // "0:17"
+ +{{< hint type=note >}} + +You'll notice that this is the default style when calling `.formatted()` on any `Duration` without specifying a style. + +{{< /hint >}} + +### Available Patterns + +There are six total patterns available, broken out into two types: properties and methods. + +The properties are the "defaults" for each type of pattern and will choose the configuration for you. + +| Pattern | Description | +| ----------------- | --------------------------------------------------------------- | +| `.hourMinute` | Displays the hour and minute values for the Duration | +| `.hourMinuteSecond` | Displays the hour, minute, and second values for the Duration | +| `.minuteSecond` | Displays the minute and second values for the Duration | + +
Swift
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinute)) // "0:17"
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinuteSecond)) // "0:16:40"
+Duration.seconds(1_000).formatted(.time(pattern: .minuteSecond)) // "16:40"
+ +The method variants allow you to fully customize the behaviour of each of the patterns: + +| Pattern | Description | +| --------------------- | ------------------------------------------------------------- | +| `.hourMinute()` | Displays the hour and minute values for the Duration | +| `.hourMinuteSecond()` | Displays the hour, minute, and second values for the Duration | +| `.minuteSecond()` | Displays the minute and second values for the Duration | + +The following are the parameter options for `hourMinute`: + +| Parameter | Description | +| ----------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `padHourToLength` | Pads the output to include that number of digits. | +| `roundSeconds` | The rounding rule to use on the seconds value ([See Rounding for all options](#numbers-rounding)). Defaults to `.toNearestOrEven` | + +
Swift
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 3, roundSeconds: .awayFromZero))) // "000:17"
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .down))) // "000:16"
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .toNearestOrAwayFromZero))) // "0:17"
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .toNearestOrEven))) // "0:17"
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .towardZero))) // "0:16"
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .up))) // "0:17"
+The following are the parameter options for both `hourMinuteSecond` and 'minuteSecond`: + +| Parameter | Description | +| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `padHourToLength` | Pads the output to include that number of digits. | +| `fractionalSecondsLength` | The number of digits to include when displaying fractional seconds. | +| `roundFractionalSeconds` | The rounding rule to use on the seconds value ([See Rounding for all options](#numbers-rounding)). Defaults to `.toNearestOrEven` | + +
Swift
+Duration.seconds(1_000).formatted(
+    .time(
+        pattern: .hourMinuteSecond(
+            padHourToLength: 3,
+            fractionalSecondsLength: 3,
+            roundFractionalSeconds: .awayFromZero
+        )
+    )
+) // "000:16:40.000"
+
+Duration.seconds(1_000).formatted(
+    .time(
+        pattern: .minuteSecond(
+            padMinuteToLength: 3,
+            fractionalSecondsLength: 3,
+            roundFractionalSeconds: .awayFromZero
+        )
+    )
+) // "016:40.000"
+ +### Setting the Locale + +You can set the `Locale` of the style by adding the `.locale()` method to your `.time(pattern:)` method, or including the locale parameter in the `Duration.TimeFormatStyle` initializer. + +
Swift
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinute).locale(Locale(identifier: "fr_FR"))) // "0:17"
+
+let frenchTimeFormatStyle = Duration.TimeFormatStyle(pattern: .minuteSecond, locale: Locale(identifier: "fr_FR"))
+
+frenchTimeFormatStyle.format(Duration.seconds(1_000)) // "16:40"
+ +### Attributed String Output + +You can output `AttributedString` values by adding the `.attributed` method. + +
Swift
+Duration.seconds(1_000).formatted(.time(pattern: .hourMinuteSecond).attributed)
+ +

Unit Style {{< xcode14-badge >}}

+ +The units style allows you to declare and customize the specific units to display for your duration. + +You can either initialize a new instance of `Duration.UnitsFormatStyle`, or use the `.units()` extension on FormatStyle. + +
Swift
+Duration.seconds(100).formatted(.units()) // "1 min, 40 sec"
+Duration.UnitsFormatStyle(allowedUnits: [.hours, .minutes, .seconds], width: .abbreviated).format(.seconds(100)) // "1 min, 40 sec"
+ +In both cases, there are two variants the initializer or style method. + +| Parameter | Description | +| ------------------ | ----------------------------------------------------------- | +| `.allowed` | A Set stating the possible units to display. | +| `width` | The "width" of the output string. | +| `maximumUnitCount` | The maximum number of time units to display. | +| `zeroValueUnits` | How to handle units with zero values. | +| `valueLength` | How to pad or truncate values for display. | +| `fractionalPart` | How to display fractional values if the unit can't be shown | + +| Parameter | Description | +| ------------------- | ----------------------------------------------------------- | +| `.allowed` | A Set stating the possible units to display. | +| `width` | The "width" of the output string. | +| `maximumUnitCount` | The maximum number of time units to display. | +| `zeroValueUnits` | How to handle units with zero values. | +| `valueLengthLimits` | How to pad or truncate values for display. | +| `fractionalPart` | How to display fractional values if the unit can't be shown | + +If you can't spot it at a glance, the difference is the `valueLength`/`valueLengthLimits` parameter which configures how each unit is padded or truncated. `valueLength` accepts an optional `Integer`, while `valueLengthLimits` takes an optional `ValueRange` value. + +--- + +#### allowed + +This parameter, passed in as a `Set< Duration.UnitsFormatStyle.Unit>`, declares which units to use in the final display. By default, if a unit has a value of "0", then it will be omitted from the final string. You can override this functionality with the `zeroValueUnits` parameter. + +The following units are available (from smallest to largest): + +- `.nanoseconds` +- `.microseconds` +- `.miliseconds` +- `.seconds` +- `.minutes` +- `.hours` +- `.days` +- `.weeks` + +
Swift
+Duration.milliseconds(500).formatted(.units(allowed: [.nanoseconds])) // "500,000,000 ns"
+Duration.milliseconds(500).formatted(.units(allowed: [.microseconds])) // "500,000 μs"
+Duration.milliseconds(500).formatted(.units(allowed: [.milliseconds])) // "500 ms"
+Duration.milliseconds(500).formatted(.units(allowed: [.seconds])) // "0 sec"
+Duration.milliseconds(500).formatted(.units(allowed: [.minutes])) // "0 min"
+Duration.milliseconds(500).formatted(.units(allowed: [.hours])) // "0 hr"
+Duration.milliseconds(500).formatted(.units(allowed: [.days])) // "0 days"
+Duration.milliseconds(500).formatted(.units(allowed: [.weeks])) // "0 wks"
+
+Duration.seconds(1_000_000.00123).formatted(
+    .units(
+        allowed: [
+            .nanoseconds,
+            .milliseconds,
+            .milliseconds,
+            .seconds,
+            .minutes,
+            .hours,
+            .days,
+            .weeks
+        ]
+    )
+) // "1 wk, 4 days, 13 hr, 46 min, 40 sec, 1 ms, 230,000 ns"
+
+Duration.seconds(1).formatted(
+    .units(
+        allowed: [
+            .nanoseconds,
+            .milliseconds,
+            .milliseconds,
+            .seconds,
+            .minutes,
+            .hours,
+            .days,
+            .weeks
+        ]
+    )
+) // "1 sec"
+ +--- + +#### width + +This parameter controls how verbose/wordy and condensed the string output is. + +| Parameter | Description | +| ----------------------- | ----------------------------------------------------------------------------- | +| `.wide` | Shows the full unit name, eg. "3 hours" | +| `.abbreviated` | Shows the shortened version of the unit "3 hrs" | +| `.condensedAbbreviated` | Shows the shortened version, without a space between the value and unit "3hr" | +| `.narrow` | Shows the shortest possible unit name | + + +
Swift
+Duration.seconds(100).formatted(.units(width: .abbreviated)) // "1 min, 40 sec"
+Duration.seconds(100).formatted(.units(width: .condensedAbbreviated)) // "1 min,40 sec"
+Duration.seconds(100).formatted(.units(width: .narrow)) // "1m 40s"
+Duration.seconds(100).formatted(.units(width: .wide)) // "1 minute, 40 seconds"
+ +--- + +#### maximumUnitCount + +Controls the number of units to display in the final string. This works in tandem with the `.units` property. + +
Swift
+Duration.seconds(10000).formatted(.units(maximumUnitCount: 1)) // "3 hr"
+Duration.seconds(10000).formatted(.units(maximumUnitCount: 2)) // "2 hr, 47 min"
+Duration.seconds(10000).formatted(.units(maximumUnitCount: 3)) // "2 hr, 46 min, 40 sec"
+ +--- + +#### zeroValueUnits + +Controls how units with a value of zero are shown or not, and if set to `.show`, how many zeros to use in their display. + +
Swift
+Duration.seconds(100).formatted(.units(zeroValueUnits: .hide)) // "1 min, 40 sec"
+Duration.seconds(100).formatted(.units(zeroValueUnits: .show(length: 1))) // "0 hr, 1 min, 40 sec"
+Duration.seconds(100).formatted(.units(zeroValueUnits: .show(length: 3))) // "000 hr, 001 min, 040 sec"
+ +--- + +#### valueLength and valueLengthLimits + +Controls how many digits of each unit to display. `valueLength` accepts a fixed integer, while `valueLengthLimits` accepts a range. + +
Swift
+Duration.seconds(1_000).formatted(.units(valueLength: 1)) // "16 min, 40 sec"
+Duration.seconds(1_000).formatted(.units(valueLength: 3)) // "016 min, 040 sec"
+
+Duration.seconds(10_000).formatted(.units(valueLengthLimits: 1...)) // This is a bug (Feedback FB10607619)
+Duration.seconds(10_000).formatted(.units(valueLengthLimits: ...3)) // "2 hr, 46 min, 40 sec"
+Duration.seconds(100).formatted(.units(valueLengthLimits: 2 ... 3)) // "01 min, 40 sec"
+ +{{< hint type=warning >}} +As of Xcode 14.0 beta 3 (14A5270f), there's a bug when you use a `ValueRange` of `1...`. In the above example, the output is "16.0 measure-unit/duration-minute, 40.0 measure-unit/duration-second” instead of “16 min, 40 sec”. Feedback FB10607619 has been submitted. +{{< /hint >}} + +--- + +#### fractionalPart + +Controls how fractional values are handled for display. + +
Swift
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide)) // "10 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .up))) // "11 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .towardZero))) // "10 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .toNearestOrEven))) // "10 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .toNearestOrAwayFromZero))) // "10 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .down))) // "10 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .awayFromZero))) // "11 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 0))) // "10 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 5))) // "10.00230 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .up))) // "10.003 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .towardZero))) // "10.002 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .toNearestOrEven))) // "10.002 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .toNearestOrAwayFromZero))) // "10.002 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .down))) // "10.002 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .awayFromZero))) // "10.003 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, increment: 1))) // "10.000 sec"
+Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, increment: 0.001))) // "10.002 sec"
+ diff --git a/content/_includes/measurement.md b/content/_includes/measurement.md index 17e31b7..65a2e9e 100644 --- a/content/_includes/measurement.md +++ b/content/_includes/measurement.md @@ -1,6 +1,7 @@ --- sitemap_ignore: true --- + [If you're curious, here's Apple's list of supported units.](https://developer.apple.com/documentation/foundation/dimension) Regardless of which unit you're using, the format style has three possible widths: @@ -27,16 +28,6 @@ gForce.formatted(.measu gForce.formatted(.measurement(width: .narrow).locale(franceLocale)) // "1G" gForce.formatted(.measurement(width: .abbreviated).locale(franceLocale)) // "1 force g") -### Setting the locale - -You can set the locale by appending the `locale()` method onto the end of the format style. - -
let franceLocale = Locale(identifier: "fr_FR")
-
-gForce.formatted(.measurement(width: .wide).locale(franceLocale))        // "1 fois l’accélération de pesanteur terrestre"
-gForce.formatted(.measurement(width: .narrow).locale(franceLocale))      // "1G"
-gForce.formatted(.measurement(width: .abbreviated).locale(franceLocale)) // "1 force g"
- ### Initializing a Measurement Style Due to the associated types required by the Measurement API, initializing a measurement style requires you to set the associated type. @@ -55,3 +46,10 @@ gForce.formatted(inFrench) gForce.formatted(.measurement(width: .wide).attributed) + +{{< hint type=note >}} +In Xcode 14, there is the `Measurement.FormatStyle.ByteCount` format style available to you that lets you easily format byte counts + +See the byte count format style section for all of the details + +{{< /hint >}} diff --git a/content/_includes/quiz.md b/content/_includes/quiz.md index 567da3c..3fc4ab8 100644 --- a/content/_includes/quiz.md +++ b/content/_includes/quiz.md @@ -11,30 +11,38 @@ If you have a specific need and aren't quite sure how to execute it, follow alon {{< expand "Number" "⇅" >}} +{{< xcode13-badge >}} + All built-in number types are supported by these format styles (`Float`, `Double`, `Int`, and `Decimal`) {{< expand "And I just want to customize the output" "⇅" >}} -See Number Style +{{< xcode13-badge >}} + +See Number Style | Property | Description | | ----------------------------------------------------------- | ------------------------------------------------------------- | -| [Rounding](#numbers-rounding) | Customize the rounding behaviour | -| [Sign](#numbers-sign) | Do you want to show or hide the + or - sign? | -| [Decimal Separator](#numbers-decimal-separator) | Do you want to show or hide the decimal separator | -| [Grouping](#numbers-grouping) | How do you want the thousands numbers to be grouped | -| [Precision](#numbers-prescision) | How many fractional or significant digits do you want to show | -| [Notation](#numbers-notation) | Enable scientific or compact notation | -| [Scale](#numbers-scale) | Scale the number up or down before display | -| [Locale](#numbers-locale) | Set the `Locale` for one output | -| [Compositing](#numbers-compositing) | Mix and match any and all of the above | -| [AttributedString output](#numbers-attributed-string-output) | Output an `AttributedString` | +| [Rounding](/number-styles/#numbers-rounding) | Customize the rounding behaviour | +| [Sign](/number-styles/#numbers-sign) | Do you want to show or hide the + or - sign? | +| [Decimal Separator](/number-styles/#numbers-decimal-separator) | Do you want to show or hide the decimal separator | +| [Grouping](/number-styles/#numbers-grouping) | How do you want the thousands numbers to be grouped | +| [Precision](/number-styles/#numbers-prescision) | How many fractional or significant digits do you want to show | +| [Notation](/number-styles/#numbers-notation) | Enable scientific or compact notation | +| [Scale](/number-styles/#numbers-scale) | Scale the number up or down before display | +| [Locale](/number-styles/#numbers-locale) | Set the `Locale` for one output | +| [Compositing](/number-styles/#numbers-compositing) | Mix and match any and all of the above | +| [AttributedString output](/number-styles/#numbers-attributed-string-output) | Output an `AttributedString` | {{< /expand >}} {{< expand "And it's actually a Percentage" "⇅" >}} -See Percent Style +{{< xcode13-badge >}} + +See Percent Style + +{{< xcode13-badge >}} {{< hint type=important >}} When formatting a floating point number (Double/Float/Decimal), `1.0` is "100%", while formatting the integer `1` is only "1%" @@ -42,16 +50,16 @@ When formatting a floating point number (Double/Float/Decimal), `1.0` is "100%", | Property | Description | | ------------------------------------------------------------ | ------------------------------------------------------------- | -| [Rounding](#percent-rounding) | Customize the rounding behaviour | -| [Sign](#percent-sign) | Do you want to show or hide the + or - sign? | -| [Decimal Separator](#percent-decimal-separator) | Do you want to show or hide the decimal separator | -| [Grouping](#percent-grouping) | How do you want the thousands numbers to be grouped | -| [Precision](#percent-prescision) | How many fractional or significant digits do you want to show | -| [Notation](#percent-notation) | Enable scientific or compact notation | -| [Scale](#percent-scale) | Scale the number up or down before display | -| [Locale](#percent-locale) | Set the `Locale` for one output | -| [Compositing](#percent-compositing) | Mix and match any and all of the above | -| [AttributedString output](#percent-attributed-string-output) | Output an `AttributedString` | +| [Rounding](/number-styles/#percent-rounding) | Customize the rounding behaviour | +| [Sign](/number-styles/#percent-sign) | Do you want to show or hide the + or - sign? | +| [Decimal Separator](/number-styles/#percent-decimal-separator) | Do you want to show or hide the decimal separator | +| [Grouping](/number-styles/#percent-grouping) | How do you want the thousands numbers to be grouped | +| [Precision](/number-styles/#percent-prescision) | How many fractional or significant digits do you want to show | +| [Notation](/number-styles/#percent-notation) | Enable scientific or compact notation | +| [Scale](/number-styles/#percent-scale) | Scale the number up or down before display | +| [Locale](/number-styles/#percent-locale) | Set the `Locale` for one output | +| [Compositing](/number-styles/#percent-compositing) | Mix and match any and all of the above | +| [AttributedString output](/number-styles/#percent-attributed-string-output) | Output an `AttributedString` | {{< hint type=important >}} The various percent formatters handle integers and floating point values differently. @@ -65,7 +73,11 @@ The various percent formatters handle integers and floating point values differe {{< expand "And it's actually Money" "⇅" >}} -See Currency Style +{{< xcode13-badge >}} + +See Currency Style + +{{< xcode13-badge >}} Currency requires you to set the ISO 4217 code for the target currency to output. @@ -77,16 +89,16 @@ Floating point types shouldn't be used to store or do calculations on numbers th | Property | Description | | ------------------------------------------------------------- | ------------------------------------------------------------- | -| [Rounding](#currency-rounding) | Customize the rounding behaviour | -| [Sign](#currency-sign) | Do you want to show or hide the + or - sign? | -| [Decimal Separator](#currency-decimal-separator) | Do you want to show or hide the decimal separator | -| [Grouping](#currency-grouping) | How do you want the thousands numbers to be grouped | -| [Precision](#currency-prescision) | How many fractional or significant digits do you want to show | -| [Presentation](#currency-presentation) | Controls the style of the displayed currency | -| [Scale](#currency-scale) | Scale the number up or down before display | -| [Locale](#currency-locale) | Set the `Locale` for one output | -| [Compositing](#currency-compositing) | Mix and match any and all of the above | -| [AttributedString output](#currency-attributed-string-output) | Output an `AttributedString` | +| [Rounding](/number-styles/#currency-rounding) | Customize the rounding behaviour | +| [Sign](/number-styles/#currency-sign) | Do you want to show or hide the + or - sign? | +| [Decimal Separator](/number-styles/#currency-decimal-separator) | Do you want to show or hide the decimal separator | +| [Grouping](/number-styles/#currency-grouping) | How do you want the thousands numbers to be grouped | +| [Precision](/number-styles/#currency-prescision) | How many fractional or significant digits do you want to show | +| [Presentation](/number-styles/#currency-presentation) | Controls the style of the displayed currency | +| [Scale](/number-styles/#currency-scale) | Scale the number up or down before display | +| [Locale](/number-styles/#currency-locale) | Set the `Locale` for one output | +| [Compositing](/number-styles/#currency-compositing) | Mix and match any and all of the above | +| [AttributedString output](/number-styles/#currency-attributed-string-output) | Output an `AttributedString` | {{< /expand >}} @@ -94,45 +106,57 @@ Floating point types shouldn't be used to store or do calculations on numbers th {{< expand "Single Date" "⇅" >}} +{{< xcode13-badge >}} {{< xcode14-badge >}} + {{< expand "Just the full date and/or time" "⇅" >}} +{{< xcode13-badge >}} + Apple added a date extension onto `Date` that lets you output the date and/or time in certain formats. -See Date and Time Style +See Date and Time Style {{< /expand >}} {{< expand "I want to mix and match date components" "⇅" >}} +{{< xcode13-badge >}} + The `dateTime()` format style lets you compose a date string by choosing which date components you'd like to include. Each component can be further customized passing in some options. -See composting using .dateTime +See composting using .dateTime {{< /expand >}} {{< expand "I want a standard ISO8601" "⇅" >}} +{{< xcode13-badge >}} + Output date strings that conform to the ISO 8601 standard. -See ISO8601 Style +See ISO8601 Style {{< /expand >}} {{< expand "Exactly how far away it is" "⇅" >}} +{{< xcode13-badge >}} + Relative date formatting outputs a plain language string that describes how far away that date is to right now. You can also customize which date components are used in the output. This is similar to the components format style on date ranges, but that one uses date ranges instead of assuming the current date and time. -See Relative Style +See Relative Style {{< /expand >}} -{{< expand "An un-localized date in a very specific format" "⇅" >}} +{{< expand "An date in a very specific format" "⇅" >}} -If you need to output a very rigid date string that follows an exact format, you can use the verbatim format style to do that. +{{< xcode13-badge >}} {{< xcode14-badge >}} -See Verbatim Style +If you need to output a very rigid date string that follows an exact format, you can use the verbatim format style to do that. Updated in Xcode 14. + +See Verbatim Style {{< /expand >}} @@ -140,19 +164,23 @@ See Verbatim Style {{< expand "Range of Dates" "⇅" >}} +{{< xcode13-badge >}} + {{< expand "Just show the earliest and latest dates" "⇅" >}} The interval formatter just shows the earliest and latest dates in a range. -See Interval Style +See Interval Style {{< /expand >}} {{< expand "How long as passed between the earliest and latest dates" "⇅" >}} +{{< xcode13-badge >}} + Use the components format style if you want a plain language representation of the distance between the earliest and latest dates in a date range. This is similar to the relative date format style (but for date ranges). -See Components Style +See Components Style {{< /expand >}} @@ -160,41 +188,92 @@ See Components Style {{< expand "An Array/List" "⇅" >}} +{{< xcode13-badge >}} + The list format style lets you take an array of data objects, and output them as a list on screen. You can also customize how each object is formatted within the list. -See List style +See List style {{< /expand >}} -{{< expand "Some Measurement" "⇅" >}} +{{< expand "Measurement" "⇅" >}} + +{{< xcode13-badge >}} {{< xcode14-badge >}} -Any unit that's supported by the `Measurement` API can be formatted with many different customization options. +Any unit that's supported by the `Measurement` API can be formatted with many different customization options. Updated in Xcode 14. -See Measurement Style +See Measurement Style {{< /expand >}} {{< expand "Person's Name" "⇅" >}} +{{< xcode13-badge >}} + A person's name is tricky to localize correctly, the PersonNameComponents format style can handle the complexities of localization for you. -See Person Name Components +See Person Name Components {{< /expand >}} {{< expand "A number of bytes" "⇅" >}} +{{< xcode13-badge >}} + Easily display how many gigabytes that byte count is. -See Byte Count Style +See Byte Count Style + +{{< /expand >}} + +{{< expand "Duration" "⇅" >}} + +{{< xcode14-badge >}} + +Easily format the `Duration` type. + +See Duration Style + +{{< expand "Time Style" "⇅" >}} + +{{< xcode14-badge >}} + +Shows the `Duration` represented in a combination of hours, minutes, and seconds. + +See Duration Style + +{{< /expand >}} + +{{< expand "Units Style" "⇅" >}} + +{{< xcode14-badge >}} + +Shows the `Duration` using a specific set of units. + +See Duration Style + +{{< /expand >}} + +{{< /expand >}} + + +{{< expand "URL" "⇅" >}} + +{{< xcode14-badge >}} + +Format your Universal Resource Locator. + +See URL Style {{< /expand >}} {{< expand "Need for something custom" "⇅" >}} +{{< xcode13-badge >}} + You can easily bend the `FormatStyle` protocol to your will and arbitrarily convert any type into any type. -See Custom FormatStyle (with locale and attributed string support) +See Custom FormatStyle (with locale and attributed string support) {{< /expand >}} @@ -206,31 +285,47 @@ Several of the included format styles also conform to `ParseableFormatStyle`, a {{< expand "Parsing Dates" "⇅" >}} +{{< xcode13-badge >}} + By setting up either a `Date.FormatStyle` or `Date.ISO8601FormatStyle` with your date structure, you can parse dates easily. -[See Parsing Dates](/#parsing-dates-from-strings) +[See Parsing Dates](/date-styles/#parsing-dates-from-strings) -[See Parsing ISO8601 Dates](/#parsing-iso8601-dates-from-strings) +[See Parsing ISO8601 Dates](/date-styles/#parsing-iso8601-dates-from-strings) {{< /expand >}} {{< expand "Parsing Decimal Numbers" "⇅" >}} +{{< xcode13-badge >}} + You can parse Decimals, Percentages, or Currency values into Decimals. -[See Parsing Decimals](/#parsing-decimals-from-strings) +[See Parsing Decimals](/number-styles/#parsing-decimals-from-strings) -[See Parsing Decimal Percentages](/#parsing-percentages-from-strings) +[See Parsing Decimal Percentages](/number-styles/#parsing-percentages-from-strings) -[See Parsing Decimal Currencies](/#parsing-currencies-from-strings) +[See Parsing Decimal Currencies](/number-styles/#parsing-currencies-from-strings) {{< /expand >}} {{< expand "Parsing Names" "⇅" >}} +{{< xcode13-badge >}} + Generally useful for parsing names from string respecting a `Locale`, it's easy to parse names. -[See Parsing Names](/#parsing-names-from-strings) +[See Parsing Names](/person-name-style/#parsing-names-from-strings) + +{{< /expand >}} + +{{< expand "Parsing URLs" "⇅" >}} + +{{< xcode14-badge >}} + +Parse a URL string into a URL value. + +[See Parsing URLs](/url-style/#parsing-urls) {{< /expand >}} diff --git a/content/_includes/requirements.md b/content/_includes/requirements.md index 840f01a..7658289 100644 --- a/content/_includes/requirements.md +++ b/content/_includes/requirements.md @@ -3,12 +3,25 @@ sitemap_ignore: true --- ## Minimum Requirements -- Xcode 13.0+ - -- iOS 15.0+ -- iPadOS 15.0+ -- macOS 12.0+ -- Mac Catalyst 15.0+ -- tvOS 15.0+ -- watchOS 8.0+ +

{{< xcode13-badge >}}

+ +This badge represents a style that is available on any platform that is built by Xcode 13 and above (iOS 15.0+, iPadOS 15.0+, Mac Catalyst 15.0+, tvOS 15.0+, watchOS 8.0+, macOS 12.0+): + +- Number styles (including currency and percent styles) +- Date styles (including iso8601, relative, and verbatim styles) +- Date range styles (interval, and components) +- Measurement styles +- List styles +- Person name styles +- Byte count styles + +

{{< xcode14-badge >}}

+ +This badge represents a style that has been updated, or is only available only, on platforms built by Xcode 14 and above (iOS 16.0+, iPadOS 16.0+, Mac Catalyst 16.0+, tvOS 16.0+, watchOS 9.0+, macOS 13.0+): + +- (Updated) Byte count style +- (Updated) Measurement style +- Duration style +- URL style +- (Updated) Verbatim date style diff --git a/content/_includes/swiftui.md b/content/_includes/swiftui.md index bcd40e6..c6a99fb 100644 --- a/content/_includes/swiftui.md +++ b/content/_includes/swiftui.md @@ -1,8 +1,6 @@ --- sitemap_ignore: true --- -## SwiftUI Integration - {{< hint type=tip title=TL;DR >}} Never write `Text("\()")` again. Just pass in the right `FormatStyle` diff --git a/content/_includes/url.md b/content/_includes/url.md new file mode 100644 index 0000000..6c38c61 --- /dev/null +++ b/content/_includes/url.md @@ -0,0 +1,185 @@ +--- +sitemap_ignore: true +--- + +## Available Options + +| Parameter | Description | +| ------------------------- | ------------------------------------------------- | +| `.always` | Always display this value | +| `.never` | Omit this value | +| `.omitIfHTTPFamily` | Omit this value if the URL is `http` or `https` | +| `.displayWhen(_:matches)` | Only display this value when the condition is met | +| `.omitWhen(:matches)` | Omit this value when the condition is met | + +{{< hint type=important >}} + +Brushing up on the [URLComponents documentation](https://developer.apple.com/documentation/foundation/urlcomponents/) can be very helpful to know the use cases for each individual component. + +{{< /hint >}} + +
Swift
+let appleURL = URL(string: "https://apple.com")!
+appleURL.formatted() // "https://apple.com"
+appleURL.formatted(.url) // "https://apple.com"
+appleURL.formatted(.url.locale(Locale(identifier: "fr_FR"))) // "https://apple.com"
+
+var httpComponents = URLComponents(url: appleURL, resolvingAgainstBaseURL: false)!
+httpComponents.scheme = "https"
+httpComponents.user = "jAppleseed"
+httpComponents.password = "Test1234"
+httpComponents.host = "apple.com"
+httpComponents.port = 80
+httpComponents.path = "/macbook-pro"
+httpComponents.query = "get-free"
+httpComponents.fragment = "someFragmentOfSomething"
+
+let complexURL = httpComponents.url!
+let everythingStyle = URL.FormatStyle(
+    scheme: .always,
+    user: .always,
+    password: .always,
+    host: .always,
+    port: .always,
+    path: .always,
+    query: .always,
+    fragment: .always
+)
+
+everythingStyle.format(complexURL) // "https://jAppleseed:Test1234@apple.com:80/macbook-pro?get-free#someFragmentOfSomething"
+
+let omitStyle = URL.FormatStyle(
+    scheme: .omitIfHTTPFamily,
+    user: .omitIfHTTPFamily,
+    password: .omitIfHTTPFamily,
+    host: .omitIfHTTPFamily,
+    port: .omitIfHTTPFamily,
+    path: .omitIfHTTPFamily,
+    query: .omitIfHTTPFamily,
+    fragment: .omitIfHTTPFamily
+)
+
+var httpsComponent = httpComponents
+httpsComponent.scheme = "https"
+let httpsURL = httpsComponent.url!
+
+var ftpComponents = httpComponents
+ftpComponents.scheme = "ftp"
+let ftpURL = ftpComponents.url!
+
+omitStyle.format(complexURL) // ""
+omitStyle.format(httpsURL) // ""
+omitStyle.format(ftpURL) // "ftp://jAppleseed@apple.com:80/macbook-pro?get-free#someFragmentOfSomething"
+
+let localhostURL = URL(string: "https://localhost:80/macbook-pro")!
+
+let displayWhen = URL.FormatStyle(
+    scheme: .always,
+    user: .never,
+    password: .never,
+    host: .displayWhen(.host, matches: ["localhost"]),
+    port: .always,
+    path: .always,
+    query: .never,
+    fragment: .never
+)
+
+displayWhen.format(complexURL) // "https://:80/macbook-pro"
+displayWhen.format(localhostURL) // "https://localhost:80/macbook-pro"
+
+let omitWhen = URL.FormatStyle(
+    scheme: .always,
+    user: .never,
+    password: .never,
+    host: .omitWhen(.host, matches: ["localhost"]),
+    port: .always,
+    path: .always,
+    query: .never,
+    fragment: .never
+)
+
+omitWhen.format(complexURL) // "https://apple.com:80/macbook-pro"
+omitWhen.format(localhostURL) // "https://:80/macbook-pro"
+
+let omitSpecificWhen = URL.FormatStyle(
+    scheme: .always,
+    user: .never,
+    password: .never,
+    host: .omitSpecificSubdomains(["secret"], includeMultiLevelSubdomains: false),
+    port: .always,
+    path: .always,
+    query: .never,
+    fragment: .never
+)
+
+var secretAppleURL = URL(string: "https://secret.apple.com/macbook-pro")!
+
+omitSpecificWhen.format(complexURL) // "https://apple.com:80/macbook-pro"
+omitSpecificWhen.format(secretAppleURL) // "https://apple.com/macbook-pro"
+
+let omitSpecificWhenWhere = URL.FormatStyle(
+    scheme: .always,
+    user: .never,
+    password: .never,
+    host: .omitSpecificSubdomains(["secret"], includeMultiLevelSubdomains: false, when: .user, matches: ["jAppleseed"]),
+    port: .always,
+    path: .always,
+    query: .never,
+    fragment: .never
+)
+
+let complexSecretURL = URL(string: "https://jAppleseed:Test1234@secret.apple.com:80/macbook-pro?get-free#someFragmentOfSomething")!
+
+omitSpecificWhenWhere.format(complexSecretURL) // "https://apple.com:80/macbook-pro"
+omitSpecificWhenWhere.format(secretAppleURL) // "https://secret.apple.com/macbook-pro"
+ +## Parsing URLs + +The venerable URL initializer `URL(string:relativeTo:)` has been available to us since Xcode 10.2. In most cases this will work well for most use cases. + +If you're looking to parse URLs in a structured way, you can use the URL parse strategy on the URL.FormatStyle struct. + +### Available Options + +For each component of the URL (scheme, user, password, host, port, path, query, or fragment) you can configure them with the following options: + +| Parameter | Description | +| ----------------- | ----------------------------------------- | +| `.optional` | Sets the unit as optional | +| `.required` | Sets the unit as required for a valid URL | +| `.defaultValue()` | If missing, uses the default value | + +{{< hint type=note >}} + +By default, only the scheme and the host are set as required. + +{{< /hint >}} + +
Swift
+try URL.FormatStyle.Strategy(port: .defaultValue(80)).parse("http://www.apple.com") // http://www.apple.com:80
+try URL.FormatStyle.Strategy(port: .optional).parse("http://www.apple.com") // http://www.apple.com
+try URL.FormatStyle.Strategy(port: .required).parse("http://www.apple.com") // throws an error
+
+// This returns a valid URL
+try URL.FormatStyle.Strategy()
+    .scheme(.required)
+    .user(.required)
+    .password(.required)
+    .host(.required)
+    .port(.required)
+    .path(.required)
+    .query(.required)
+    .fragment(.required)
+    .parse("https://jAppleseed:Test1234@apple.com:80/macbook-pro?get-free#someFragmentOfSomething")
+
+// This throws an error (the port is missing)
+try URL.FormatStyle.Strategy()
+    .scheme(.required)
+    .user(.required)
+    .password(.required)
+    .host(.required)
+    .port(.required)
+    .path(.required)
+    .query(.required)
+    .fragment(.required)
+    .parse("https://jAppleseed:Test1234@apple.com/macbook-pro?get-free#someFragmentOfSomething")
diff --git a/content/_includes/verbatimDate.md b/content/_includes/verbatimDate.md index a6c004e..30cf7f1 100644 --- a/content/_includes/verbatimDate.md +++ b/content/_includes/verbatimDate.md @@ -1,9 +1,15 @@ --- sitemap_ignore: true --- + +## Version Differences + +1. {{< xcode13-badge >}} You use this style by initializing a new instance of `Date.VerbatimFormatStyle` which can be used directly, or passed into a `.formatted` method. +2. {{< xcode14-badge >}} Similar to other date format styles, you can now use the `.formatted(.verbatim(…))` type method. + ### Format Strings -The `Date.VerbatimFormatStyle` accepts a `Date.FormatString` as a parameter inside of it's initializer. Inside of which, you can include any of symbol tokens (with options) along with other regular characters. +The power of the verbatim format style lies in the `Date.FormatString` that is passed in. This struct conforms to the [ExpressibleByStringInterpolation protocol](https://developer.apple.com/documentation/swift/expressiblebystringinterpolation) which allows us mix and match structured values ([known as tokens](#symbol-tokens)) and strings as much as we want.
let twosdayDateComponents = DateComponents(
     year: 2022,
@@ -17,23 +23,47 @@ The `Date.VerbatimFormatStyle` accepts a `Date.FormatString` as a parameter insi
 let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
 
 let verbatim = Date.VerbatimFormatStyle(
-    format: "\(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .twoDigits)",
+    format: "Today is \(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .twoDigits) otherwise known as \"Twosday\"",
     timeZone: TimeZone.current,
     calendar: .current
 )
 
-verbatim.format(twosday) // "02:22"
- -### Symbol Tokens - -Similar to the `.dateTime` you have a wide variety of symbols and customization options. +verbatim.format(twosday) // "Today is 02:22 otherwise known as "Twosday"" -{{< hint type=note >}} +twosday.formatted( + .verbatim( + "Today is \(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .twoDigits) otherwise known as \"Twosday\"", + locale: Locale(identifier: "zh_CN"), + timeZone: .current, + calendar: .current + ) +) // "Today is 02:22 otherwise known as "Twosday"" -Oddly enough, each of these options are thoroughly documented by Apple (this is where the following content comes from) in their header files. Unfortunately, none of this documentation has been added to the official docs online and within Xcode. +### Symbol Tokens -{{< /hint >}} +Every conceivable piece of information inside of a `Date` is available for use. + +- [Era](#era) +- [Year](#year) +- [YearForWeekOfYear](#yearforweekofyear) +- [CyclicYear](#cyclicyear) +- [Quarter](#quarter) +- [Month](#month) +- [Week](#week) +- [Day](#day) +- [DayOfYear](#dayofyear) +- [Weekday](#weekday) +- [DayPeriod](#dayperiod) +- [Minute](#minute) +- [Second](#second) +- [SecondFraction](#secondfraction) +- [TimeZone](#timezone) +- [StandaloneQuarter](#standalonequarter) +- [StandaloneMonth](#standalonemonth) +- [StandaloneWeekday](#standaloneweekday) +- [VerbatimHour](#verbatimhour) +--- #### Era @@ -43,6 +73,8 @@ Oddly enough, each of these options are thoroughly documented by Apple (this is | `.wide` | Wide era name. For example, "Anno Domini", "Reiwa", "令和". | | `.narrow` | Narrow era name. For example, For example, "A", "R", "R". | +--- + #### Year | Option | Description | @@ -53,6 +85,8 @@ Oddly enough, each of these options are thoroughly documented by Apple (this is | `.relatedGregorian(minimumLength:)` | Related Gregorian year. For non-Gregorian calendars, this corresponds to the extended Gregorian year in which the calendar’s year begins. Related Gregorian years are often displayed, for example, when formatting dates in the Japanese calendar — e.g. "2012(平成24)年1月15日" — or in the Chinese calendar — e.g. "2012壬辰年腊月初四". | | `.extended(minimumLength:)` | Extended year. This is a single number designating the year of this calendar system, encompassing all supra-year fields. For example, for the Julian calendar system, year numbers are positive, with an era of BCE or CE. An extended year value for the Julian calendar system assigns positive values to CE years and negative values to BCE years, with 1 BCE being year 0. | +--- + #### YearForWeekOfYear | Option | Description | @@ -61,6 +95,8 @@ Oddly enough, each of these options are thoroughly documented by Apple (this is | `.twoDigits` | Two low-order digits. Padded or truncated if necessary. For example, `02`, `20`, `01`, `17`, `73`. | | `.padded(_ length: Int)` | Three or more digits. Padded if necessary. For example, `002`, `020`, `201`, `2017`, `20173`. | +--- + #### CyclicYear Calendars such as the Chinese lunar calendar (and related calendars) and the Hindu calendars use 60-year cycles of year names. If the calendar does not provide cyclic year name data, or if the year value to be formatted is out of the range of years for which cyclic name data is provided, then numeric formatting is used (behaves like `Year`). @@ -72,6 +108,8 @@ Calendars such as the Chinese lunar calendar (and related calendars) and the Hin | `.narrow` | Narrow cyclic year name. For example, "甲子". | +--- + #### Quarter | Option | Description | @@ -83,6 +121,8 @@ Calendars such as the Chinese lunar calendar (and related calendars) and the Hin | `.narrow` | Narrow quarter. For example `2`. | +--- + #### Month | Option | Description | | @@ -92,6 +132,8 @@ Calendars such as the Chinese lunar calendar (and related calendars) and the Hin | `.abbreviated` | Abbreviated month name. For example, "Sep". | | | `.wide` | Wide month name. For example, "September". | `` | | `.narrow` | Narrow month name. For example, "S". | | + +--- #### Week @@ -103,6 +145,8 @@ Week symbols. Use with `YearForWeekOfYear` for the year field instead of `Year`. | `.twoDigits` | Two-digit numeric week of year, zero padded as necessary. For example, `08`, `27`. | | `.weekOfMonth` | One-digit numeric week of month, starting from 1. For example, `1`. | +--- + #### Day | Option | Description | @@ -112,6 +156,8 @@ Week symbols. Use with `YearForWeekOfYear` for the year field instead of `Year`. | `.ordinalOfDayInMonth` | Ordinal of day in month. For example, the 2nd Wed in July would yield `2`. | | `.ulianModified(minimumLength:)` | The field length specifies the minimum number of digits, with zero-padding as necessary.
This is different from the conventional Julian day number in two regards. First, it demarcates days at local zone midnight, rather than noon GMT. Second, it is a local number; that is, it depends on the local time zone. It can be thought of as a single number that encompasses all the date-related fields.
For example, `2451334`. | + +--- #### DayOfYear @@ -122,6 +168,8 @@ Week symbols. Use with `YearForWeekOfYear` for the year field instead of `Year`. | `.threeDigits` | Three-digit day of year, with zero-padding as necessary. For example, `007`, `033`, `345`. | +--- + #### Weekday | Option | Description | @@ -132,6 +180,8 @@ Week symbols. Use with `YearForWeekOfYear` for the year field instead of `Year`. | `.short` | Short day of week name. For example, "Tu". | | `.oneDigit` | Local day of week number/name. The value depends on the local starting day of the week. | | `.twoDigits` | Local day of week number/name, format style; two digits, zero-padded if necessary. | + +--- #### DayPeriod @@ -149,6 +199,8 @@ Each of the options can be passed a `width` case. | `.with12s(_ width:)` | Day period including designations for noon and midnight. For example,
Abbreviated: `mid`
Wide: `midnight`
Narrow: `md`.
| | `.conversational` | Conversational day period. For example,
Abbreviated: `at night`, `nachm.`, `ip.`
Wide: `at night`, `nachmittags`, `iltapäivällä`.
Narrow: `at night`, `nachm.`, `iltap`.
| +--- + #### Minute | Option | Description | @@ -157,6 +209,8 @@ Each of the options can be passed a `width` case. | `.twoDigits` | Two-digit numeric, zero padded if needed. For example, `08`, `59`. | +--- + #### Second | Option | Description | @@ -165,6 +219,8 @@ Each of the options can be passed a `width` case. | `.twoDigits` | Two digits numeric, zero padded if needed, not rounded. For example, `08`, `12`. | +--- + #### SecondFraction | Option | Description | @@ -172,6 +228,8 @@ Each of the options can be passed a `width` case. | `.fractional(_ val:)` | Fractional second (numeric).
Truncates, like other numeric time fields, but in this case to the number of digits specified by the associated `Int`.
For example, specifying `4` for seconds value `12.34567` yields `12.3456`. | | `.milliseconds(_ val:)` | Milliseconds in day (numeric).
The associated `Int` specifies the minimum number of digits, with zero-padding as necessary. The maximum number of digits is 9.
This field behaves exactly like a composite of all time-related fields, not including the zone fields. As such, it also reflects discontinuities of those fields on DST transition days. On a day of DST onset, it will jump forward. On a day of DST cessation, it will jump backward. This reflects the fact that is must be combined with the offset field to obtain a unique local time value. | +--- + #### TimeZone Each talkes a `Width` case. @@ -188,6 +246,8 @@ Each talkes a `Width` case. | `.exemplarLocation` | The exemplar city (location) for the time zone. The localized exemplar city name for the special zone or unknown is used as the fallback if it is unavailable.
For example, "Los Angeles". | | `.genericLocation` | The generic location format. Falls back to `longLocalizedGMT` if unavailable. Recommends for presenting possible time zone choices for user selection.
For example, "Los Angeles Time". | +--- + #### StandaloneQuarter | Option | Description | @@ -199,6 +259,8 @@ Each talkes a `Width` case. | `.narrow` | Standalone narrow quarter. For example "2". | +--- + #### StandaloneMonth | Option | Description | @@ -208,6 +270,8 @@ Each talkes a `Width` case. | `.abbreviated` | Stand-alone abbreviated month.For example, "Sep". | | `.wide` | Stand-alone wide month. For example, "September". | | `.narrow` | Stand-alone narrow month. For example, "S". | + +--- #### StandaloneWeekday @@ -218,6 +282,8 @@ Each talkes a `Width` case. | `.wide` | Standalone wide local day of week number/name.For example, "Tuesday". | | `.narrow` | Standalone narrow local day of week number/name. For example, "T". | | `.short` | Standalone short local day of week number/name. For example, "Tu". | + +--- #### VerbatimHour diff --git a/content/_index.md b/content/_index.md index cc0464a..ed55e1f 100644 --- a/content/_index.md +++ b/content/_index.md @@ -28,254 +28,12 @@ This site is going to help you do just that. {{< include file="/_includes/basics.md" type="page" >}} ---- - -## Number Style - -{{< hint type=tip title=TL;DR >}} - -The many ways you can customize the display of numbers. - -{{< /hint >}} - -There are many ways to format Swift's numerical types (Float, Double, Decimal, and Integer) for display to the user. Each of the following options can be used by the Percent Format Styles and the Currency Format Style. - -The examples below show the individual options available to format your final string, the real power available is that you chain these options together to allow for a truly staggering amount of customization. - -{{< include file="/_includes/numbers.md" type="page" >}} - ---- - -## Percent Style - -{{< hint type=tip title=TL;DR >}} - -Output number as a percentage. - -{{< /hint >}} - -There are many ways to format Swift's numerical types (Float, Double, Decimal, and Integer) for display to the user. Each of the following options can be used by the Percent Format Styles and the Currency Format Style. - -The examples below show the individual options available to format your final string, the real power available is that you chain these options together to allow for a truly staggering amount of customization. - -{{< hint type=important >}} - -Percentages are set by a range from 0.0 to 1.0, where 0.5 being 50%. This is consistent with the rest of Cocoa. - -{{< /hint >}} - -{{< include file="/_includes/percent.md" type="page" >}} - ---- - -## Currency Style - -{{< hint type=tip title=TL;DR >}} - -Output number values in the local currency. - -{{< /hint >}} - -The currency format style is very similar to the Number and Percent format styles and works with Swift's numerical types (Float, Double, Decimal, and Integer). - -The key difference is that you will need to pass in the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) country code for the currency you would like to display. - -{{< hint type=important >}} - -Because accuracy can't be guaranteed, never use floating point numbers (Float and Double) to store and do calculations on important values like money. Either store cents as Integers, or use `Decimal` values. - -{{< /hint >}} - -{{< include file="/_includes/currency.md" type="page" >}} - ---- - -## Date Style (Single Date) - -{{< hint type=tip title=TL;DR >}} - -A flexible way to composite the exact date string of your dreams. - -{{< /hint >}} - -Like lego blocks, this format style allows you to mix and match the date components you would like to use in your final string. - -{{< include file="/_includes/dateTime.md" type="page" >}} - ---- - -## Date and Time (Single Date) - -{{< hint type=tip title=TL;DR >}} - -A quick way of displaying the date and time. - -{{< /hint >}} - -This is the default formatter used when calling `formatted` on any date object. In general, it's an easy way to display a date with or without it's corresponding time component. - -Each portion (the date and the time), can be customized in the following ways. - -{{< include file="/_includes/dateAndTime.md" type="page" >}} - ---- - -## ISO 8601 Date Style (Single Date) - -{{< hint type=tip title=TL;DR >}} - -The fastest way to output the globe's standard date string. - -{{< /hint >}} - -Since this is an international standard, the amount of customization is limited with this format style. - -{{< include file="/_includes/iso8601.md" type="page" >}} - ---- - -## Relative Date Style (Single Date) - -{{< hint type=tip title=TL;DR >}} - -Quickly say approximately how long it will be between a date and now. - -{{< /hint >}} - -This format style will tell you the approximate distance to or from a date to now. - -There are no options available to set the units you would like to display, the system will only show the largest unit. - -{{< include file="/_includes/relativeDate.md" type="page" >}} - ---- - -## Verbatim Date Style (Single Date) - -{{< hint type=tip title=TL;DR >}} - -A fixed and structured method of formatting a datetime string, avoiding localization pitfalls. - -{{< /hint >}} - -The verbatim date format style is as close as Format Styles get to the old date formatting string methods used by (NS)DateFormatter. - -Unlike the older methods, the format string that you pass into the format style is much more regimented in how you access the various symbols. - -{{< hint type=important >}} - -This style does not use any localization and you cannot set the Locale. - -{{< /hint >}} - -{{< include file="/_includes/verbatimDate.md" type="page" >}} - ---- - -## Interval Date Style (Date Range) - -{{< hint type=tip title=TL;DR >}} - -Shows the earliest and last dates in a `Range`, similar options to the [Date and Time](#) single date format style. - -{{< /hint >}} - -For a given `Range`, this format will output the earliest and last days. - -{{< include file="/_includes/intervalDateRange.md" type="page" >}} - ---- - -## Components Date Style (Date Range) - -{{< hint type=tip title=TL;DR >}} - -Shows the distance between the earliest and latest dates in a range, similar to the [Relative Date](#) style for single dates. - -{{< /hint >}} - -For a given `Range`, you can display the distance between the earliest and latest dates using specific units. - -{{< hint type=important >}} - -For all given fields, the system will only display the unit if it's not a 0 value. Including the field will only specify that the unit _might_ be used. - -{{< /hint >}} - -{{< include file="/_includes/componentsDateRange.md" type="page" >}} - ---- - -## Measurement Style - -{{< hint type=tip title=TL;DR >}} - -Print out your various measurements. - -{{< /hint >}} - -The Measurement API inside of Foundation is a powerful toolkit for converting and displaying units. - -{{< include file="/_includes/measurement.md" type="page" >}} - ---- - -## List Style - -{{< hint type=tip title=TL;DR >}} - -Outputs an array as a list, you can further customize how each element in the array is displayed. - -{{< /hint >}} - -This format style works on any Array or items. In cases where the items are string convertible, the output is standard. - -{{< include file="/_includes/listStyle.md" type="page" >}} - ---- - -## Person Name Component Style - -{{< hint type=tip title=TL;DR >}} - -Never again do you have to remember if the family name goes before the given name in a locale. - -{{< /hint >}} - -This format style solves the complexities of displaying an individual's name in the correct, localized fashion. - -{{< include file="/_includes/names.md" type="page" >}} - ---- - -## Byte Count Style - -{{< hint type=tip title=TL;DR >}} - -Simplify converting that byte value to terabytes. - -{{< /hint >}} - -This style allows you to format `Int64` byte counts as different units. - -{{< include file="/_includes/bytes.md" type="page" >}} - ---- - -## Custom Format Style - -{{< hint type=tip title=TL;DR >}} - -Visit the "Build a Format Style Workshop". - -{{< /hint >}} - -{{< include file="/_includes/custom.md" type="page" >}} - ---- - -{{< include file="/_includes/swiftui.md" type="page" >}} - ---- - -{{< include file="/_includes/attributedStrings.md" type="page" >}} + \ No newline at end of file diff --git a/content/attributed-string-output/_index.md b/content/attributed-string-output/_index.md new file mode 100644 index 0000000..fdaf375 --- /dev/null +++ b/content/attributed-string-output/_index.md @@ -0,0 +1,16 @@ +--- +title: "Attributed String Output" +sitemap_ignore: false +--- + +{{< include file="/_includes/attributedStrings.md" type="page" >}} + + \ No newline at end of file diff --git a/content/byte-count-style/_index.md b/content/byte-count-style/_index.md new file mode 100644 index 0000000..07b6230 --- /dev/null +++ b/content/byte-count-style/_index.md @@ -0,0 +1,34 @@ +--- +title: "Byte Count Format Style" +sitemap_ignore: false +--- + +{{< hint type=tip title=TL;DR >}} + +Simply showing your storage values, and even have fun converting units. + +{{< /hint >}} + +There are actually two byte count format styles available to you (depending on your Xcode version): + +1. {{< xcode13-badge >}} `ByteCountFormatStyle` is used to format `Int64` values. +2. {{< xcode14-badge >}} `Measurement.FormatStyle.ByteCount` is used to format `Measurement` values who are `UnitInformationStorage` types. + +Thankfully, the APIs are identical between these two formatters. + +{{< include file="/_includes/bytes.md" type="page" >}} + + \ No newline at end of file diff --git a/content/custom-styles/_index.md b/content/custom-styles/_index.md new file mode 100644 index 0000000..3dfa58a --- /dev/null +++ b/content/custom-styles/_index.md @@ -0,0 +1,27 @@ +--- +title: "Creating Custom Format Styles" +sitemap_ignore: false +--- + +{{< hint type=tip title=TL;DR >}} + +Visit the "Build a Format Style Workshop". + +{{< /hint >}} + +{{< include file="/_includes/custom.md" type="page" >}} + + \ No newline at end of file diff --git a/content/date-range-styles/_index.md b/content/date-range-styles/_index.md new file mode 100644 index 0000000..7df3e55 --- /dev/null +++ b/content/date-range-styles/_index.md @@ -0,0 +1,51 @@ +--- +title: "Date Range Styles" +sitemap_ignore: false +--- + +

Interval Date Style (Date Range) {{< xcode13-badge >}}

+ +{{< hint type=tip title=TL;DR >}} + +Shows the earliest and last dates in a `Range`, similar options to the [Date and Time](#) single date format style. + +{{< /hint >}} + +For a given `Range`, this format will output the earliest and last days. + +{{< include file="/_includes/intervalDateRange.md" type="page" >}} + +--- + +

Components Date Style (Date Range) {{< xcode13-badge >}}

+ +{{< hint type=tip title=TL;DR >}} + +Shows the distance between the earliest and latest dates in a range, similar to the [Relative Date](#) style for single dates. + +{{< /hint >}} + +For a given `Range`, you can display the distance between the earliest and latest dates using specific units. + +{{< hint type=important >}} + +For all given fields, the system will only display the unit if it's not a 0 value. Including the field will only specify that the unit _might_ be used. + +{{< /hint >}} + +{{< include file="/_includes/componentsDateRange.md" type="page" >}} + + diff --git a/content/date-styles/_index.md b/content/date-styles/_index.md new file mode 100644 index 0000000..39f2410 --- /dev/null +++ b/content/date-styles/_index.md @@ -0,0 +1,93 @@ +--- +title: "Single Date Styles" +sitemap_ignore: false +--- + +

Compositing {{< xcode13-badge >}}

+ +{{< hint type=tip title=TL;DR >}} + +A flexible way to composite the exact date string of your dreams. + +{{< /hint >}} + +Like lego blocks, this format style allows you to mix and match the date components you would like to use in your final string. + +{{< include file="/_includes/dateTime.md" type="page" >}} + +--- + +

Date and Time {{< xcode13-badge >}}

+ +{{< hint type=tip title=TL;DR >}} + +A quick way of displaying the date and time. + +{{< /hint >}} + +This is the default formatter used when calling `formatted` on any date object. In general, it's an easy way to display a date with or without it's corresponding time component. + +Each portion (the date and the time), can be customized in the following ways. + +{{< include file="/_includes/dateAndTime.md" type="page" >}} + +--- + +

ISO 8601 Date Style{{< xcode13-badge >}}

+ +{{< hint type=tip title=TL;DR >}} + +The fastest way to output the globe's standard date string. + +{{< /hint >}} + +Since this is an international standard, the amount of customization is limited with this format style. + +{{< include file="/_includes/iso8601.md" type="page" >}} + +--- + +

Relative Date Style{{< xcode13-badge >}}

+ +{{< hint type=tip title=TL;DR >}} + +Quickly say approximately how long it will be between a date and now. + +{{< /hint >}} + +This format style will tell you the approximate distance to or from a date to now. + +There are no options available to set the units you would like to display, the system will only show the largest unit. + +{{< include file="/_includes/relativeDate.md" type="page" >}} + +--- + +

Verbatim Date Style {{< xcode13-badge >}} {{< xcode14-badge >}}

+ +{{< hint type=tip title=TL;DR >}} + +A method of creating fixed, structured date format strings. + +{{< /hint >}} + +The verbatim date format style is as close as Format Styles get to the old date formatting string methods used by (NS)DateFormatter. + +Unlike the older methods, the format string that you pass into the format style is much more regimented in how you access the various symbols. + +{{< include file="/_includes/verbatimDate.md" type="page" >}} + + diff --git a/content/duration-styles/_index.md b/content/duration-styles/_index.md new file mode 100644 index 0000000..c9f5464 --- /dev/null +++ b/content/duration-styles/_index.md @@ -0,0 +1,14 @@ +--- +title: "Duration Style" +sitemap_ignore: false +--- + +{{< hint type=tip title=TL;DR >}} + +Print out a nice text version of your new `Duration`. + +{{< /hint >}} + +The `Duration` type, introduced in at WWDC 2022, has its own set of styles available. + +{{< include file="/_includes/duration.md" type="page" >}} diff --git a/content/list-style/_index.md b/content/list-style/_index.md new file mode 100644 index 0000000..a87df5d --- /dev/null +++ b/content/list-style/_index.md @@ -0,0 +1,31 @@ +--- +title: "List Style" +sitemap_ignore: false +--- + +{{< hint type=tip title=TL;DR >}} + +Outputs an array as a list, you can further customize how each element in the array is displayed. + +{{< /hint >}} + +{{< xcode13-badge >}} + +This format style works on any Array or items. In cases where the items are string convertible, the output is standard. + +{{< include file="/_includes/listStyle.md" type="page" >}} + + \ No newline at end of file diff --git a/content/measurement-style/_index.md b/content/measurement-style/_index.md new file mode 100644 index 0000000..877dd49 --- /dev/null +++ b/content/measurement-style/_index.md @@ -0,0 +1,30 @@ +--- +sitemap_ignore: false +--- + +{{< hint type=tip title=TL;DR >}} + +Print out your various measurements. + +{{< /hint >}} + +{{< xcode13-badge >}} + +The Measurement API inside of Foundation is a powerful toolkit for converting and displaying units. + +{{< include file="/_includes/measurement.md" type="page" >}} + + \ No newline at end of file diff --git a/content/numeric-styles/_index.md b/content/numeric-styles/_index.md new file mode 100644 index 0000000..0381b24 --- /dev/null +++ b/content/numeric-styles/_index.md @@ -0,0 +1,77 @@ +--- +title: "Numeric Styles" +sitemap_ignore: false +--- + +

Number Style {{< xcode13-badge >}}

+ +{{< hint type=tip title=TL;DR >}} + +The many ways you can customize the display of numbers. + +{{< /hint >}} + +There are many ways to format Swift's numerical types (Float, Double, Decimal, and Integer) for display to the user. Each of the following options can be used by the Percent Format Styles and the Currency Format Style. + +The examples below show the individual options available to format your final string, the real power available is that you chain these options together to allow for a truly staggering amount of customization. + +{{< include file="/_includes/numbers.md" type="page" >}} + +--- + +

Percent Style {{< xcode13-badge >}}

+ +{{< hint type=tip title=TL;DR >}} + +Output number as a percentage. + +{{< /hint >}} + +There are many ways to format Swift's numerical types (Float, Double, Decimal, and Integer) for display to the user. Each of the following options can be used by the Percent Format Styles and the Currency Format Style. + +The examples below show the individual options available to format your final string, the real power available is that you chain these options together to allow for a truly staggering amount of customization. + +{{< hint type=important >}} + +Percentages are set by a range from 0.0 to 1.0, where 0.5 being 50%. This is consistent with the rest of Cocoa. + +{{< /hint >}} + +{{< include file="/_includes/percent.md" type="page" >}} + +--- + +

Currency Style {{< xcode13-badge >}}

+ +{{< hint type=tip title=TL;DR >}} + +Output number values in the local currency. + +{{< /hint >}} + +The currency format style is very similar to the Number and Percent format styles and works with Swift's numerical types (Float, Double, Decimal, and Integer). + +The key difference is that you will need to pass in the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) country code for the currency you would like to display. + +{{< hint type=important >}} + +Because accuracy can't be guaranteed, never use floating point numbers (Float and Double) to store and do calculations on important values like money. Either store cents as Integers, or use `Decimal` values. + +{{< /hint >}} + +{{< include file="/_includes/currency.md" type="page" >}} + + diff --git a/content/person-name-style/_index.md b/content/person-name-style/_index.md new file mode 100644 index 0000000..5b2f39d --- /dev/null +++ b/content/person-name-style/_index.md @@ -0,0 +1,31 @@ +--- +title: "Person Name Style" +sitemap_ignore: false +--- + +{{< hint type=tip title=TL;DR >}} + +Never again do you have to remember if the family name goes before the given name in a locale. + +{{< /hint >}} + +{{< xcode13-badge >}} + +This format style solves the complexities of displaying an individual's name in the correct, localized fashion. + +{{< include file="/_includes/names.md" type="page" >}} + + \ No newline at end of file diff --git a/content/swiftui/_index.md b/content/swiftui/_index.md new file mode 100644 index 0000000..25ca81a --- /dev/null +++ b/content/swiftui/_index.md @@ -0,0 +1,21 @@ +--- +title: "SwiftUI Integration" +sitemap_ignore: false +--- + +{{< include file="/_includes/swiftui.md" type="page" >}} + + \ No newline at end of file diff --git a/content/url-style/_index.md b/content/url-style/_index.md new file mode 100644 index 0000000..676eece --- /dev/null +++ b/content/url-style/_index.md @@ -0,0 +1,31 @@ +--- +title: "URL Style" +sitemap_ignore: false +--- + +{{< hint type=tip title=TL;DR >}} + +Show off the all of the parts of your URL that you'd like. + +{{< /hint >}} + +{{< xcode14-badge >}} + +Easily convert every bit of your URL into a string. + +{{< include file="/_includes/url.md" type="page" >}} + + \ No newline at end of file diff --git a/data/menu/main.yaml b/data/menu/main.yaml index ce9f864..a836a10 100644 --- a/data/menu/main.yaml +++ b/data/menu/main.yaml @@ -12,66 +12,79 @@ main: - name: The Basics ref: "/#the-basics" external: true - - name: Number Styles - ref: "/#number-style" - external: true + - name: Numeric Styles + ref: "/numeric-styles" + external: false sub: - name: Number Style - ref: "/#number-style" + ref: "/number-styles/#number-style" external: true - name: Percent Style - ref: "/#percent-style" + ref: "/number-styles/#percent-style" external: true - name: Currency Style - ref: "/#currency-style" + ref: "/number-styles/#currency-style" external: true - name: Single Date Styles - ref: "/#date-and-time-single-date" - external: true + ref: "/date-styles/" + external: false sub: - - name: Date and Time Style - ref: "/#date-and-time-single-date" + - name: Compositing + ref: "/date-styles/#compositing" external: true - - name: dateTime Style - ref: "#datetime-compositing-single-date" + - name: Date and Time Style + ref: "/date-styles//#date-and-time-single-date" external: true - name: ISO 8601 Style - ref: "#iso-8601-date-style-single-date" + ref: "/date-styles/#iso-8601-date-style-single-date" external: true - name: Relative Date Style - ref: "#relative-date-style-single-date" + ref: "/date-styles/#relative-date-style-single-date" external: true - name: Verbatim Style - ref: "#verbatim-date-style-single-date" + ref: "/date-styles/#verbatim-date-style-single-date" external: true - name: Date Range Styles - ref: "#interval-date-style-date-range" - external: true + ref: "/date-range-styles/" + external: false sub: - name: Interval Style - ref: "#interval-date-style-date-range" + ref: "/date-range-styles/#interval-date-style-date-range" external: true - name: Components Style - ref: "#components-date-style-date-range" + ref: "/date-range-styles/#components-date-style-date-range" + external: true + - name: Duration Styles + ref: "/duration-styles/" + external: false + sub: + - name: Time Style + ref: "/duration-styles/#time-style" + external: true + - name: Units Style + ref: "/duration-styles/#units-style" external: true - name: Measurement Style - ref: "#measurement-style" - external: true + ref: "/measurement-style/" + external: false - name: List Style - ref: "#list-style" - external: true + ref: "/list-style/" + external: false - name: Person Name Style - ref: "#person-name-component-style" - external: true + ref: "/person-name-style/" + external: false - name: Byte Count Style - ref: "#byte-count-style" - external: true + ref: "/byte-count-style/" + external: false + - name: URL Style + ref: "/url-style/" + external: false - name: Custom FormatStyle - ref: "#custom-format-style" - external: true + ref: "/custom-styles" + external: false - name: SwiftUI Integration - ref: "#swiftui-integration" - external: true + ref: "/swiftui/" + external: false - name: AttributedString Output - ref: "#attributed-string-output" - external: true \ No newline at end of file + ref: "/attributed-string-output/" + external: false \ No newline at end of file diff --git a/layouts/_default/rss.xml b/layouts/_default/rss.xml index 6b1f9c0..644bd92 100644 --- a/layouts/_default/rss.xml +++ b/layouts/_default/rss.xml @@ -32,7 +32,7 @@ {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }} {{ with .Site.Author.email }}{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}{{end}} {{ .Permalink }} - {{ .Summary | html }} + {{ .Content | html }} {{ end }}{{ end }} diff --git a/layouts/shortcodes/xcode13-badge.html b/layouts/shortcodes/xcode13-badge.html new file mode 100644 index 0000000..ce23839 --- /dev/null +++ b/layouts/shortcodes/xcode13-badge.html @@ -0,0 +1 @@ +Xcode 13+ \ No newline at end of file diff --git a/layouts/shortcodes/xcode14-badge.html b/layouts/shortcodes/xcode14-badge.html new file mode 100644 index 0000000..72001a1 --- /dev/null +++ b/layouts/shortcodes/xcode14-badge.html @@ -0,0 +1 @@ +Xcode 14+ \ No newline at end of file diff --git a/sections/attributedStrings.md b/sections/attributedStrings.md index 35e28fa..0596aab 100644 --- a/sections/attributedStrings.md +++ b/sections/attributedStrings.md @@ -1,8 +1,6 @@ --- sitemap_ignore: true --- -## Attributed String Output - {{< hint type=tip title=TL;DR >}} Some of the built-in styles will output `AttributedString` values instead. diff --git a/sections/bytes.md b/sections/bytes.md index e1c21ee..5dfe89d 100644 --- a/sections/bytes.md +++ b/sections/bytes.md @@ -1,9 +1,18 @@ --- sitemap_ignore: true --- + +{{< hint type=warning >}} + +**This style is broken in certain ways in Xcode 13**. Providing any unit above gigabyte will cause a crash with the message: "Fatal error: invalid Units value". + +Experiment for yourself to see if you can make this work for you, but you may want to fall back to the older `ByteCountFormatter` in this case. + +{{< /hint >}} + ### Available Options -The format style has four possible options: +Both format styles have the following options available: | Option | Description | | ------------------------- | ----------------------------------------------------- | @@ -33,49 +42,90 @@ The format style has four possible options: | `.zb` | As zetabytes (1,000,000,000,000,000,000,000 bytes) | | `.ybOrHigher` | As yottabytes (1,000,000,000,000,000,000,000,000 bytes) | -{{< hint type=warning >}} - -Currently, using any value above `.gb` will cause a crash with the message: "Fatal error: invalid Units value". (Feedback FB10031442 has been submitted to Apple.) +{{< hint type=important >}} -If you're looking to convert between two units, you can use the `Measurement` API and convert between `UnitInformationStorage` values. +You can provide a `Set` for the `units` parameter instead of an individual unit. Doing so will cause the style to use the unit that will provide the smallest number of digits. {{< /hint >}} -``` +

 

+ +``` Swift + +// MARK: - Int64 + let terabyte: Int64 = 1_000_000_000_000 -let formatter = ByteCountFormatter() -formatter.countStyle = .binary -formatter.allowedUnits -formatter.includesActualByteCount -formatter.countStyle = .file +var integerByteCountStyle = ByteCountFormatStyle() +integerByteCountStyle.style = .file +integerByteCountStyle.allowedUnits = [.gb, .tb] +integerByteCountStyle.includesActualByteCount = true + +integerByteCountStyle.format(terabyte) // "1 TB (1,000,000,000,000 bytes)" +terabyte.formatted(integerByteCountStyle) // "1 TB (1,000,000,000,000 bytes)" -terabyte.formatted(.byteCount(style: .binary)) // "931.32 GB" +terabyte.formatted(.byteCount(style: .binary)) // "931.32 GB" terabyte.formatted(.byteCount(style: .decimal)) // "1 TB" -terabyte.formatted(.byteCount(style: .file)) // "1 TB" -terabyte.formatted(.byteCount(style: .memory)) // "931.32 GB" +terabyte.formatted(.byteCount(style: .file)) // "1 TB" +terabyte.formatted(.byteCount(style: .memory)) // "931.32 GB" -terabyte.formatted(.byteCount(style: .memory, allowedUnits: .bytes)) // "1,000,000,000,000 bytes" -terabyte.formatted(.byteCount(style: .memory, allowedUnits: .bytes)) // "1,000,000,000,000 bytes" -terabyte.formatted(.byteCount(style: .memory, allowedUnits: .kb)) // "1,000,000,000 kB" -terabyte.formatted(.byteCount(style: .memory, allowedUnits: .mb)) // "1,000,000 MB" +terabyte.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes" +terabyte.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes" +terabyte.formatted(.byteCount(style: .file, allowedUnits: .kb)) // "976,562,500 kB" +terabyte.formatted(.byteCount(style: .file, allowedUnits: .mb)) // "953,674.3 MB" +terabyte.formatted(.byteCount(style: .file, allowedUnits: .gb)) // "931.32 GB" +terabyte.formatted(.byteCount(style: .file, allowedUnits: .tb)) // "0.91 TB" +terabyte.formatted(.byteCount(style: .file, allowedUnits: .pb)) // "0 PB" +terabyte.formatted(.byteCount(style: .file, allowedUnits: .zb)) // "0 PB" +terabyte.formatted(.byteCount(style: .file, allowedUnits: .ybOrHigher)) // 0 PB -// .gb, .tb, .pb, .eb, .zb, and .ybOrHigher cause a FatalError (Feedback FB10031442) -// terabyte.formatted(.byteCount(style: .file, allowedUnits: .gb)) +Int64(1_000).formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 kB" +Int64(1_000_000).formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 MB" -Int64(0).formatted(.byteCount(style: .file, allowedUnits: .mb, spellsOutZero: true)) // "Zero bytes" -Int64(0).formatted(.byteCount(style: .file, allowedUnits: .mb, spellsOutZero: false)) // "0 MB" +Int64.zero.formatted(.byteCount(style: .file, spellsOutZero: true)) // "Zero kB" +Int64.zero.formatted(.byteCount(style: .file, spellsOutZero: false)) // "0 bytes" + +Int64(1_000).formatted(.byteCount(style: .file, includesActualByteCount: true)) // "1 kB (1,000 bytes)" + +// MARK: - Measurement + +let terabyteMeasurement = Measurement(value: 1, unit: UnitInformationStorage.terabytes) + +terabyteMeasurement.formatted(.byteCount(style: .binary)) // "931.32 GB" +terabyteMeasurement.formatted(.byteCount(style: .decimal)) // "1 TB" +terabyteMeasurement.formatted(.byteCount(style: .file)) // "1 TB" +terabyteMeasurement.formatted(.byteCount(style: .memory)) // "931.32 GB" + +terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes" +terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes" +terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .kb)) // "976,562,500 kB" +terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .mb)) // "953,674.3 MB" +terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .gb)) // "931.32 GB" +terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .tb)) // "0.91 TB" +terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .pb)) // "0 PB" +terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .zb)) // "0 PB" +terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .ybOrHigher)) // 0 PB + +let kilobyteMeasurement = Measurement(value: 1, unit: UnitInformationStorage.kilobytes) +let megabyteMeasurement = Measurement(value: 1, unit: UnitInformationStorage.megabytes) + +kilobyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 kB" +megabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 MB" + +let zeroMeasurement = Measurement(value: 0, unit: UnitInformationStorage.bytes) + +zeroMeasurement.formatted(.byteCount(style: .file, spellsOutZero: true)) // "Zero kB" +zeroMeasurement.formatted(.byteCount(style: .file, spellsOutZero: false)) // "0 bytes" + +megabyteMeasurement.formatted(.byteCount(style: .file, includesActualByteCount: true)) // "1 MB (1,000,000 bytes)" -terabyte.formatted(.byteCount(style: .file, allowedUnits: .mb, includesActualByteCount: true)) // "1,000,000 MB (1,000,000,000,000 bytes)" -terabyte.formatted(.byteCount(style: .file, allowedUnits: .mb, includesActualByteCount: false)) // "1,000,000 MB" -terabyte.formatted(.byteCount(style: .file, allowedUnits: .all, spellsOutZero: true, includesActualByteCount: true)) // "1 TB (1,000,000,000,000 bytes)" ``` ### Customizing the locale You can set the locale by appending the `locale()` method onto the end of the format style. -``` +``` Swift let franceLocale = Locale(identifier: "fr_FR") terabyte.formatted(.byteCount(style: .binary).locale(franceLocale)) // "931,32 Go" @@ -83,22 +133,18 @@ terabyte.formatted(.byteCount(style: .decimal).locale(franceLocale)) // "1To" terabyte.formatted(.byteCount(style: .file).locale(franceLocale)) // "1To" terabyte.formatted(.byteCount(style: .memory).locale(franceLocale)) // "931,32 Go" -let inFrench = ByteCountFormatStyle( - style: .memory, - allowedUnits: .all, - spellsOutZero: false, - includesActualByteCount: true, - locale: Locale(identifier: "fr_FR") -) - -inFrench.format(terabyte) // "931,32 Go (1 000 000 000 000 octets)" -terabyte.formatted(inFrench) // "931,32 Go (1 000 000 000 000 octets)" +terabyteMeasurement.formatted(.byteCount(style: .binary).locale(franceLocale)) // "931,32 Go" +terabyteMeasurement.formatted(.byteCount(style: .decimal).locale(franceLocale)) // "1To" +terabyteMeasurement.formatted(.byteCount(style: .file).locale(franceLocale)) // "1To" +terabyteMeasurement.formatted(.byteCount(style: .memory).locale(franceLocale)) // "931,32 Go" ```

Attributed String Output

You can output attributed strings by appending the `attributed` method onto the end of the format style. -``` +``` Swift terabyte.formatted(.byteCount(style: .binary).attributed) + +terabyteMeasurement.formatted(.byteCount(style: .binary).attributed) ``` \ No newline at end of file diff --git a/sections/datetime.md b/sections/datetime.md index bd0c000..3dd019a 100644 --- a/sections/datetime.md +++ b/sections/datetime.md @@ -86,11 +86,25 @@ The order of the symbols in the final string are controlled by the date's `Local {{< /hint >}} -### Customization +## Customization Each symbol has customization options. -#### Day +- [Day](#day) +- [Day of Year](#day-of-year) +- [Era](#era) +- [Hour](#hour) +- [Minute](#minute) +- [Month](#month) +- [Quarter](#quarter) +- [Second](#second) +- [Fractional Second](#fractional-second) +- [Time Zone](#time-zone) +- [Week](#week) +- [Weekday](#weekday) +- [Year](#year) + +### Day ``` twosday.formatted(.dateTime.day(.twoDigits)) // "22" @@ -106,7 +120,7 @@ twosday.formatted(Date.FormatStyle().day(.julianModified())) // "2459633" twosday.formatted(Date.FormatStyle().day(.julianModified(minimumLength: 8))) // "02459633" ``` -#### Day of Year +### Day of Year ``` twosday.formatted(.dateTime.dayOfYear(.defaultDigits)) // "53" @@ -117,7 +131,7 @@ twosday.formatted(Date.FormatStyle().dayOfYear(.defaultDigits)) // "53" twosday.formatted(Date.FormatStyle().dayOfYear(.threeDigits)) // "053" twosday.formatted(Date.FormatStyle().dayOfYear(.twoDigits)) // "53" ``` -#### Era +### Era ``` twosday.formatted(.dateTime.era(.abbreviated)) // "AD" @@ -129,7 +143,7 @@ twosday.formatted(Date.FormatStyle().era(.narrow)) // "A" twosday.formatted(Date.FormatStyle().era(.wide)) // "Anno Domini" ``` -#### Hour +### Hour Each of the following methods accepts an `AMPMStyle`. @@ -184,7 +198,7 @@ twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .abbreviated))) // "0 twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .omitted))) // "02" ``` -#### Minute +### Minute ``` twosday.formatted(.dateTime.minute(.twoDigits)) // "22" @@ -194,7 +208,7 @@ twosday.formatted(Date.FormatStyle().minute(.twoDigits)) // "22" twosday.formatted(Date.FormatStyle().minute(.defaultDigits)) // "22" ``` -#### Month +### Month ``` twosday.formatted(.dateTime.month(.defaultDigits)) // "2" @@ -210,7 +224,7 @@ twosday.formatted(Date.FormatStyle().month(.abbreviated)) // "Feb" twosday.formatted(Date.FormatStyle().month(.narrow)) // "F" ``` -#### Quarter +### Quarter ``` twosday.formatted(.dateTime.quarter(.narrow)) // "1" @@ -226,7 +240,7 @@ twosday.formatted(Date.FormatStyle().quarter(.twoDigits)) // "01" twosday.formatted(Date.FormatStyle().quarter(.oneDigit)) // "1" ``` -#### Second +### Second ``` twosday.formatted(.dateTime.second(.twoDigits)) // "22" @@ -236,7 +250,7 @@ twosday.formatted(Date.FormatStyle().second(.twoDigits)) // "22" twosday.formatted(Date.FormatStyle().second(.defaultDigits)) // "22" ``` -#### Fractional Second +### Fractional Second ``` twosday.formatted(Date.FormatStyle().secondFraction(.fractional(2))) // "00" @@ -246,7 +260,7 @@ twosday.formatted(.dateTime.secondFraction(.fractional(2))) // "00" twosday.formatted(.dateTime.secondFraction(.milliseconds(1))) // "8542000" ``` -#### Time Zone +### Time Zone ``` twosday.formatted(.dateTime.timeZone(.exemplarLocation)) // "Edmonton" @@ -276,7 +290,7 @@ twosday.formatted(Date.FormatStyle().timeZone(.localizedGMT(.short))) // "GMT-7" twosday.formatted(Date.FormatStyle().timeZone(.localizedGMT(.long))) // "GMT-07:00" ``` -#### Week +### Week ``` twosday.formatted(.dateTime.week(.defaultDigits)) // "9" @@ -288,7 +302,7 @@ twosday.formatted(Date.FormatStyle().week(.twoDigits)) // "09" twosday.formatted(Date.FormatStyle().week(.weekOfMonth)) // "9" ``` -#### Weekday +### Weekday ``` twosday.formatted(.dateTime.weekday(.abbreviated)) // "Tue" @@ -306,7 +320,7 @@ twosday.formatted(Date.FormatStyle().weekday(.wide)) // "Tuesday" twosday.formatted(Date.FormatStyle().weekday(.narrow)) // "T" ``` -#### Year +### Year ``` twosday.formatted(.dateTime.year(.twoDigits)) // "22" @@ -326,7 +340,7 @@ twosday.formatted(Date.FormatStyle().year(.relatedGregorian())) // "2022" twosday.formatted(Date.FormatStyle().year(.relatedGregorian(minimumLength: 2))) // "22" ``` -### Setting the Locale +## Setting the Locale You can set the Locale by appending the `.locale()` method onto the last Symbol. @@ -344,7 +358,7 @@ twosday.formatted(.dateTime.attributed) twosday.formatted(Date.FormatStyle().attributed) ``` -### Parsing Dates From Strings +## Parsing Dates From Strings `Date.FormatStyle` conforms to `ParseableFormatStyle` and can be set up to parse `Date` objects from a `String`. diff --git a/sections/duration.md b/sections/duration.md new file mode 100644 index 0000000..7b58184 --- /dev/null +++ b/sections/duration.md @@ -0,0 +1,296 @@ +--- +sitemap_ignore: true +--- + +There are two format styles available for the `Duration` type: + +

Time Style {{< xcode14-badge >}}

+ +Time format style allows you to output your `Duration` as a combination of hours, minutes, and seconds, which is set by the `pattern` parameter on the style. + +You can either initialize a new instance of `Duration.TimeFormatStyle`, or use the `.time(pattern:)` extension on FormatStyle. + +``` Swift +Duration.seconds(1_000).formatted() // "0:16:40" +Duration.seconds(1_000).formatted(.time(pattern: .hourMinute)) // "0:17" +Duration.TimeFormatStyle(pattern: .hourMinute).format(Duration.seconds(1_000)) // "0:17" + +``` + +{{< hint type=note >}} + +You'll notice that this is the default style when calling `.formatted()` on any `Duration` without specifying a style. + +{{< /hint >}} + +### Available Patterns + +There are six total patterns available, broken out into two types: properties and methods. + +The properties are the "defaults" for each type of pattern and will choose the configuration for you. + +| Pattern | Description | +| ----------------- | --------------------------------------------------------------- | +| `.hourMinute` | Displays the hour and minute values for the Duration | +| `.hourMinuteSecond` | Displays the hour, minute, and second values for the Duration | +| `.minuteSecond` | Displays the minute and second values for the Duration | + +``` Swift +Duration.seconds(1_000).formatted(.time(pattern: .hourMinute)) // "0:17" +Duration.seconds(1_000).formatted(.time(pattern: .hourMinuteSecond)) // "0:16:40" +Duration.seconds(1_000).formatted(.time(pattern: .minuteSecond)) // "16:40" +``` + +The method variants allow you to fully customize the behaviour of each of the patterns: + +| Pattern | Description | +| --------------------- | ------------------------------------------------------------- | +| `.hourMinute()` | Displays the hour and minute values for the Duration | +| `.hourMinuteSecond()` | Displays the hour, minute, and second values for the Duration | +| `.minuteSecond()` | Displays the minute and second values for the Duration | + +The following are the parameter options for `hourMinute`: + +| Parameter | Description | +| ----------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `padHourToLength` | Pads the output to include that number of digits. | +| `roundSeconds` | The rounding rule to use on the seconds value ([See Rounding for all options](#numbers-rounding)). Defaults to `.toNearestOrEven` | + +``` Swift +Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 3, roundSeconds: .awayFromZero))) // "000:17" +Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .down))) // "000:16" +Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .toNearestOrAwayFromZero))) // "0:17" +Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .toNearestOrEven))) // "0:17" +Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .towardZero))) // "0:16" +Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .up))) // "0:17" +``` +The following are the parameter options for both `hourMinuteSecond` and 'minuteSecond`: + +| Parameter | Description | +| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `padHourToLength` | Pads the output to include that number of digits. | +| `fractionalSecondsLength` | The number of digits to include when displaying fractional seconds. | +| `roundFractionalSeconds` | The rounding rule to use on the seconds value ([See Rounding for all options](#numbers-rounding)). Defaults to `.toNearestOrEven` | + +``` Swift +Duration.seconds(1_000).formatted( + .time( + pattern: .hourMinuteSecond( + padHourToLength: 3, + fractionalSecondsLength: 3, + roundFractionalSeconds: .awayFromZero + ) + ) +) // "000:16:40.000" + +Duration.seconds(1_000).formatted( + .time( + pattern: .minuteSecond( + padMinuteToLength: 3, + fractionalSecondsLength: 3, + roundFractionalSeconds: .awayFromZero + ) + ) +) // "016:40.000" +``` + +### Setting the Locale + +You can set the `Locale` of the style by adding the `.locale()` method to your `.time(pattern:)` method, or including the locale parameter in the `Duration.TimeFormatStyle` initializer. + +``` Swift +Duration.seconds(1_000).formatted(.time(pattern: .hourMinute).locale(Locale(identifier: "fr_FR"))) // "0:17" + +let frenchTimeFormatStyle = Duration.TimeFormatStyle(pattern: .minuteSecond, locale: Locale(identifier: "fr_FR")) + +frenchTimeFormatStyle.format(Duration.seconds(1_000)) // "16:40" +``` + +### Attributed String Output + +You can output `AttributedString` values by adding the `.attributed` method. + +``` Swift +Duration.seconds(1_000).formatted(.time(pattern: .hourMinuteSecond).attributed) +``` + +

Unit Style {{< xcode14-badge >}}

+ +The units style allows you to declare and customize the specific units to display for your duration. + +You can either initialize a new instance of `Duration.UnitsFormatStyle`, or use the `.units()` extension on FormatStyle. + +``` Swift +Duration.seconds(100).formatted(.units()) // "1 min, 40 sec" +Duration.UnitsFormatStyle(allowedUnits: [.hours, .minutes, .seconds], width: .abbreviated).format(.seconds(100)) // "1 min, 40 sec" +``` + +In both cases, there are two variants the initializer or style method. + +| Parameter | Description | +| ------------------ | ----------------------------------------------------------- | +| `.allowed` | A Set stating the possible units to display. | +| `width` | The "width" of the output string. | +| `maximumUnitCount` | The maximum number of time units to display. | +| `zeroValueUnits` | How to handle units with zero values. | +| `valueLength` | How to pad or truncate values for display. | +| `fractionalPart` | How to display fractional values if the unit can't be shown | + +| Parameter | Description | +| ------------------- | ----------------------------------------------------------- | +| `.allowed` | A Set stating the possible units to display. | +| `width` | The "width" of the output string. | +| `maximumUnitCount` | The maximum number of time units to display. | +| `zeroValueUnits` | How to handle units with zero values. | +| `valueLengthLimits` | How to pad or truncate values for display. | +| `fractionalPart` | How to display fractional values if the unit can't be shown | + +If you can't spot it at a glance, the difference is the `valueLength`/`valueLengthLimits` parameter which configures how each unit is padded or truncated. `valueLength` accepts an optional `Integer`, while `valueLengthLimits` takes an optional `ValueRange` value. + +--- + +#### allowed + +This parameter, passed in as a `Set< Duration.UnitsFormatStyle.Unit>`, declares which units to use in the final display. By default, if a unit has a value of "0", then it will be omitted from the final string. You can override this functionality with the `zeroValueUnits` parameter. + +The following units are available (from smallest to largest): + +- `.nanoseconds` +- `.microseconds` +- `.miliseconds` +- `.seconds` +- `.minutes` +- `.hours` +- `.days` +- `.weeks` + +``` Swift +Duration.milliseconds(500).formatted(.units(allowed: [.nanoseconds])) // "500,000,000 ns" +Duration.milliseconds(500).formatted(.units(allowed: [.microseconds])) // "500,000 μs" +Duration.milliseconds(500).formatted(.units(allowed: [.milliseconds])) // "500 ms" +Duration.milliseconds(500).formatted(.units(allowed: [.seconds])) // "0 sec" +Duration.milliseconds(500).formatted(.units(allowed: [.minutes])) // "0 min" +Duration.milliseconds(500).formatted(.units(allowed: [.hours])) // "0 hr" +Duration.milliseconds(500).formatted(.units(allowed: [.days])) // "0 days" +Duration.milliseconds(500).formatted(.units(allowed: [.weeks])) // "0 wks" + +Duration.seconds(1_000_000.00123).formatted( + .units( + allowed: [ + .nanoseconds, + .milliseconds, + .milliseconds, + .seconds, + .minutes, + .hours, + .days, + .weeks + ] + ) +) // "1 wk, 4 days, 13 hr, 46 min, 40 sec, 1 ms, 230,000 ns" + +Duration.seconds(1).formatted( + .units( + allowed: [ + .nanoseconds, + .milliseconds, + .milliseconds, + .seconds, + .minutes, + .hours, + .days, + .weeks + ] + ) +) // "1 sec" +``` + +--- + +#### width + +This parameter controls how verbose/wordy and condensed the string output is. + +| Parameter | Description | +| ----------------------- | ----------------------------------------------------------------------------- | +| `.wide` | Shows the full unit name, eg. "3 hours" | +| `.abbreviated` | Shows the shortened version of the unit "3 hrs" | +| `.condensedAbbreviated` | Shows the shortened version, without a space between the value and unit "3hr" | +| `.narrow` | Shows the shortest possible unit name | + + +``` Swift +Duration.seconds(100).formatted(.units(width: .abbreviated)) // "1 min, 40 sec" +Duration.seconds(100).formatted(.units(width: .condensedAbbreviated)) // "1 min,40 sec" +Duration.seconds(100).formatted(.units(width: .narrow)) // "1m 40s" +Duration.seconds(100).formatted(.units(width: .wide)) // "1 minute, 40 seconds" +``` + +--- + +#### maximumUnitCount + +Controls the number of units to display in the final string. This works in tandem with the `.units` property. + +``` Swift +Duration.seconds(10000).formatted(.units(maximumUnitCount: 1)) // "3 hr" +Duration.seconds(10000).formatted(.units(maximumUnitCount: 2)) // "2 hr, 47 min" +Duration.seconds(10000).formatted(.units(maximumUnitCount: 3)) // "2 hr, 46 min, 40 sec" +``` + +--- + +#### zeroValueUnits + +Controls how units with a value of zero are shown or not, and if set to `.show`, how many zeros to use in their display. + +``` Swift +Duration.seconds(100).formatted(.units(zeroValueUnits: .hide)) // "1 min, 40 sec" +Duration.seconds(100).formatted(.units(zeroValueUnits: .show(length: 1))) // "0 hr, 1 min, 40 sec" +Duration.seconds(100).formatted(.units(zeroValueUnits: .show(length: 3))) // "000 hr, 001 min, 040 sec" +``` + +--- + +#### valueLength and valueLengthLimits + +Controls how many digits of each unit to display. `valueLength` accepts a fixed integer, while `valueLengthLimits` accepts a range. + +``` Swift +Duration.seconds(1_000).formatted(.units(valueLength: 1)) // "16 min, 40 sec" +Duration.seconds(1_000).formatted(.units(valueLength: 3)) // "016 min, 040 sec" + +Duration.seconds(10_000).formatted(.units(valueLengthLimits: 1...)) // This is a bug (Feedback FB10607619) +Duration.seconds(10_000).formatted(.units(valueLengthLimits: ...3)) // "2 hr, 46 min, 40 sec" +Duration.seconds(100).formatted(.units(valueLengthLimits: 2 ... 3)) // "01 min, 40 sec" +``` + +{{< hint type=warning >}} +As of Xcode 14.0 beta 3 (14A5270f), there's a bug when you use a `ValueRange` of `1...`. In the above example, the output is "16.0 measure-unit/duration-minute, 40.0 measure-unit/duration-second” instead of “16 min, 40 sec”. Feedback FB10607619 has been submitted. +{{< /hint >}} + +--- + +#### fractionalPart + +Controls how fractional values are handled for display. + +``` Swift +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide)) // "10 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .up))) // "11 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .towardZero))) // "10 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .toNearestOrEven))) // "10 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .toNearestOrAwayFromZero))) // "10 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .down))) // "10 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .awayFromZero))) // "11 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 0))) // "10 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 5))) // "10.00230 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .up))) // "10.003 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .towardZero))) // "10.002 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .toNearestOrEven))) // "10.002 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .toNearestOrAwayFromZero))) // "10.002 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .down))) // "10.002 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .awayFromZero))) // "10.003 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, increment: 1))) // "10.000 sec" +Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, increment: 0.001))) // "10.002 sec" +``` diff --git a/sections/measurement.md b/sections/measurement.md index ed24355..2d0315d 100644 --- a/sections/measurement.md +++ b/sections/measurement.md @@ -1,6 +1,7 @@ --- sitemap_ignore: true --- + [If you're curious, here's Apple's list of supported units.](https://developer.apple.com/documentation/foundation/dimension) Regardless of which unit you're using, the format style has three possible widths: @@ -31,18 +32,6 @@ gForce.formatted(.measurement(width: .narrow).locale(franceLocale)) // "1G" gForce.formatted(.measurement(width: .abbreviated).locale(franceLocale)) // "1 force g") ``` -### Setting the locale - -You can set the locale by appending the `locale()` method onto the end of the format style. - -``` -let franceLocale = Locale(identifier: "fr_FR") - -gForce.formatted(.measurement(width: .wide).locale(franceLocale)) // "1 fois l’accélération de pesanteur terrestre" -gForce.formatted(.measurement(width: .narrow).locale(franceLocale)) // "1G" -gForce.formatted(.measurement(width: .abbreviated).locale(franceLocale)) // "1 force g" -``` - ### Initializing a Measurement Style Due to the associated types required by the Measurement API, initializing a measurement style requires you to set the associated type. @@ -64,4 +53,11 @@ You can output Attributed Strings by appending the `attributed` method onto the ``` gForce.formatted(.measurement(width: .wide).attributed) -``` \ No newline at end of file +``` + +{{< hint type=note >}} +In Xcode 14, there is the `Measurement.FormatStyle.ByteCount` format style available to you that lets you easily format byte counts + +See the byte count format style section for all of the details + +{{< /hint >}} \ No newline at end of file diff --git a/sections/quiz.md b/sections/quiz.md index ba5379d..9fe7cdf 100644 --- a/sections/quiz.md +++ b/sections/quiz.md @@ -11,30 +11,38 @@ If you have a specific need and aren't quite sure how to execute it, follow alon {{< expand "Number" "⇅" >}} +{{< xcode13-badge >}} + All built-in number types are supported by these format styles (`Float`, `Double`, `Int`, and `Decimal`) {{< expand "And I just want to customize the output" "⇅" >}} -See Number Style +{{< xcode13-badge >}} + +See Number Style | Property | Description | | ----------------------------------------------------------- | ------------------------------------------------------------- | -| [Rounding](#numbers-rounding) | Customize the rounding behaviour | -| [Sign](#numbers-sign) | Do you want to show or hide the + or - sign? | -| [Decimal Separator](#numbers-decimal-separator) | Do you want to show or hide the decimal separator | -| [Grouping](#numbers-grouping) | How do you want the thousands numbers to be grouped | -| [Precision](#numbers-prescision) | How many fractional or significant digits do you want to show | -| [Notation](#numbers-notation) | Enable scientific or compact notation | -| [Scale](#numbers-scale) | Scale the number up or down before display | -| [Locale](#numbers-locale) | Set the `Locale` for one output | -| [Compositing](#numbers-compositing) | Mix and match any and all of the above | -| [AttributedString output](#numbers-attributed-string-output) | Output an `AttributedString` | +| [Rounding](/number-styles/#numbers-rounding) | Customize the rounding behaviour | +| [Sign](/number-styles/#numbers-sign) | Do you want to show or hide the + or - sign? | +| [Decimal Separator](/number-styles/#numbers-decimal-separator) | Do you want to show or hide the decimal separator | +| [Grouping](/number-styles/#numbers-grouping) | How do you want the thousands numbers to be grouped | +| [Precision](/number-styles/#numbers-prescision) | How many fractional or significant digits do you want to show | +| [Notation](/number-styles/#numbers-notation) | Enable scientific or compact notation | +| [Scale](/number-styles/#numbers-scale) | Scale the number up or down before display | +| [Locale](/number-styles/#numbers-locale) | Set the `Locale` for one output | +| [Compositing](/number-styles/#numbers-compositing) | Mix and match any and all of the above | +| [AttributedString output](/number-styles/#numbers-attributed-string-output) | Output an `AttributedString` | {{< /expand >}} {{< expand "And it's actually a Percentage" "⇅" >}} -See Percent Style +{{< xcode13-badge >}} + +See Percent Style + +{{< xcode13-badge >}} {{< hint type=important >}} When formatting a floating point number (Double/Float/Decimal), `1.0` is "100%", while formatting the integer `1` is only "1%" @@ -42,16 +50,16 @@ When formatting a floating point number (Double/Float/Decimal), `1.0` is "100%", | Property | Description | | ------------------------------------------------------------ | ------------------------------------------------------------- | -| [Rounding](#percent-rounding) | Customize the rounding behaviour | -| [Sign](#percent-sign) | Do you want to show or hide the + or - sign? | -| [Decimal Separator](#percent-decimal-separator) | Do you want to show or hide the decimal separator | -| [Grouping](#percent-grouping) | How do you want the thousands numbers to be grouped | -| [Precision](#percent-prescision) | How many fractional or significant digits do you want to show | -| [Notation](#percent-notation) | Enable scientific or compact notation | -| [Scale](#percent-scale) | Scale the number up or down before display | -| [Locale](#percent-locale) | Set the `Locale` for one output | -| [Compositing](#percent-compositing) | Mix and match any and all of the above | -| [AttributedString output](#percent-attributed-string-output) | Output an `AttributedString` | +| [Rounding](/number-styles/#percent-rounding) | Customize the rounding behaviour | +| [Sign](/number-styles/#percent-sign) | Do you want to show or hide the + or - sign? | +| [Decimal Separator](/number-styles/#percent-decimal-separator) | Do you want to show or hide the decimal separator | +| [Grouping](/number-styles/#percent-grouping) | How do you want the thousands numbers to be grouped | +| [Precision](/number-styles/#percent-prescision) | How many fractional or significant digits do you want to show | +| [Notation](/number-styles/#percent-notation) | Enable scientific or compact notation | +| [Scale](/number-styles/#percent-scale) | Scale the number up or down before display | +| [Locale](/number-styles/#percent-locale) | Set the `Locale` for one output | +| [Compositing](/number-styles/#percent-compositing) | Mix and match any and all of the above | +| [AttributedString output](/number-styles/#percent-attributed-string-output) | Output an `AttributedString` | {{< hint type=important >}} The various percent formatters handle integers and floating point values differently. @@ -65,7 +73,11 @@ The various percent formatters handle integers and floating point values differe {{< expand "And it's actually Money" "⇅" >}} -See Currency Style +{{< xcode13-badge >}} + +See Currency Style + +{{< xcode13-badge >}} Currency requires you to set the ISO 4217 code for the target currency to output. @@ -77,16 +89,16 @@ Floating point types shouldn't be used to store or do calculations on numbers th | Property | Description | | ------------------------------------------------------------- | ------------------------------------------------------------- | -| [Rounding](#currency-rounding) | Customize the rounding behaviour | -| [Sign](#currency-sign) | Do you want to show or hide the + or - sign? | -| [Decimal Separator](#currency-decimal-separator) | Do you want to show or hide the decimal separator | -| [Grouping](#currency-grouping) | How do you want the thousands numbers to be grouped | -| [Precision](#currency-prescision) | How many fractional or significant digits do you want to show | -| [Presentation](#currency-presentation) | Controls the style of the displayed currency | -| [Scale](#currency-scale) | Scale the number up or down before display | -| [Locale](#currency-locale) | Set the `Locale` for one output | -| [Compositing](#currency-compositing) | Mix and match any and all of the above | -| [AttributedString output](#currency-attributed-string-output) | Output an `AttributedString` | +| [Rounding](/number-styles/#currency-rounding) | Customize the rounding behaviour | +| [Sign](/number-styles/#currency-sign) | Do you want to show or hide the + or - sign? | +| [Decimal Separator](/number-styles/#currency-decimal-separator) | Do you want to show or hide the decimal separator | +| [Grouping](/number-styles/#currency-grouping) | How do you want the thousands numbers to be grouped | +| [Precision](/number-styles/#currency-prescision) | How many fractional or significant digits do you want to show | +| [Presentation](/number-styles/#currency-presentation) | Controls the style of the displayed currency | +| [Scale](/number-styles/#currency-scale) | Scale the number up or down before display | +| [Locale](/number-styles/#currency-locale) | Set the `Locale` for one output | +| [Compositing](/number-styles/#currency-compositing) | Mix and match any and all of the above | +| [AttributedString output](/number-styles/#currency-attributed-string-output) | Output an `AttributedString` | {{< /expand >}} @@ -94,45 +106,57 @@ Floating point types shouldn't be used to store or do calculations on numbers th {{< expand "Single Date" "⇅" >}} +{{< xcode13-badge >}} {{< xcode14-badge >}} + {{< expand "Just the full date and/or time" "⇅" >}} +{{< xcode13-badge >}} + Apple added a date extension onto `Date` that lets you output the date and/or time in certain formats. -See Date and Time Style +See Date and Time Style {{< /expand >}} {{< expand "I want to mix and match date components" "⇅" >}} +{{< xcode13-badge >}} + The `dateTime()` format style lets you compose a date string by choosing which date components you'd like to include. Each component can be further customized passing in some options. -See composting using .dateTime +See composting using .dateTime {{< /expand >}} {{< expand "I want a standard ISO8601" "⇅" >}} +{{< xcode13-badge >}} + Output date strings that conform to the ISO 8601 standard. -See ISO8601 Style +See ISO8601 Style {{< /expand >}} {{< expand "Exactly how far away it is" "⇅" >}} +{{< xcode13-badge >}} + Relative date formatting outputs a plain language string that describes how far away that date is to right now. You can also customize which date components are used in the output. This is similar to the components format style on date ranges, but that one uses date ranges instead of assuming the current date and time. -See Relative Style +See Relative Style {{< /expand >}} -{{< expand "An un-localized date in a very specific format" "⇅" >}} +{{< expand "An date in a very specific format" "⇅" >}} -If you need to output a very rigid date string that follows an exact format, you can use the verbatim format style to do that. +{{< xcode13-badge >}} {{< xcode14-badge >}} -See Verbatim Style +If you need to output a very rigid date string that follows an exact format, you can use the verbatim format style to do that. Updated in Xcode 14. + +See Verbatim Style {{< /expand >}} @@ -140,19 +164,23 @@ See Verbatim Style {{< expand "Range of Dates" "⇅" >}} +{{< xcode13-badge >}} + {{< expand "Just show the earliest and latest dates" "⇅" >}} The interval formatter just shows the earliest and latest dates in a range. -See Interval Style +See Interval Style {{< /expand >}} {{< expand "How long as passed between the earliest and latest dates" "⇅" >}} +{{< xcode13-badge >}} + Use the components format style if you want a plain language representation of the distance between the earliest and latest dates in a date range. This is similar to the relative date format style (but for date ranges). -See Components Style +See Components Style {{< /expand >}} @@ -160,41 +188,92 @@ See Components Style {{< expand "An Array/List" "⇅" >}} +{{< xcode13-badge >}} + The list format style lets you take an array of data objects, and output them as a list on screen. You can also customize how each object is formatted within the list. -See List style +See List style {{< /expand >}} -{{< expand "Some Measurement" "⇅" >}} +{{< expand "Measurement" "⇅" >}} + +{{< xcode13-badge >}} {{< xcode14-badge >}} -Any unit that's supported by the `Measurement` API can be formatted with many different customization options. +Any unit that's supported by the `Measurement` API can be formatted with many different customization options. Updated in Xcode 14. -See Measurement Style +See Measurement Style {{< /expand >}} {{< expand "Person's Name" "⇅" >}} +{{< xcode13-badge >}} + A person's name is tricky to localize correctly, the PersonNameComponents format style can handle the complexities of localization for you. -See Person Name Components +See Person Name Components {{< /expand >}} {{< expand "A number of bytes" "⇅" >}} +{{< xcode13-badge >}} + Easily display how many gigabytes that byte count is. -See Byte Count Style +See Byte Count Style + +{{< /expand >}} + +{{< expand "Duration" "⇅" >}} + +{{< xcode14-badge >}} + +Easily format the `Duration` type. + +See Duration Style + +{{< expand "Time Style" "⇅" >}} + +{{< xcode14-badge >}} + +Shows the `Duration` represented in a combination of hours, minutes, and seconds. + +See Duration Style + +{{< /expand >}} + +{{< expand "Units Style" "⇅" >}} + +{{< xcode14-badge >}} + +Shows the `Duration` using a specific set of units. + +See Duration Style + +{{< /expand >}} + +{{< /expand >}} + + +{{< expand "URL" "⇅" >}} + +{{< xcode14-badge >}} + +Format your Universal Resource Locator. + +See URL Style {{< /expand >}} {{< expand "Need for something custom" "⇅" >}} +{{< xcode13-badge >}} + You can easily bend the `FormatStyle` protocol to your will and arbitrarily convert any type into any type. -See Custom FormatStyle (with locale and attributed string support) +See Custom FormatStyle (with locale and attributed string support) {{< /expand >}} @@ -206,31 +285,47 @@ Several of the included format styles also conform to `ParseableFormatStyle`, a {{< expand "Parsing Dates" "⇅" >}} +{{< xcode13-badge >}} + By setting up either a `Date.FormatStyle` or `Date.ISO8601FormatStyle` with your date structure, you can parse dates easily. -[See Parsing Dates](/#parsing-dates-from-strings) +[See Parsing Dates](/date-styles/#parsing-dates-from-strings) -[See Parsing ISO8601 Dates](/#parsing-iso8601-dates-from-strings) +[See Parsing ISO8601 Dates](/date-styles/#parsing-iso8601-dates-from-strings) {{< /expand >}} {{< expand "Parsing Decimal Numbers" "⇅" >}} +{{< xcode13-badge >}} + You can parse Decimals, Percentages, or Currency values into Decimals. -[See Parsing Decimals](/#parsing-decimals-from-strings) +[See Parsing Decimals](/number-styles/#parsing-decimals-from-strings) -[See Parsing Decimal Percentages](/#parsing-percentages-from-strings) +[See Parsing Decimal Percentages](/number-styles/#parsing-percentages-from-strings) -[See Parsing Decimal Currencies](/#parsing-currencies-from-strings) +[See Parsing Decimal Currencies](/number-styles/#parsing-currencies-from-strings) {{< /expand >}} {{< expand "Parsing Names" "⇅" >}} +{{< xcode13-badge >}} + Generally useful for parsing names from string respecting a `Locale`, it's easy to parse names. -[See Parsing Names](/#parsing-names-from-strings) +[See Parsing Names](/person-name-style/#parsing-names-from-strings) + +{{< /expand >}} + +{{< expand "Parsing URLs" "⇅" >}} + +{{< xcode14-badge >}} + +Parse a URL string into a URL value. + +[See Parsing URLs](/url-style/#parsing-urls) {{< /expand >}} diff --git a/sections/requirements.md b/sections/requirements.md index 014ab02..a56b04d 100644 --- a/sections/requirements.md +++ b/sections/requirements.md @@ -3,11 +3,24 @@ sitemap_ignore: true --- ## Minimum Requirements -- Xcode 13.0+ - -- iOS 15.0+ -- iPadOS 15.0+ -- macOS 12.0+ -- Mac Catalyst 15.0+ -- tvOS 15.0+ -- watchOS 8.0+ +

{{< xcode13-badge >}}

+ +This badge represents a style that is available on any platform that is built by Xcode 13 and above (iOS 15.0+, iPadOS 15.0+, Mac Catalyst 15.0+, tvOS 15.0+, watchOS 8.0+, macOS 12.0+): + +- Number styles (including currency and percent styles) +- Date styles (including iso8601, relative, and verbatim styles) +- Date range styles (interval, and components) +- Measurement styles +- List styles +- Person name styles +- Byte count styles + +

{{< xcode14-badge >}}

+ +This badge represents a style that has been updated, or is only available only, on platforms built by Xcode 14 and above (iOS 16.0+, iPadOS 16.0+, Mac Catalyst 16.0+, tvOS 16.0+, watchOS 9.0+, macOS 13.0+): + +- (Updated) Byte count style +- (Updated) Measurement style +- Duration style +- URL style +- (Updated) Verbatim date style diff --git a/sections/swiftui.md b/sections/swiftui.md index 8c62fde..d09d2d3 100644 --- a/sections/swiftui.md +++ b/sections/swiftui.md @@ -1,8 +1,6 @@ --- sitemap_ignore: true --- -## SwiftUI Integration - {{< hint type=tip title=TL;DR >}} Never write `Text("\()")` again. Just pass in the right `FormatStyle` diff --git a/sections/url.md b/sections/url.md new file mode 100644 index 0000000..cc7df7c --- /dev/null +++ b/sections/url.md @@ -0,0 +1,187 @@ +--- +sitemap_ignore: true +--- + +## Available Options + +| Parameter | Description | +| ------------------------- | ------------------------------------------------- | +| `.always` | Always display this value | +| `.never` | Omit this value | +| `.omitIfHTTPFamily` | Omit this value if the URL is `http` or `https` | +| `.displayWhen(_:matches)` | Only display this value when the condition is met | +| `.omitWhen(:matches)` | Omit this value when the condition is met | + +{{< hint type=important >}} + +Brushing up on the [URLComponents documentation](https://developer.apple.com/documentation/foundation/urlcomponents/) can be very helpful to know the use cases for each individual component. + +{{< /hint >}} + +``` Swift +let appleURL = URL(string: "https://apple.com")! +appleURL.formatted() // "https://apple.com" +appleURL.formatted(.url) // "https://apple.com" +appleURL.formatted(.url.locale(Locale(identifier: "fr_FR"))) // "https://apple.com" + +var httpComponents = URLComponents(url: appleURL, resolvingAgainstBaseURL: false)! +httpComponents.scheme = "https" +httpComponents.user = "jAppleseed" +httpComponents.password = "Test1234" +httpComponents.host = "apple.com" +httpComponents.port = 80 +httpComponents.path = "/macbook-pro" +httpComponents.query = "get-free" +httpComponents.fragment = "someFragmentOfSomething" + +let complexURL = httpComponents.url! +let everythingStyle = URL.FormatStyle( + scheme: .always, + user: .always, + password: .always, + host: .always, + port: .always, + path: .always, + query: .always, + fragment: .always +) + +everythingStyle.format(complexURL) // "https://jAppleseed:Test1234@apple.com:80/macbook-pro?get-free#someFragmentOfSomething" + +let omitStyle = URL.FormatStyle( + scheme: .omitIfHTTPFamily, + user: .omitIfHTTPFamily, + password: .omitIfHTTPFamily, + host: .omitIfHTTPFamily, + port: .omitIfHTTPFamily, + path: .omitIfHTTPFamily, + query: .omitIfHTTPFamily, + fragment: .omitIfHTTPFamily +) + +var httpsComponent = httpComponents +httpsComponent.scheme = "https" +let httpsURL = httpsComponent.url! + +var ftpComponents = httpComponents +ftpComponents.scheme = "ftp" +let ftpURL = ftpComponents.url! + +omitStyle.format(complexURL) // "" +omitStyle.format(httpsURL) // "" +omitStyle.format(ftpURL) // "ftp://jAppleseed@apple.com:80/macbook-pro?get-free#someFragmentOfSomething" + +let localhostURL = URL(string: "https://localhost:80/macbook-pro")! + +let displayWhen = URL.FormatStyle( + scheme: .always, + user: .never, + password: .never, + host: .displayWhen(.host, matches: ["localhost"]), + port: .always, + path: .always, + query: .never, + fragment: .never +) + +displayWhen.format(complexURL) // "https://:80/macbook-pro" +displayWhen.format(localhostURL) // "https://localhost:80/macbook-pro" + +let omitWhen = URL.FormatStyle( + scheme: .always, + user: .never, + password: .never, + host: .omitWhen(.host, matches: ["localhost"]), + port: .always, + path: .always, + query: .never, + fragment: .never +) + +omitWhen.format(complexURL) // "https://apple.com:80/macbook-pro" +omitWhen.format(localhostURL) // "https://:80/macbook-pro" + +let omitSpecificWhen = URL.FormatStyle( + scheme: .always, + user: .never, + password: .never, + host: .omitSpecificSubdomains(["secret"], includeMultiLevelSubdomains: false), + port: .always, + path: .always, + query: .never, + fragment: .never +) + +var secretAppleURL = URL(string: "https://secret.apple.com/macbook-pro")! + +omitSpecificWhen.format(complexURL) // "https://apple.com:80/macbook-pro" +omitSpecificWhen.format(secretAppleURL) // "https://apple.com/macbook-pro" + +let omitSpecificWhenWhere = URL.FormatStyle( + scheme: .always, + user: .never, + password: .never, + host: .omitSpecificSubdomains(["secret"], includeMultiLevelSubdomains: false, when: .user, matches: ["jAppleseed"]), + port: .always, + path: .always, + query: .never, + fragment: .never +) + +let complexSecretURL = URL(string: "https://jAppleseed:Test1234@secret.apple.com:80/macbook-pro?get-free#someFragmentOfSomething")! + +omitSpecificWhenWhere.format(complexSecretURL) // "https://apple.com:80/macbook-pro" +omitSpecificWhenWhere.format(secretAppleURL) // "https://secret.apple.com/macbook-pro" +``` + +## Parsing URLs + +The venerable URL initializer `URL(string:relativeTo:)` has been available to us since Xcode 10.2. In most cases this will work well for most use cases. + +If you're looking to parse URLs in a structured way, you can use the URL parse strategy on the URL.FormatStyle struct. + +### Available Options + +For each component of the URL (scheme, user, password, host, port, path, query, or fragment) you can configure them with the following options: + +| Parameter | Description | +| ----------------- | ----------------------------------------- | +| `.optional` | Sets the unit as optional | +| `.required` | Sets the unit as required for a valid URL | +| `.defaultValue()` | If missing, uses the default value | + +{{< hint type=note >}} + +By default, only the scheme and the host are set as required. + +{{< /hint >}} + +``` Swift +try URL.FormatStyle.Strategy(port: .defaultValue(80)).parse("http://www.apple.com") // http://www.apple.com:80 +try URL.FormatStyle.Strategy(port: .optional).parse("http://www.apple.com") // http://www.apple.com +try URL.FormatStyle.Strategy(port: .required).parse("http://www.apple.com") // throws an error + +// This returns a valid URL +try URL.FormatStyle.Strategy() + .scheme(.required) + .user(.required) + .password(.required) + .host(.required) + .port(.required) + .path(.required) + .query(.required) + .fragment(.required) + .parse("https://jAppleseed:Test1234@apple.com:80/macbook-pro?get-free#someFragmentOfSomething") + +// This throws an error (the port is missing) +try URL.FormatStyle.Strategy() + .scheme(.required) + .user(.required) + .password(.required) + .host(.required) + .port(.required) + .path(.required) + .query(.required) + .fragment(.required) + .parse("https://jAppleseed:Test1234@apple.com/macbook-pro?get-free#someFragmentOfSomething") +``` \ No newline at end of file diff --git a/sections/verbatimDate.md b/sections/verbatimDate.md index fcaeb87..6f5f92f 100644 --- a/sections/verbatimDate.md +++ b/sections/verbatimDate.md @@ -1,9 +1,15 @@ --- sitemap_ignore: true --- + +## Version Differences + +1. {{< xcode13-badge >}} You use this style by initializing a new instance of `Date.VerbatimFormatStyle` which can be used directly, or passed into a `.formatted` method. +2. {{< xcode14-badge >}} Similar to other date format styles, you can now use the `.formatted(.verbatim(…))` type method. + ### Format Strings -The `Date.VerbatimFormatStyle` accepts a `Date.FormatString` as a parameter inside of it's initializer. Inside of which, you can include any of symbol tokens (with options) along with other regular characters. +The power of the verbatim format style lies in the `Date.FormatString` that is passed in. This struct conforms to the [ExpressibleByStringInterpolation protocol](https://developer.apple.com/documentation/swift/expressiblebystringinterpolation) which allows us mix and match structured values ([known as tokens](#symbol-tokens)) and strings as much as we want. ``` let twosdayDateComponents = DateComponents( @@ -18,24 +24,49 @@ let twosdayDateComponents = DateComponents( let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)! let verbatim = Date.VerbatimFormatStyle( - format: "\(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .twoDigits)", + format: "Today is \(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .twoDigits) otherwise known as \"Twosday\"", timeZone: TimeZone.current, calendar: .current ) -verbatim.format(twosday) // "02:22" -``` - -### Symbol Tokens +verbatim.format(twosday) // "Today is 02:22 otherwise known as "Twosday"" -Similar to the `.dateTime` you have a wide variety of symbols and customization options. +twosday.formatted( + .verbatim( + "Today is \(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .twoDigits) otherwise known as \"Twosday\"", + locale: Locale(identifier: "zh_CN"), + timeZone: .current, + calendar: .current + ) +) // "Today is 02:22 otherwise known as "Twosday"" -{{< hint type=note >}} +``` -Oddly enough, each of these options are thoroughly documented by Apple (this is where the following content comes from) in their header files. Unfortunately, none of this documentation has been added to the official docs online and within Xcode. +### Symbol Tokens -{{< /hint >}} +Every conceivable piece of information inside of a `Date` is available for use. + +- [Era](#era) +- [Year](#year) +- [YearForWeekOfYear](#yearforweekofyear) +- [CyclicYear](#cyclicyear) +- [Quarter](#quarter) +- [Month](#month) +- [Week](#week) +- [Day](#day) +- [DayOfYear](#dayofyear) +- [Weekday](#weekday) +- [DayPeriod](#dayperiod) +- [Minute](#minute) +- [Second](#second) +- [SecondFraction](#secondfraction) +- [TimeZone](#timezone) +- [StandaloneQuarter](#standalonequarter) +- [StandaloneMonth](#standalonemonth) +- [StandaloneWeekday](#standaloneweekday) +- [VerbatimHour](#verbatimhour) +--- #### Era @@ -45,6 +76,8 @@ Oddly enough, each of these options are thoroughly documented by Apple (this is | `.wide` | Wide era name. For example, "Anno Domini", "Reiwa", "令和". | | `.narrow` | Narrow era name. For example, For example, "A", "R", "R". | +--- + #### Year | Option | Description | @@ -55,6 +88,8 @@ Oddly enough, each of these options are thoroughly documented by Apple (this is | `.relatedGregorian(minimumLength:)` | Related Gregorian year. For non-Gregorian calendars, this corresponds to the extended Gregorian year in which the calendar’s year begins. Related Gregorian years are often displayed, for example, when formatting dates in the Japanese calendar — e.g. "2012(平成24)年1月15日" — or in the Chinese calendar — e.g. "2012壬辰年腊月初四". | | `.extended(minimumLength:)` | Extended year. This is a single number designating the year of this calendar system, encompassing all supra-year fields. For example, for the Julian calendar system, year numbers are positive, with an era of BCE or CE. An extended year value for the Julian calendar system assigns positive values to CE years and negative values to BCE years, with 1 BCE being year 0. | +--- + #### YearForWeekOfYear | Option | Description | @@ -63,6 +98,8 @@ Oddly enough, each of these options are thoroughly documented by Apple (this is | `.twoDigits` | Two low-order digits. Padded or truncated if necessary. For example, `02`, `20`, `01`, `17`, `73`. | | `.padded(_ length: Int)` | Three or more digits. Padded if necessary. For example, `002`, `020`, `201`, `2017`, `20173`. | +--- + #### CyclicYear Calendars such as the Chinese lunar calendar (and related calendars) and the Hindu calendars use 60-year cycles of year names. If the calendar does not provide cyclic year name data, or if the year value to be formatted is out of the range of years for which cyclic name data is provided, then numeric formatting is used (behaves like `Year`). @@ -74,6 +111,8 @@ Calendars such as the Chinese lunar calendar (and related calendars) and the Hin | `.narrow` | Narrow cyclic year name. For example, "甲子". | +--- + #### Quarter | Option | Description | @@ -85,6 +124,8 @@ Calendars such as the Chinese lunar calendar (and related calendars) and the Hin | `.narrow` | Narrow quarter. For example `2`. | +--- + #### Month | Option | Description | | @@ -94,6 +135,8 @@ Calendars such as the Chinese lunar calendar (and related calendars) and the Hin | `.abbreviated` | Abbreviated month name. For example, "Sep". | | | `.wide` | Wide month name. For example, "September". | `` | | `.narrow` | Narrow month name. For example, "S". | | + +--- #### Week @@ -105,6 +148,8 @@ Week symbols. Use with `YearForWeekOfYear` for the year field instead of `Year`. | `.twoDigits` | Two-digit numeric week of year, zero padded as necessary. For example, `08`, `27`. | | `.weekOfMonth` | One-digit numeric week of month, starting from 1. For example, `1`. | +--- + #### Day | Option | Description | @@ -114,6 +159,8 @@ Week symbols. Use with `YearForWeekOfYear` for the year field instead of `Year`. | `.ordinalOfDayInMonth` | Ordinal of day in month. For example, the 2nd Wed in July would yield `2`. | | `.ulianModified(minimumLength:)` | The field length specifies the minimum number of digits, with zero-padding as necessary.
This is different from the conventional Julian day number in two regards. First, it demarcates days at local zone midnight, rather than noon GMT. Second, it is a local number; that is, it depends on the local time zone. It can be thought of as a single number that encompasses all the date-related fields.
For example, `2451334`. | + +--- #### DayOfYear @@ -124,6 +171,8 @@ Week symbols. Use with `YearForWeekOfYear` for the year field instead of `Year`. | `.threeDigits` | Three-digit day of year, with zero-padding as necessary. For example, `007`, `033`, `345`. | +--- + #### Weekday | Option | Description | @@ -134,6 +183,8 @@ Week symbols. Use with `YearForWeekOfYear` for the year field instead of `Year`. | `.short` | Short day of week name. For example, "Tu". | | `.oneDigit` | Local day of week number/name. The value depends on the local starting day of the week. | | `.twoDigits` | Local day of week number/name, format style; two digits, zero-padded if necessary. | + +--- #### DayPeriod @@ -151,6 +202,8 @@ Each of the options can be passed a `width` case. | `.with12s(_ width:)` | Day period including designations for noon and midnight. For example,
Abbreviated: `mid`
Wide: `midnight`
Narrow: `md`.
| | `.conversational` | Conversational day period. For example,
Abbreviated: `at night`, `nachm.`, `ip.`
Wide: `at night`, `nachmittags`, `iltapäivällä`.
Narrow: `at night`, `nachm.`, `iltap`.
| +--- + #### Minute | Option | Description | @@ -159,6 +212,8 @@ Each of the options can be passed a `width` case. | `.twoDigits` | Two-digit numeric, zero padded if needed. For example, `08`, `59`. | +--- + #### Second | Option | Description | @@ -167,6 +222,8 @@ Each of the options can be passed a `width` case. | `.twoDigits` | Two digits numeric, zero padded if needed, not rounded. For example, `08`, `12`. | +--- + #### SecondFraction | Option | Description | @@ -174,6 +231,8 @@ Each of the options can be passed a `width` case. | `.fractional(_ val:)` | Fractional second (numeric).
Truncates, like other numeric time fields, but in this case to the number of digits specified by the associated `Int`.
For example, specifying `4` for seconds value `12.34567` yields `12.3456`. | | `.milliseconds(_ val:)` | Milliseconds in day (numeric).
The associated `Int` specifies the minimum number of digits, with zero-padding as necessary. The maximum number of digits is 9.
This field behaves exactly like a composite of all time-related fields, not including the zone fields. As such, it also reflects discontinuities of those fields on DST transition days. On a day of DST onset, it will jump forward. On a day of DST cessation, it will jump backward. This reflects the fact that is must be combined with the offset field to obtain a unique local time value. | +--- + #### TimeZone Each talkes a `Width` case. @@ -190,6 +249,8 @@ Each talkes a `Width` case. | `.exemplarLocation` | The exemplar city (location) for the time zone. The localized exemplar city name for the special zone or unknown is used as the fallback if it is unavailable.
For example, "Los Angeles". | | `.genericLocation` | The generic location format. Falls back to `longLocalizedGMT` if unavailable. Recommends for presenting possible time zone choices for user selection.
For example, "Los Angeles Time". | +--- + #### StandaloneQuarter | Option | Description | @@ -201,6 +262,8 @@ Each talkes a `Width` case. | `.narrow` | Standalone narrow quarter. For example "2". | +--- + #### StandaloneMonth | Option | Description | @@ -210,6 +273,8 @@ Each talkes a `Width` case. | `.abbreviated` | Stand-alone abbreviated month.For example, "Sep". | | `.wide` | Stand-alone wide month. For example, "September". | | `.narrow` | Stand-alone narrow month. For example, "S". | + +--- #### StandaloneWeekday @@ -220,6 +285,8 @@ Each talkes a `Width` case. | `.wide` | Standalone wide local day of week number/name.For example, "Tuesday". | | `.narrow` | Standalone narrow local day of week number/name. For example, "T". | | `.short` | Standalone short local day of week number/name. For example, "Tu". | + +--- #### VerbatimHour diff --git a/static/custom.css b/static/custom.css index 6fa892f..570323d 100755 --- a/static/custom.css +++ b/static/custom.css @@ -5,6 +5,9 @@ --footer-background: #FF1A87; --footer-link-color: white; --footer-link-color-visited: white; + --yellow: #F9FF1A; + --green: #1AFF92; + --blue: #201AFF; } body { @@ -58,6 +61,55 @@ hr { height: 400px; } +.version-badge { + margin: 0 5px; + padding: 2px 8px; + font-size: 15px; + border-radius: 0.5em; + color: white; +} + +a.version-badge:visited { + color: #ced3d8; +} + +.version-badge.xcode13 { + background: var(--blue); + color: #ced3d8; +} + +.version-badge.xcode14 { + background: var(--green); + color: #343a40; +} + +.hovertext { + position: relative; +} + +.hovertext:before { + content: attr(data-hover); + visibility: hidden; + opacity: 0; + width: 140px; + background-color: gray; + color: #fff; + text-align: center; + border-radius: 5px; + padding: 8px; + transition: opacity 1s ease-in-out; + position: absolute; + z-index: 1; + left: 0; + top: 110%; + font-size: 0.8em; +} + +.hovertext:hover:before { + opacity: 1; + visibility: visible; +} + @media screen and (max-width: 45rem) { html body .gdoc-nav nav { position: relative;