Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save akashios/ed741acc93802eb9b943988c79823f02 to your computer and use it in GitHub Desktop.
Save akashios/ed741acc93802eb9b943988c79823f02 to your computer and use it in GitHub Desktop.
iOS 15 FormatStyle Deep Dive Example
import PlaygroundSupport
import SwiftUI
// Not all FormatStyle instances provided by Apple will output AttributedString values.
// Check the blog for all of the details.
struct ContentView: View {
var percentAttributed: AttributedString {
var percentAttributedString = 0.8890.formatted(.percent.attributed)
percentAttributedString.swiftUI.font = .title
percentAttributedString.runs.forEach { run in
if let numberRun = run.numberPart {
switch numberRun {
case .integer:
percentAttributedString[run.range].foregroundColor = .orange
case .fraction:
percentAttributedString[run.range].foregroundColor = .blue
}
}
if let symbolRun = run.numberSymbol {
switch symbolRun {
case .percent:
percentAttributedString[run.range].foregroundColor = .green
case .decimalSeparator:
percentAttributedString[run.range].foregroundColor = .red
default:
break
}
}
}
return percentAttributedString
}
var body: some View {
VStack {
Text(percentAttributed)
}
.padding()
}
}
PlaygroundPage.current.setLiveView(ContentView())
import Foundation
let terabyte: Int64 = 1_000_000_000_000
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, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .kb)) // "1,000,000,000 kB"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .mb)) // "1,000,000 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"
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
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)"
// MARK: - AttributedString Output
terabyte.formatted(.byteCount(style: .binary).attributed)
import Foundation
struct ToYen: FormatStyle {
typealias FormatInput = Int
typealias FormatOutput = String
static let multiplier = 100
static let formatter = IntegerFormatStyle<Int>.Currency.currency(code: "jpy")
var attributed: ToYen.AttributedStyle = AttributedStyle()
func format(_ value: Int) -> String {
(value * ToYen.multiplier).formatted(ToYen.formatter)
}
}
extension ToYen {
struct AttributedStyle: FormatStyle {
typealias FormatInput = Int
typealias FormatOutput = AttributedString
func format(_ value: Int) -> AttributedString {
(value * ToYen.multiplier).formatted(ToYen.formatter.attributed)
}
}
}
extension FormatStyle where Self == ToYen {
static var toYen: ToYen { .init() }
}
30.formatted(ToYen()) // "¥3,000"
30.formatted(.toYen) // "¥3,000"
30.formatted(ToYen().attributed)
30.formatted(.toYen.attributed)
// MARK: Output AttributedString
import Foundation
// MARK: - Date.ComponentsFormatStyle
let testRange = Date(timeIntervalSince1970: 0) ..< Date(timeIntervalSinceReferenceDate: 0)
testRange.formatted(.components(style: .abbreviated, fields: [.day])) // "11,323 days"
testRange.formatted(.components(style: .narrow, fields: [.day])) // "11,323days"
testRange.formatted(.components(style: .wide, fields: [.day])) // "11,323 days"
testRange.formatted(.components(style: .spellOut, fields: [.day])) // "eleven thousand three hundred twenty-three days"
testRange.formatted(.components(style: .condensedAbbreviated, fields: [.day])) // "11,323d"
testRange.formatted(.components(style: .condensedAbbreviated, fields: [.day, .month, .year, .hour, .second, .week])) // "31y"
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let appleReferenceDay = Date(timeIntervalSinceReferenceDate: 0)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
let secondRange = appleReferenceDay..<twosday
// "21 yrs, 1 mth, 3 wks, 9 hr, 1,342 sec"
secondRange.formatted(.components(style: .abbreviated, fields: [.day, .month, .year, .hour, .second, .week]))
// "21yrs 1mth 3wks 9hr 1,342sec"
secondRange.formatted(.components(style: .narrow, fields: [.day, .month, .year, .hour, .second, .week]))
// "21 years, 1 month, 3 weeks, 9 hours, 1,342 seconds"
secondRange.formatted(.components(style: .wide, fields: [.day, .month, .year, .hour, .second, .week]))
// "twenty-one years, one month, three weeks, nine hours, one thousand three hundred forty-two seconds"
secondRange.formatted(.components(style: .spellOut, fields: [.day, .month, .year, .hour, .second, .week]))
// "21y 1mo 3w 9h 1,342s"
secondRange.formatted(.components(style: .condensedAbbreviated, fields: [.day, .month, .year, .hour, .second, .week]))
// MARK: - Setting Locale
let franceLocale = Locale(identifier: "fr_FR")
// "vingt-et-un ans, un mois, trois semaines, neuf heures et mille trois cent quarante-deux secondes"
secondRange.formatted(.components(style: .spellOut, fields: [.day, .month, .year, .hour, .second, .week]).locale(franceLocale))
// MARK: - Custom Date.ComponentsFormatStyle
let componentsFormat = Date.ComponentsFormatStyle(
style: .wide,
locale: Locale(identifier: "fr_FR"),
calendar: Calendar(identifier: .gregorian),
fields: [
.day,
.month,
.year,
.hour,
.second,
.week,
]
)
componentsFormat.format(secondRange) // "21 ans, 1 mois, 3 semaines, 9 heures et 1 342 secondes"
secondRange.formatted(componentsFormat) // "21 ans, 1 mois, 3 semaines, 9 heures et 1 342 secondes"
struct InFrench: FormatStyle {
typealias FormatInput = Range<Date>
typealias FormatOutput = String
static let componentsFormat = Date.ComponentsFormatStyle(
style: .wide,
locale: Locale(identifier: "fr_FR"),
calendar: Calendar(identifier: .gregorian),
fields: [
.day,
.month,
.year,
.hour,
.second,
.week,
]
)
func format(_ value: Range<Date>) -> String {
InFrench.componentsFormat.format(value)
}
}
extension FormatStyle where Self == InFrench {
static var inFrench: InFrench { .init() }
}
secondRange.formatted(.inFrench) // "21 ans, 1 mois, 3 semaines, 9 heures et 1 342 secondes"
import Foundation
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
// MARK: - Date.FormatStyle.dateTime styles
twosday.formatted(.dateTime.day()) // "22"
twosday.formatted(.dateTime.dayOfYear()) // "53"
twosday.formatted(.dateTime.era()) // "AD"
twosday.formatted(.dateTime.hour()) // "2 AM"
twosday.formatted(.dateTime.minute()) // "22"
twosday.formatted(.dateTime.month()) // "Feb"
twosday.formatted(.dateTime.quarter()) // "Q1"
twosday.formatted(.dateTime.second()) // "22"
twosday.formatted(.dateTime.timeZone()) // "MST"
twosday.formatted(.dateTime.week()) // "9"
twosday.formatted(.dateTime.weekday(.abbreviated)) // "Tue"
twosday.formatted(.dateTime.year()) // "2022"
// MARK: - Compositing
twosday.formatted(.dateTime.year().month().day().hour().minute().second()) // "Feb 22, 2022, 2:22:22 AM"
twosday.formatted(.dateTime.second().minute().hour().day().month().year()) // "Feb 22, 2022, 2:22:22 AM"
// MARK: - Customizing individual units
twosday.formatted(.dateTime.day(.twoDigits)) // "22"
twosday.formatted(.dateTime.day(.ordinalOfDayInMonth)) // "4"
twosday.formatted(.dateTime.day(.defaultDigits)) // "22"
// MARK: - Setting Locale
let franceLocale = Locale(identifier: "fr_FR")
twosday.formatted(.dateTime.year().month().day().hour().minute().second().locale(franceLocale)) // "22 févr. 2022 à 02:22:22"
// MARK: - Attributed String Output
twosday.formatted(.dateTime.hour().minute().attributed)
import Foundation
// MARK: - Setup
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
// MARK: - The Basics
twosday.formatted() // "2/22/2022, 2:22 AM"
// MARK: - DateStyle
twosday.formatted(date: .abbreviated, time: .omitted) // "Feb 22, 2022"
twosday.formatted(date: .complete, time: .omitted) // "Tuesday, February 22, 2022"
twosday.formatted(date: .long, time: .omitted) // "February 22, 2022"
twosday.formatted(date: .numeric, time: .omitted) // "2/22/2022"
// MARK: - TimeStyle
twosday.formatted(date: .omitted, time: .complete) // "2:22:22 AM MST"
twosday.formatted(date: .omitted, time: .shortened) // "2:22 AM"
twosday.formatted(date: .omitted, time: .standard) // "2:22:22 AM"
// MARK: - DateStyle & TimeStyle
twosday.formatted(date: .abbreviated, time: .complete) // "Feb 22, 2022, 2:22:22 AM MST"
twosday.formatted(date: .abbreviated, time: .shortened) // "Feb 22, 2022, 2:22 AM"
twosday.formatted(date: .abbreviated, time: .standard) // "Feb 22, 2022, 2:22:22 AM"
twosday.formatted(date: .complete, time: .complete) // "Tuesday, February 22, 2022, 2:22:22 AM MST"
twosday.formatted(date: .complete, time: .shortened) // "Tuesday, February 22, 2022, 2:22 AM"
twosday.formatted(date: .complete, time: .standard) // "Tuesday, February 22, 2022, 2:22:22 AM"
twosday.formatted(date: .long, time: .complete) // "February 22, 2022, 2:22:22 AM MST"
twosday.formatted(date: .long, time: .shortened) // "February 22, 2022, 2:22 AM"
twosday.formatted(date: .long, time: .standard) // "February 22, 2022, 2:22:22 AM"
twosday.formatted(date: .numeric, time: .complete) // "2/22/2022, 2:22:22 AM MST"
twosday.formatted(date: .numeric, time: .shortened) // "2/22/2022, 2:22 AM"
twosday.formatted(date: .numeric, time: .standard) // "2/22/2022, 2:22:22 AM"
// MARK: - Custom Date.FormatStyle
let frenchHebrew = Date.FormatStyle(
date: .complete,
time: .complete,
locale: Locale(identifier: "fr_FR"),
calendar: Calendar(identifier: .hebrew),
timeZone: TimeZone(secondsFromGMT: 0)!,
capitalizationContext: .standalone
)
twosday.formatted(frenchHebrew) // "Mardi 22 février 2022 ap. J.-C. 9:22:22 UTC"
frenchHebrew.format(twosday) // "Mardi 22 février 2022 ap. J.-C. 9:22:22 UTC"
// MARK: - Custom Date.FormatStyle wrapped in custom FormatStyle extension
struct FrenchHebrewStyle: FormatStyle {
typealias FormatInput = Date
typealias FormatOutput = String
static let frenchHebrew = Date.FormatStyle(
date: .complete,
time: .complete,
locale: Locale(identifier: "fr_FR"),
calendar: Calendar(identifier: .hebrew),
timeZone: TimeZone(secondsFromGMT: 0)!,
capitalizationContext: .standalone
)
func format(_ value: Date) -> String {
FrenchHebrewStyle.frenchHebrew.format(value)
}
}
extension FormatStyle where Self == FrenchHebrewStyle {
static var frenchHebrew: FrenchHebrewStyle { .init() }
}
twosday.formatted(.frenchHebrew) // "Mardi 22 février 2022 ap. J.-C. 9:22:22 UTC"
import Foundation
// MARK: - Date.IntervalFormatStyle
let range = Date(timeIntervalSince1970: 0)..<Date(timeIntervalSinceReferenceDate: 2837)
range.formatted(.interval) // "12/31/69, 5:00 PM – 12/31/00, 5:47 PM"
// MARK: - Setting Locale
let franceLocale = Locale(identifier: "fr_FR")
range.formatted(.interval.locale(franceLocale)) // "31/12/1969 à 17:00 – 31/12/2000 à 17:47"
// MARK: - Custom Date.IntervalFormatStyle
let interval = Date.IntervalFormatStyle(
date: .abbreviated,
time: .shortened,
locale: Locale(identifier: "en_US"),
calendar: Calendar(identifier: .gregorian),
timeZone: TimeZone(secondsFromGMT: 0)!
)
interval.format(range) // "Jan 1, 1970, 12:00 AM – Jan 1, 2001, 12:47 AM"
range.formatted(interval) // "Jan 1, 1970, 12:00 AM – Jan 1, 2001, 12:47 AM"
struct NarrowIntervalStyle: FormatStyle {
typealias FormatInput = Range<Date>
typealias FormatOutput = String
static let interval = Date.IntervalFormatStyle(
date: .abbreviated,
time: .shortened,
locale: Locale(identifier: "en_US"),
calendar: Calendar(identifier: .gregorian),
timeZone: TimeZone(secondsFromGMT: 0)!
)
func format(_ value: Range<Date>) -> String {
NarrowIntervalStyle.interval.format(value)
}
}
extension FormatStyle where Self == NarrowIntervalStyle {
static var narrowInterval: NarrowIntervalStyle { .init() }
}
range.formatted(.narrowInterval)
import Foundation
// MARK: - Date.ISO8601FormatStyle
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
twosday.formatted(.iso8601) // "2022-02-22T09:22:22Z"
// MARK: - Setting Locale
let franceLocale = Locale(identifier: "fr_FR")
twosday.formatted(.iso8601.locale(franceLocale)) // "2022-02-22T09:22:22Z"
// MARK: - Customization Options
let isoFormat = Date.ISO8601FormatStyle(
dateSeparator: .dash,
dateTimeSeparator: .standard,
timeSeparator: .colon,
timeZoneSeparator: .colon,
includingFractionalSeconds: true,
timeZone: TimeZone(secondsFromGMT: 0)!
)
isoFormat.format(twosday) // "2022-02-22T09:22:22.000Z"
twosday.formatted(isoFormat)
struct ISO8601Variant: FormatStyle {
typealias FormatInput = Date
typealias FormatOutput = String
static let isoFormat = Date.ISO8601FormatStyle(
dateSeparator: .dash,
dateTimeSeparator: .standard,
timeSeparator: .colon,
timeZoneSeparator: .colon,
includingFractionalSeconds: true,
timeZone: TimeZone(secondsFromGMT: 0)!
)
func format(_ value: Date) -> String {
ISO8601Variant.isoFormat.format(value)
}
}
extension FormatStyle where Self == ISO8601Variant {
static var iso8601Variant: ISO8601Variant { .init() }
}
twosday.formatted(.iso8601Variant) // "2022-02-22T09:22:22.000Z"
import Foundation
let thePast = Calendar(identifier: .gregorian).date(byAdding: .day, value: -14, to: Date())!
// MARK: - Without Units
thePast.formatted(.relative(presentation: .numeric)) // "2 weeks ago"
thePast.formatted(.relative(presentation: .numeric)) // "2 weeks ago"
thePast.formatted(.relative(presentation: .named)) // "2 weeks ago"
// MARK: - Including Units
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .abbreviated)) // "2 wk. ago"
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .narrow)) // "2 wk. ago"
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .spellOut)) // "two weeks ago"
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .wide)) // "2 weeks ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .abbreviated)) // "2 wk. ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .narrow)) // "2 wk. ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .spellOut)) // "two weeks ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .wide)) // "2 weeks ago"
// MARK: - Set Locale
let franceLocale = Locale(identifier: "fr_FR")
thePast.formatted(.relative(presentation: .named, unitsStyle: .spellOut).locale(franceLocale)) // "il y a deux semaines"
// MARK: - Custom RelativeFormatStyle
let relativeInFrench = Date.RelativeFormatStyle(
presentation: .named,
unitsStyle: .spellOut,
locale: Locale(identifier: "fr_FR"),
calendar: Calendar(identifier: .gregorian),
capitalizationContext: .beginningOfSentence
)
thePast.formatted(relativeInFrench) // "Il y a deux semaines"
relativeInFrench.format(thePast) // "Il y a deux semaines"
// MARK: - Extending FormatStyle
struct InFrench: FormatStyle {
typealias FormatInput = Date
typealias FormatOutput = String
static let relativeInFrench = Date.RelativeFormatStyle(
presentation: .named,
unitsStyle: .spellOut,
locale: Locale(identifier: "fr_FR"),
calendar: Calendar(identifier: .gregorian),
capitalizationContext: .beginningOfSentence
)
func format(_ value: Date) -> String {
InFrench.relativeInFrench.format(value)
}
}
extension FormatStyle where Self == InFrench {
static var inFrench: InFrench { .init() }
}
thePast.formatted(.inFrench) // "Il y a deux semaines"
import Foundation
// MARK: - Date.VerbatimFormatStyle
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
let verbatim = Date.VerbatimFormatStyle(
format: "\(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .twoDigits)",
timeZone: TimeZone.current,
calendar: .current
)
verbatim.format(twosday) // "02:22"
twosday.formatted(verbatim.attributed)
import Foundation
// Dates
Date(timeIntervalSinceReferenceDate: 0).formatted() // "12/31/2000, 5:00 PM"
// Measurements
Measurement(value: 20, unit: UnitDuration.minutes).formatted() // "20 min"
Measurement(value: 300, unit: UnitLength.miles).formatted() // "300 mi"
Measurement(value: 10, unit: UnitMass.kilograms).formatted() // "22 lb"
Measurement(value: 100, unit: UnitTemperature.celsius).formatted() // "212°F"
// Numbers
32.formatted() // "32"
Decimal(20.0).formatted() // "20"
Float(10.0).formatted() // "10"
Int(2).formatted() // "2"
Double(100.0003).formatted() // "100.0003"
// Names
PersonNameComponents(givenName: "Johnny", familyName: "Appleseed").formatted() // "Johnny Appleseed"
// Lists
["Alba", "Bruce", "Carol", "Billson"].formatted() // "Alba, Bruce, Carol, and Billson"
// TimeInterval
let referenceDay = Date(timeIntervalSinceReferenceDate: 0)
(referenceDay ..< referenceDay.addingTimeInterval(200)).formatted() // "12/31/00, 5:00 – 5:03 PM"
import Foundation
let letters = ["a", "b", "c", "d"]
// MARK: - ListFormatStyle
letters.formatted() // "a, b, c, and d"
letters.formatted(.list(type: .and)) // "a, b, c, and d"
letters.formatted(.list(type: .or)) // "a, b, c, or d"
letters.formatted(.list(type: .and, width: .narrow)) // "a, b, c, d"
letters.formatted(.list(type: .and, width: .standard)) // "a, b, c, and d"
letters.formatted(.list(type: .and, width: .short)) // "a, b, c, & d"
letters.formatted(.list(type: .or, width: .narrow)) // "a, b, c, or d"
letters.formatted(.list(type: .or, width: .standard)) // "a, b, c, or d"
letters.formatted(.list(type: .or, width: .short)) // "a, b, c, or d"
// MARK: Set Locale
let franceLocale = Locale(identifier: "fr_FR")
letters.formatted(.list(type: .and).locale(franceLocale)) // "a, b, c, et d"
letters.formatted(.list(type: .or).locale(franceLocale)) // "a, b, c, ou d"
letters.formatted(.list(type: .and, width: .narrow).locale(franceLocale)) // "a, b, c, d"
letters.formatted(.list(type: .and, width: .standard).locale(franceLocale)) // "a, b, c, et d"
letters.formatted(.list(type: .and, width: .short).locale(franceLocale)) // "a, b, c, et d"
letters.formatted(.list(type: .or, width: .narrow).locale(franceLocale)) // "a, b, c, ou d"
letters.formatted(.list(type: .or, width: .standard).locale(franceLocale)) // "a, b, c, ou d"
letters.formatted(.list(type: .or, width: .short).locale(franceLocale)) // "a, b, c, ou d"
let importantDates = [
Date(timeIntervalSinceReferenceDate: 0),
Date(timeIntervalSince1970: 0)
]
let yearOnlyFormat = Date.FormatStyle.dateTime.year()
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .and)) // "2000 and 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .or)) // "2000 or 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .and, width: .standard)) // "2000 and 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .and, width: .narrow)) // "2000, 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .and, width: .short)) // "2000 & 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .or, width: .standard)) // "2000 or 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .or, width: .narrow)) // "2000 or 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .or, width: .short)) // "2000 or 1969"
let yearStyle = ListFormatStyle<Date.FormatStyle, Array<Date>>.init(memberStyle: .dateTime.year())
importantDates.formatted(yearStyle)
import Foundation
// MARK: - Measurement.FormatStyle
let gForce = Measurement(value: 1.0, unit: UnitAcceleration.gravity)
let mpsec = Measurement(value: 1.0, unit: UnitAcceleration.metersPerSecondSquared)
gForce.formatted(.measurement(width: .wide)) // "1 g-force"
gForce.formatted(.measurement(width: .narrow)) // "1G"
gForce.formatted(.measurement(width: .abbreviated)) // "1 G"
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"
// MARK: - Customizing
let inFrench = Measurement<UnitAcceleration>.FormatStyle(
width: .wide,
locale: Locale(identifier: "fr_FR"),
usage: .general
)
inFrench.format(gForce) // "1 fois l’accélération de pesanteur terrestre"
gForce.formatted(inFrench) // "1 fois l’accélération de pesanteur terrestre"
// MARK: - Custom Measurement FormatStyle
struct InFrench: FormatStyle {
typealias FormatInput = Measurement<UnitAcceleration>
typealias FormatOutput = String
static let formatter = Measurement<UnitAcceleration>.FormatStyle(
width: .wide,
locale: Locale(identifier: "fr_FR"),
usage: .general
)
func format(_ value: Measurement<UnitAcceleration>) -> String {
InFrench.formatter.format(value)
}
}
extension FormatStyle where Self == InFrench {
static var inFrench: InFrench { .init() }
}
gForce.formatted(.inFrench) // "1 fois l’accélération de pesanteur terrestre"
// MARK: - Output Attributed Strings
gForce.formatted(.measurement(width: .wide).attributed)
gForce.formatted(.measurement(width: .narrow).attributed)
gForce.formatted(.measurement(width: .abbreviated).attributed)
import Foundation
// MARK: - Grouping
Int(3_000).formatted(.currency(code: "GBP").grouping(.never)) // "£3000.00"
Int(3_000).formatted(.currency(code: "GBP").grouping(.automatic)) // "£3,000.00"
// MARK: - Precision
// Please don't use Floating point numbers to store currency. Please.
Float(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(4))) // "£3,000.0029" <- This is why
Float(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(1 ... 4))) // "£3,000.0029"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(4))) // "£3,000.0029"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(1 ... 4))) // "£3,000.0029"
Decimal(3).formatted(.currency(code: "GBP").precision(.integerAndFractionLength(integer: 4, fraction: 4))) // "£0,003.0000"
Decimal(3).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£3.0"
Decimal(3.00004).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£3.00004"
Decimal(3.000000004).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
)
Decimal(30000.01).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£30,000.01"
Decimal(3000000.000001).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£0.0"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(1))) // "£10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(2))) // "£10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(3))) // "£10.1"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(4))) // "£10.10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(5))) // "£10.100"
Decimal(1).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£1"
Decimal(10).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£10.1"
Decimal(10.01).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£10"
// MARK: - Decimal Separator
Decimal(3000).formatted(.currency(code: "GBP").decimalSeparator(strategy: .automatic)) // "£3,000.00"
Decimal(3000).formatted(.currency(code: "GBP").decimalSeparator(strategy: .always)) // "£3,000.00"
// MARK: - Presentation
Decimal(10).formatted(.currency(code: "GBP").presentation(.fullName)) // "10.00 British pounds"
Decimal(10).formatted(.currency(code: "GBP").presentation(.isoCode)) // "GBP 10.00"
Decimal(10).formatted(.currency(code: "GBP").presentation(.narrow)) // "£10.00"
Decimal(10).formatted(.currency(code: "GBP").presentation(.standard)) // "£10.00"
// MARK: - Presentation with Locale
Decimal(10).formatted(.currency(code: "GBP").presentation(.fullName).locale(Locale(identifier: "fr_FR"))) // "10,00 livres sterling"
// MARK: - Rounded
Decimal(0.59).formatted(.currency(code: "GBP").rounded()) // "£0.59"
Decimal(0.599).formatted(.currency(code: "GBP").rounded()) // "£0.60"
Decimal(0.5999).formatted(.currency(code: "GBP").rounded()) // "£0.60"
Decimal(5.001).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero, increment: 1)) // "£6"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero, increment: 10)) // "£10"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .down)) // "£5.00"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .toNearestOrAwayFromZero)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .towardZero)) // "£5.00"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .up)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .down, increment: 1)) // "£5"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "£5"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .towardZero, increment: 1)) // "£5"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .up, increment: 1)) // "£5"
// MARK: - Sign
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .automatic)) // "£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .never)) // "£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accounting)) // "£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accountingAlways())) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accountingAlways(showZero: true))) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accountingAlways(showZero: false))) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .always())) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .always(showZero: true))) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .always(showZero: false))) // "+£7.00"
Decimal(10).formatted(
.currency(code: "GBP")
.scale(200.0)
.sign(strategy: .always())
.presentation(.fullName)
) // "+2,000.00 British pounds"
import Foundation
// MARK: - Rounded
Double(1.9999999).formatted(.number.rounded()) // "2"
Decimal(1.9999999).formatted(.number.rounded()) // "2"
Float(1.9999999).formatted(.number.rounded()) // "2"
Int(1.9999999).formatted(.number.rounded()) // "1"
Float(0.26575467567788).formatted(.number.rounded(rule: .awayFromZero)) // "0.265755"
Float(0.00900999876871).formatted(.number.rounded(rule: .awayFromZero)) // "0.00901"
Float(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 1)) // "6"
Float(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 10)) // "10"
Float(0.01).formatted(.number.rounded(rule: .down)) // "0.009999"
Float(0.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero)) // "0.01"
Float(0.01).formatted(.number.rounded(rule: .towardZero)) // "0.009999"
Float(0.01).formatted(.number.rounded(rule: .up)) // "0.01"
Float(5.01).formatted(.number.rounded(rule: .down, increment: 1)) // "5"
Float(5.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "5"
Float(5.01).formatted(.number.rounded(rule: .towardZero, increment: 1)) // "5"
Float(5.01).formatted(.number.rounded(rule: .up, increment: 1)) // "5"
Double(0.26575467567788).formatted(.number.rounded(rule: .awayFromZero)) // "0.265755"
Double(0.00900999876871).formatted(.number.rounded(rule: .awayFromZero)) // "0.00901"
Double(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 1)) // "6"
Double(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 10)) // "10"
Double(0.01).formatted(.number.rounded(rule: .down)) // "0.01"
Double(0.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero)) // "0.01"
Double(0.01).formatted(.number.rounded(rule: .towardZero)) // "0.01"
Double(0.01).formatted(.number.rounded(rule: .up)) // "0.01"
Double(5.01).formatted(.number.rounded(rule: .down, increment: 1)) // "5"
Double(5.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "5"
Double(5.01).formatted(.number.rounded(rule: .towardZero, increment: 1)) // "5"
Double(5.01).formatted(.number.rounded(rule: .up, increment: 1)) // "5"
Decimal(0.26575467567788).formatted(.number.rounded(rule: .awayFromZero)) // "0.265755"
Decimal(0.00900999876871).formatted(.number.rounded(rule: .awayFromZero)) // "0.00901"
Decimal(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 1)) // "6"
Decimal(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 10)) // "10"
Decimal(0.01).formatted(.number.rounded(rule: .down)) // "0.01"
Decimal(0.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero)) // "0.01"
Decimal(0.01).formatted(.number.rounded(rule: .towardZero)) // "0.01"
Decimal(0.01).formatted(.number.rounded(rule: .up)) // "0.01"
Decimal(5.01).formatted(.number.rounded(rule: .down, increment: 1)) // "5"
Decimal(5.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "5"
Decimal(5.01).formatted(.number.rounded(rule: .towardZero, increment: 1)) // "5"
Decimal(5.01).formatted(.number.rounded(rule: .up, increment: 1)) // "5"
// MARK: - Sign
Float(1.90).formatted(.number.sign(strategy: .never)) // "1.9"
Float(-1.90).formatted(.number.sign(strategy: .never)) // "1.9"
Float(1.90).formatted(.number.sign(strategy: .automatic)) // "1.9"
Float(1.90).formatted(.number.sign(strategy: .always())) // "+1.9"
Float(0).formatted(.number.sign(strategy: .always(includingZero: true))) // "+0"
Float(0).formatted(.number.sign(strategy: .always(includingZero: false))) // "0"
// MARK: - Decimal Separator
Float(10).formatted(.number.decimalSeparator(strategy: .automatic)) // "10"
Float(10).formatted(.number.decimalSeparator(strategy: .always)) // "10."
// MARK: - Grouping
Float(1000).formatted(.number.grouping(.automatic)) // "1,000"
Float(1000).formatted(.number.grouping(.never)) // "1000"
// MARK: - Grouping with Locale
Float(1000).formatted(.number.grouping(.automatic).locale(Locale(identifier: "fr_FR"))) // "1 000"
Float(1000).formatted(.number.grouping(.never).locale(Locale(identifier: "fr_FR"))) // "1000"
// MARK: - Significant Digits
Decimal(10.1).formatted(.number.precision(.significantDigits(1))) // "10"
Decimal(10.1).formatted(.number.precision(.significantDigits(2))) // "10"
Decimal(10.1).formatted(.number.precision(.significantDigits(3))) // "10.1"
Decimal(10.1).formatted(.number.precision(.significantDigits(4))) // "10.10"
Decimal(10.1).formatted(.number.precision(.significantDigits(5))) // "10.100"
Decimal(1).formatted(.number.precision(.significantDigits(1 ... 3))) // "1"
Decimal(10).formatted(.number.precision(.significantDigits(1 ... 3))) // "10"
Decimal(10.1).formatted(.number.precision(.significantDigits(1 ... 3))) // "10.1"
Decimal(10.01).formatted(.number.precision(.significantDigits(1 ... 3))) // "10"
// MARK: - Notation
Float(1_000).formatted(.number.notation(.automatic)) // "1,000"
Float(1_000).formatted(.number.notation(.compactName)) // "1K"
Float(1_000).formatted(.number.notation(.scientific)) // "1E3"
// MARK: - Notation with Locale
Float(1_000).formatted(.number.notation(.automatic).locale(Locale(identifier: "fr_FR"))) // "1 000"
Float(1_000).formatted(.number.notation(.compactName).locale(Locale(identifier: "fr_FR"))) // "1 k"
Float(1_000).formatted(.number.notation(.scientific).locale(Locale(identifier: "fr_FR"))) // "1E3"
// MARK: - Scale
Float(10).formatted(.number.scale(1.0)) // "10"
Float(10).formatted(.number.scale(1.5)) // "15"
Float(10).formatted(.number.scale(2.0)) // "20"
Float(10).formatted(.number.scale(-2.0)) // "-20"
// MARK: - Compositing
Float(10).formatted(.number.scale(200.0).notation(.compactName).grouping(.automatic)) // "2K"
import Foundation
// MARK: - Rounded
Double(1.9999999).formatted(.percent.rounded()) // "199.99999%"
Decimal(1.9999999).formatted(.percent.rounded()) // "199.99999%"
Float(1.9999999).formatted(.percent.rounded()) // "199.999998%"
Int(1.9999999).formatted(.percent.rounded()) // 1%
Float(0.26575467567788).formatted(.percent.rounded(rule: .awayFromZero)) // "26.575467%"
Float(0.00900999876871).formatted(.percent.rounded(rule: .awayFromZero)) // "0.901%"
Float(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 1)) // "502%"
Float(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 10)) // "510%"
Float(0.01).formatted(.percent.rounded(rule: .down)) // "0.999999%"
Float(0.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero)) // "1%"
Float(0.01).formatted(.percent.rounded(rule: .towardZero)) // "0.999999%"
Float(0.01).formatted(.percent.rounded(rule: .up)) // "1%"
Float(5.01).formatted(.percent.rounded(rule: .down, increment: 1)) // "501%"
Float(5.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "501%"
Float(5.01).formatted(.percent.rounded(rule: .towardZero, increment: 1)) // "501%"
Float(5.01).formatted(.percent.rounded(rule: .up, increment: 1)) // "502%"
Double(0.26575467567788).formatted(.percent.rounded(rule: .awayFromZero)) // "26.575468%"
Double(0.00900999876871).formatted(.percent.rounded(rule: .awayFromZero)) // "0.901%"
Double(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 10)) // "510%"
Double(0.01).formatted(.percent.rounded(rule: .down)) // "1%"
Double(0.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero)) // "1%"
Double(0.01).formatted(.percent.rounded(rule: .towardZero)) // "1%"
Double(0.01).formatted(.percent.rounded(rule: .up)) // "1%"
Double(5.01).formatted(.percent.rounded(rule: .down, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .towardZero, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .up, increment: 1)) // "501%"
Decimal(0.26575467567788).formatted(.percent.rounded(rule: .awayFromZero)) // "26.575468%"
Decimal(0.00900999876871).formatted(.percent.rounded(rule: .awayFromZero)) // "0.901%"
Decimal(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 1)) // "501%"
Decimal(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 10)) // "510%"
Decimal(0.01).formatted(.percent.rounded(rule: .down)) // "1%"
Decimal(0.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero)) // "1%"
Decimal(0.01).formatted(.percent.rounded(rule: .towardZero)) // "1%"
Decimal(0.01).formatted(.percent.rounded(rule: .up)) // "1%"
Decimal(5.01).formatted(.percent.rounded(rule: .down, increment: 1)) // "500%"
Decimal(5.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "501%"
Decimal(5.01).formatted(.percent.rounded(rule: .towardZero, increment: 1)) // "500%"
Decimal(5.01).formatted(.percent.rounded(rule: .up, increment: 1)) // "501%"
// MARK: - Sign
Float(1.90).formatted(.percent.sign(strategy: .never)) // "189.999998%"
Float(-1.90).formatted(.percent.sign(strategy: .never)) // "189.999998%"
Float(1.90).formatted(.percent.sign(strategy: .automatic)) // "189.999998%"
Float(1.90).formatted(.percent.sign(strategy: .always())) // "+189.999998%"
Float(0).formatted(.percent.sign(strategy: .always(includingZero: true))) // "+0%"
Float(0).formatted(.percent.sign(strategy: .always(includingZero: false))) // "0%"
// MARK: - Decimal Separator
Float(10).formatted(.percent.decimalSeparator(strategy: .automatic)) // "1,000%"
Float(10).formatted(.percent.decimalSeparator(strategy: .always)) // "1,000.%"
// MARK: - Grouping
Float(1_000).formatted(.percent.grouping(.automatic)) // "100,000%"
Float(1_000).formatted(.percent.grouping(.never)) // "100000%"
// MARK: - Grouping with Locale
Float(1_000).formatted(.percent.grouping(.automatic).locale(Locale(identifier: "fr_FR"))) // "100 000 %"
Float(1_000).formatted(.percent.grouping(.never).locale(Locale(identifier: "fr_FR"))) // "100000 %"
// MARK: - Significant Digits
Decimal(10.1).formatted(.percent.precision(.significantDigits(1))) // "1,000%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(2))) // "1,000%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(3))) // "1,010%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(4))) // "1,010%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(5))) // "1,010.0%"
Decimal(1).formatted(.percent.precision(.significantDigits(1 ... 3))) // "100%"
Decimal(10).formatted(.percent.precision(.significantDigits(1 ... 3))) // "1,000%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(1 ... 3))) // "1,010%"
Decimal(10.01).formatted(.percent.precision(.significantDigits(1 ... 3))) // "1,000%"
// MARK: - Notation
Float(1_000).formatted(.percent.notation(.automatic)) // "100,000%"
Float(1_000).formatted(.percent.notation(.compactName)) // "100K%"
Float(1_000).formatted(.percent.notation(.scientific)) // "1E5%"
// MARK: - Notation with Locale
Float(1_000).formatted(.percent.notation(.automatic).locale(Locale(identifier: "fr_FR"))) // "100 000 %"
Float(1_000).formatted(.percent.notation(.compactName).locale(Locale(identifier: "fr_FR"))) // "100 k %"
Float(1_000).formatted(.percent.notation(.scientific).locale(Locale(identifier: "fr_FR"))) // "1E5 %"
// MARK: - Scale
Float(10).formatted(.percent.scale(1.0)) // "10%"
Float(10).formatted(.percent.scale(1.5)) // "15%"
Float(10).formatted(.percent.scale(2.0)) // "20%"
Float(10).formatted(.percent.scale(-2.0)) // "-20%"
Float(10).formatted(.percent.scale(200.0).notation(.compactName).grouping(.automatic)) // "2K%"
import Foundation
let guest = PersonNameComponents(
namePrefix: "Dr",
givenName: "Elizabeth",
middleName: "Jillian",
familyName: "Smith",
nameSuffix: "Esq.",
nickname: "Liza"
)
guest.formatted() // "Elizabeth Smith"
guest.formatted(.name(style: .abbreviated)) // "ES"
guest.formatted(.name(style: .short)) // "Liza"
guest.formatted(.name(style: .medium)) // "Elizabeth Smith"
guest.formatted(.name(style: .long)) // "Dr Elizabeth Jillian Smith Esq."
let chinaLocale = Locale(identifier: "zh_CN")
guest.formatted(.name(style: .abbreviated).locale(chinaLocale)) // "SE"
guest.formatted(.name(style: .short).locale(chinaLocale)) // "Liza"
guest.formatted(.name(style: .medium).locale(chinaLocale)) // "Smith Elizabeth"
guest.formatted(.name(style: .long).locale(chinaLocale)) // "Dr Smith Elizabeth Jillian Esq."
let inFrance = PersonNameComponents.FormatStyle(style: .long, locale: Locale(identifier: "fr_FR"))
inFrance.format(guest)
guest.formatted(inFrance)
import PlaygroundSupport
import SwiftUI
struct ContentView: View {
static let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
var twosday: Date {
Calendar(identifier: .gregorian).date(from: ContentView.twosdayDateComponents)!
}
var body: some View {
VStack {
Text(twosday, format: Date.FormatStyle(date: .complete, time: .complete))
Text(twosday, format: .dateTime.hour())
Text(twosday, format: .dateTime.year().month().day())
}
.padding()
}
}
PlaygroundPage.current.setLiveView(ContentView())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment