Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save seanlilmateus/efae27b10011cac6a7b5884e92ddff9e to your computer and use it in GitHub Desktop.
Save seanlilmateus/efae27b10011cac6a7b5884e92ddff9e to your computer and use it in GitHub Desktop.
iOS 15 FormatStyle Deep Dive Example
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"
// Calling format on a style
let byteCountStyle = ByteCountFormatStyle(
style: .file,
allowedUnits: .all,
spellsOutZero: true,
includesActualByteCount: true,
locale: Locale.current
)
byteCountStyle.format(1_000_000_000) // "1 GB (1,000,000,000 bytes)"
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
// 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(1_000).formatted(.number.grouping(.automatic)) // "1,000"
Float(1_000).formatted(.number.grouping(.never)) // "1000"
// MARK: - Precision
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_000_000.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"
Decimal(10.01).formatted(.number.precision(.fractionLength(1))) // 10.0
Decimal(10.01).formatted(.number.precision(.fractionLength(2))) // 10.01
Decimal(10.01).formatted(.number.precision(.fractionLength(3))) // 10.010
Decimal(10).formatted(.number.precision(.fractionLength(0...2))) // 10
Decimal(10.1).formatted(.number.precision(.fractionLength(0...2))) // 10.1
Decimal(10.11).formatted(.number.precision(.fractionLength(0...2))) // 10.11
Decimal(10.111).formatted(.number.precision(.fractionLength(0...2))) // 10.11
Decimal(10.111).formatted(.number.precision(.integerLength(1))) // 0.111
Decimal(10.111).formatted(.number.precision(.integerLength(2))) // 10.111
Decimal(10.111).formatted(.number.precision(.integerLength(0...1))) // .111
Decimal(10.111).formatted(.number.precision(.integerLength(0...2))) // 10.111
Decimal(10.111).formatted(.number.precision(.integerLength(0...3))) // 10.111
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 1, fraction: 1))) // 0.1
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 2, fraction: 1))) // 10.1
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 2, fraction: 2))) // 10.11
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 2, fraction: 3))) // 10.111
// MARK: - Notation
Float(1_000).formatted(.number.notation(.automatic)) // "1,000"
1_000.formatted(.number.notation(.compactName)) // "1K"
1_000_000_000.formatted(.number.notation(.compactName)) // 1B
1_500_000.formatted(.number.notation(.compactName)) // 1.5M
Float(1_000).formatted(.number.notation(.scientific)) // "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"
// MARK: - 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"
Float(1_000).formatted(.number.grouping(.automatic).locale(Locale(identifier: "fr_FR"))) // "1 000"
Float(1_000).formatted(.number.grouping(.never).locale(Locale(identifier: "fr_FR"))) // "1000"
// MARK: - Attributed String Output
Float(10).formatted(.number.scale(200.0).notation(.compactName).grouping(.automatic).attributed)
// MARK: - Initializing
let frenchStyle = IntegerFormatStyle<Int>()
.notation(.compactName)
.locale(Locale(identifier: "fr_FR"))
.scale(20)
frenchStyle.format(1_000) // "20 k"
1_000.formatted(frenchStyle) // "20 k"
// MARK: - Parsing Decimals
let decimal = Decimal(1_000.01)
try? Decimal.FormatStyle().parseStrategy.parse("-100.1002") // -100.1002
try? Decimal("-100.1002", strategy: Decimal.FormatStyle().scale(2).parseStrategy) // -50.0501
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: - Precision
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%"
Decimal(0.0001).formatted(.percent.precision(.fractionLength(1))) // 0.0%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(2))) // 0.001%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(3))) // 0.010%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...1))) // 0%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...2))) // 0.01%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...3))) // 0.01%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...4))) // 0.01%
Decimal(10.111).formatted(.percent.precision(.integerLength(1))) // 1.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(2))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(0...1))) // 1.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(0...2))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(0...3))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 1, fraction: 1))) // 1.1%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 2, fraction: 1))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 2, fraction: 2))) // 11.10%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 2, fraction: 3))) // 11.100%
// 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: - 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%"
// MARK: Compositing
Float(10).formatted(.percent.scale(200.0).notation(.compactName).grouping(.automatic)) // "2K%"
// MARK: - 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 %"
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: - Attributed String Output
Float(10).formatted(.percent.scale(200.0).notation(.compactName).grouping(.automatic).attributed)
// MARK: - Initializing
let frenchStyle = FloatingPointFormatStyle<Double>.Percent()
.notation(.compactName)
.locale(Locale(identifier: "fr_FR"))
frenchStyle.format(0.1) // "10 %"
0.1.formatted(frenchStyle) // "10 %"
import Foundation
// 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: - 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_000.003).formatted(.currency(code: "GBP").precision(.integerLength(3))) // "£000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(4))) // "£3,000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(5))) // "£03,000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(0 ... 3))) // "£.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(0 ... 4))) // "£3,000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(0 ... 5))) // "£03,000.00"
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(30_000.01).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£30,000.01"
Decimal(3_000_000.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(3_000).formatted(.currency(code: "GBP").decimalSeparator(strategy: .automatic)) // "£3,000.00"
Decimal(3_000).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: Scale
Decimal(10).formatted(.currency(code: "GBP").scale(1)) // "£10.00"
Decimal(10).formatted(.currency(code: "GBP").scale(1.5)) // "£15.00"
Decimal(10).formatted(.currency(code: "GBP").scale(-1.5)) // "-£15.00"
Decimal(10).formatted(.currency(code: "GBP").scale(10)) // "£100.00"
// MARK: - Locale
Decimal(10).formatted(.currency(code: "GBP").presentation(.fullName).locale(Locale(identifier: "fr_FR"))) // "10,00 livres sterling"
// 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"
// MARK: Compositing
Decimal(10).formatted(.currency(code: "GBP").scale(200.0).sign(strategy: .always()).presentation(.fullName)) // "+2,000.00 British pounds"
// MARK: - Attributed String
Decimal(10).formatted(.currency(code: "GBP").scale(200.0).sign(strategy: .always()).presentation(.fullName).attributed)
// MARK: - Initializing
let frenchStyle = IntegerFormatStyle<Int>.Currency(code: "GBP")
.sign(strategy: .always())
.presentation(.fullName)
.locale(Locale(identifier: "fr_FR"))
frenchStyle.format(1_000) // "+1 000,00 livres sterling"
1_000.formatted(frenchStyle) // "+1 000,00 livres sterling"
// MARK: - Parsing
let parsingStyle = Decimal.FormatStyle.Currency(code: "GBP").presentation(.fullName)
try? parsingStyle.parseStrategy.parse("10.00 British pounds") // 10
try? Decimal("10.00 British pounds", strategy: parsingStyle.parseStrategy) // 10
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)!
let style = Date.FormatStyle()
.day()
.month()
.year()
.hour()
.minute()
.second()
twosday.formatted(style) // "Feb 22, 2022, 2:22:22 AM"
// Era
twosday.formatted(Date.FormatStyle().era()) // ""
twosday.formatted(Date.FormatStyle().era(.abbreviated)) // ""
twosday.formatted(Date.FormatStyle().era(.narrow)) // ""
twosday.formatted(Date.FormatStyle().era(.wide)) // ""
// Year
twosday.formatted(Date.FormatStyle().year()) // ""
twosday.formatted(Date.FormatStyle().year(.defaultDigits)) // ""
twosday.formatted(Date.FormatStyle().year(.twoDigits)) // ""
twosday.formatted(Date.FormatStyle().year(.extended())) // ""
twosday.formatted(Date.FormatStyle().year(.extended(minimumLength: 5))) // ""
twosday.formatted(Date.FormatStyle().year(.padded(5))) // ""
twosday.formatted(Date.FormatStyle().year(.relatedGregorian())) // ""
twosday.formatted(Date.FormatStyle().year(.relatedGregorian(minimumLength: 5))) // ""
// Quarter
twosday.formatted(Date.FormatStyle().quarter()) // ""
twosday.formatted(Date.FormatStyle().quarter(.twoDigits)) // ""
twosday.formatted(Date.FormatStyle().quarter(.wide)) // ""
twosday.formatted(Date.FormatStyle().quarter(.narrow)) // ""
twosday.formatted(Date.FormatStyle().quarter(.abbreviated)) // ""
twosday.formatted(Date.FormatStyle().quarter(.oneDigit)) // ""
// Month
twosday.formatted(Date.FormatStyle().month()) // ""
twosday.formatted(Date.FormatStyle().month(.defaultDigits)) // ""
twosday.formatted(Date.FormatStyle().month(.abbreviated)) // ""
twosday.formatted(Date.FormatStyle().month(.narrow)) // ""
twosday.formatted(Date.FormatStyle().month(.wide)) // ""
twosday.formatted(Date.FormatStyle().month(.twoDigits)) // ""
// Week
twosday.formatted(Date.FormatStyle().week()) // ""
twosday.formatted(Date.FormatStyle().week(.defaultDigits)) // ""
twosday.formatted(Date.FormatStyle().week(.twoDigits)) // ""
twosday.formatted(Date.FormatStyle().week(.weekOfMonth)) // ""
// Day
twosday.formatted(Date.FormatStyle().day()) // 22 // ""
twosday.formatted(Date.FormatStyle().day(.defaultDigits)) // 22 // ""
twosday.formatted(Date.FormatStyle().day(.ordinalOfDayInMonth)) // 4 // ""
twosday.formatted(Date.FormatStyle().day(.twoDigits)) // 22 // ""
twosday.formatted(Date.FormatStyle().day(.julianModified())) // "2459633" // ""
twosday.formatted(Date.FormatStyle().day(.julianModified(minimumLength: 8))) // "02459633" // ""
// Day of Year
twosday.formatted(Date.FormatStyle().dayOfYear()) // ""
twosday.formatted(Date.FormatStyle().dayOfYear(.defaultDigits)) // ""
twosday.formatted(Date.FormatStyle().dayOfYear(.twoDigits)) // ""
twosday.formatted(Date.FormatStyle().dayOfYear(.threeDigits)) // ""
// Weekday
twosday.formatted(Date.FormatStyle().weekday()) // ""
twosday.formatted(Date.FormatStyle().weekday(.wide)) // ""
twosday.formatted(Date.FormatStyle().weekday(.narrow)) // ""
twosday.formatted(Date.FormatStyle().weekday(.abbreviated)) // ""
twosday.formatted(Date.FormatStyle().weekday(.short)) // ""
twosday.formatted(Date.FormatStyle().weekday(.twoDigits)) // ""
twosday.formatted(Date.FormatStyle().weekday(.oneDigit)) // ""
// Hour
twosday.formatted(Date.FormatStyle().hour()) // ""
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .wide))) // ""
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .narrow))) // ""
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .omitted))) // ""
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .abbreviated))) // ""
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .wide))) // ""
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .narrow))) // ""
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .omitted))) // ""
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .abbreviated))) // ""
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .wide))) // ""
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .narrow))) // ""
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .omitted))) // ""
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .abbreviated))) // ""
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .wide))) // ""
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .narrow))) // ""
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .omitted))) // ""
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .abbreviated))) // ""
// Minute
twosday.formatted(Date.FormatStyle().minute()) // ""
twosday.formatted(Date.FormatStyle().minute(.defaultDigits)) // ""
twosday.formatted(Date.FormatStyle().minute(.twoDigits)) // ""
// Second
twosday.formatted(Date.FormatStyle().second()) // ""
twosday.formatted(Date.FormatStyle().second(.twoDigits)) // ""
twosday.formatted(Date.FormatStyle().second(.defaultDigits)) // ""
// Fractional Second
twosday.formatted(Date.FormatStyle().secondFraction(.fractional(10))) // ""
twosday.formatted(Date.FormatStyle().secondFraction(.milliseconds(10))) // ""
// Time Zone
twosday.formatted(Date.FormatStyle().timeZone()) // ""
twosday.formatted(Date.FormatStyle().timeZone(.exemplarLocation)) // ""
twosday.formatted(Date.FormatStyle().timeZone(.genericLocation)) // ""
twosday.formatted(Date.FormatStyle().timeZone(.genericName(.short))) // ""
twosday.formatted(Date.FormatStyle().timeZone(.genericName(.long))) // ""
twosday.formatted(Date.FormatStyle().timeZone(.identifier(.short))) // ""
twosday.formatted(Date.FormatStyle().timeZone(.identifier(.long))) // ""
twosday.formatted(Date.FormatStyle().timeZone(.iso8601(.short))) // ""
twosday.formatted(Date.FormatStyle().timeZone(.iso8601(.long))) // ""
twosday.formatted(Date.FormatStyle().timeZone(.localizedGMT(.short))) // ""
twosday.formatted(Date.FormatStyle().timeZone(.localizedGMT(.long))) // ""
twosday.formatted(Date.FormatStyle().timeZone(.specificName(.short))) // ""
twosday.formatted(Date.FormatStyle().timeZone(.specificName(.long))) // ""
// Locale
twosday.formatted(Date.FormatStyle().locale(Locale(identifier: "fr_FR"))) // ""
// Attributed String Output
twosday.formatted(Date.FormatStyle().attributed) // ""
// Parsing
let dateString = "Feb 22, 2022, 2:22:22 AM"
let dateStyle = Date.FormatStyle()
.day()
.month()
.year()
.hour()
.minute()
.second()
try? dateStyle.parse(dateString)
try? Date(dateString, strategy: dateStyle.parseStrategy)
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
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.secondFraction(.fractional(2))) // "00"
twosday.formatted(.dateTime.secondFraction(.milliseconds(1))) // "8542000"
twosday.formatted(.dateTime.timeZone()) // "MST"
twosday.formatted(.dateTime.week()) // "9"
twosday.formatted(.dateTime.weekday()) // "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"
twosday.formatted(.dateTime.dayOfYear(.defaultDigits)) // "53"
twosday.formatted(.dateTime.dayOfYear(.threeDigits)) // "053"
twosday.formatted(.dateTime.dayOfYear(.twoDigits)) // "53"
twosday.formatted(.dateTime.era(.abbreviated)) // "AD"
twosday.formatted(.dateTime.era(.narrow)) // "A"
twosday.formatted(.dateTime.era(.wide)) // "Anno Domini"
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .wide))) // "2 AM"
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .narrow))) // "2 a"
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .abbreviated))) // "2 AM"
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .omitted))) // "02"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .wide))) // "02 AM"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .narrow))) // "02 a"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .abbreviated))) // "02 AM"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .omitted))) // "02"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .wide))) // "2 AM"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .narrow))) // "2 a"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .abbreviated))) // "2 AM"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .omitted))) // "02"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .wide))) // "02 AM"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .narrow))) // "02 a"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .abbreviated))) // "02 AM"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .omitted))) // "02"
twosday.formatted(.dateTime.minute(.twoDigits)) // "22"
twosday.formatted(.dateTime.minute(.defaultDigits)) // "22"
twosday.formatted(.dateTime.month(.defaultDigits)) // "2"
twosday.formatted(.dateTime.month(.twoDigits)) // "02"
twosday.formatted(.dateTime.month(.wide)) // "February"
twosday.formatted(.dateTime.month(.abbreviated)) // "Feb"
twosday.formatted(.dateTime.month(.narrow)) // "F"
twosday.formatted(.dateTime.quarter(.narrow)) // "1"
twosday.formatted(.dateTime.quarter(.abbreviated)) // "Q1"
twosday.formatted(.dateTime.quarter(.wide)) // "1st quarter"
twosday.formatted(.dateTime.quarter(.twoDigits)) // "01"
twosday.formatted(.dateTime.quarter(.oneDigit)) // "1"
twosday.formatted(.dateTime.second(.twoDigits)) // "22"
twosday.formatted(.dateTime.second(.defaultDigits)) // "22"
twosday.formatted(.dateTime.secondFraction(.fractional(2))) // "00"
twosday.formatted(.dateTime.secondFraction(.milliseconds(1))) // "8542000"
twosday.formatted(.dateTime.timeZone(.exemplarLocation)) // "Edmonton"
twosday.formatted(.dateTime.timeZone(.genericLocation)) // "Edmonton Time"
twosday.formatted(.dateTime.timeZone(.genericName(.long))) // "Mountain Time"
twosday.formatted(.dateTime.timeZone(.genericName(.short))) // "MT"
twosday.formatted(.dateTime.timeZone(.identifier(.short))) // "caedm"
twosday.formatted(.dateTime.timeZone(.identifier(.long))) // "America/Edmonton"
twosday.formatted(.dateTime.timeZone(.iso8601(.long))) // "-07:00"
twosday.formatted(.dateTime.timeZone(.iso8601(.short))) // "-07:00"
twosday.formatted(.dateTime.timeZone(.specificName(.short))) // "MST"
twosday.formatted(.dateTime.timeZone(.specificName(.long))) // "Mountain Standard Time"
twosday.formatted(.dateTime.week(.defaultDigits)) // "9"
twosday.formatted(.dateTime.week(.twoDigits)) // "09"
twosday.formatted(.dateTime.week(.weekOfMonth)) // "9"
twosday.formatted(.dateTime.weekday(.abbreviated)) // "Tue"
twosday.formatted(.dateTime.weekday(.twoDigits)) // "3"
twosday.formatted(.dateTime.weekday(.short)) // "Tu"
twosday.formatted(.dateTime.weekday(.oneDigit)) // "3"
twosday.formatted(.dateTime.weekday(.wide)) // "Tuesday"
twosday.formatted(.dateTime.weekday(.narrow)) // "T"
twosday.formatted(.dateTime.year(.twoDigits)) // "22"
twosday.formatted(.dateTime.year(.defaultDigits)) // "2022"
twosday.formatted(.dateTime.year(.extended())) // "22"
twosday.formatted(.dateTime.year(.extended(minimumLength: 2))) // "2022"
twosday.formatted(.dateTime.year(.padded(10))) // "0000002022"
twosday.formatted(.dateTime.year(.relatedGregorian())) // "2022"
twosday.formatted(.dateTime.year(.relatedGregorian(minimumLength: 2))) // "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)
// MARK: - Initializing
let frenchStyle = Date.FormatStyle()
.year(.twoDigits)
.month(.twoDigits)
.weekday(.wide)
.day(.twoDigits)
.hour(.twoDigits(amPM: .wide))
.locale(Locale(identifier: "fr_FR"))
twosday.formatted(frenchStyle) // "mardi 22/02/22 à 02 h"
frenchStyle.format(twosday) // "mardi 22/02/22 à 02 h"
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) // "2022-02-22T09:22:22.000Z"
let isoStyle = Date.ISO8601FormatStyle(timeZone: TimeZone(secondsFromGMT: 0)!)
.year()
.day()
.month()
.dateSeparator(.dash)
.dateTimeSeparator(.standard)
.timeSeparator(.colon)
.timeZoneSeparator(.colon)
.time(includingFractionalSeconds: true)
.locale(Locale(identifier: "fr_FR"))
isoStyle.format(twosday) // "2022-02-22T09:22:22.000"
twosday.formatted(isoStyle) // "2022-02-22T09:22:22.000"
// MARK: - Parsing
let parsingStyle = Date.ISO8601FormatStyle(timeZone: TimeZone(secondsFromGMT: 0)!)
.year()
.day()
.month()
.dateSeparator(.dash)
.dateTimeSeparator(.standard)
.timeSeparator(.colon)
.timeZoneSeparator(.colon)
.time(includingFractionalSeconds: true)
try? parsingStyle.parseStrategy.parse("2022-02-22T09:22:22.000")
try? parsingStyle.parse("2022-02-22T09:22:22.000")
try? Date("2022-02-22T09:22:22.000", strategy: parsingStyle.parseStrategy)
import Foundation
let thePast = Calendar(identifier: .gregorian).date(byAdding: .day, value: -14, to: Date())!
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)
// Note: These are grouped due to the fact that outputting everything in one string causes the output to be an empty string
let groupOne = Date.FormatString("""
Cyclic Year
.abbreviated: \(cyclicYear: .abbreviated)
.narrow: \(cyclicYear: .narrow)
.wide: \(cyclicYear: .wide)
Day
.defaultDigits: \(day: .defaultDigits)
.julianModified: \(day: .julianModified())x
.julianModified(minimumLength: 2): \(day: .julianModified(minimumLength: 2))
.ordinalOfDayInMonth: \(day: .ordinalOfDayInMonth)
.twoDigits: \(day: .twoDigits)
Day of Year
.defaultDigits: \(dayOfYear: .defaultDigits)
.threeDigits: \(dayOfYear: .threeDigits)
.twoDigits: \(dayOfYear: .twoDigits)
Day Period
.conversational(.abbreviated)): \(dayPeriod: .conversational(.abbreviated))
.conversational(.narrow)): \(dayPeriod: .conversational(.narrow))
.conversational(.wide)): \(dayPeriod: .conversational(.wide))
.standard(.abbreviated)): \(dayPeriod: .standard(.abbreviated))
.standard(.narrow)): \(dayPeriod: .standard(.narrow))
.standard(.wide)): \(dayPeriod: .standard(.wide))
.with12s(.abbreviated)): \(dayPeriod: .with12s(.abbreviated))
.with12s(.narrow)): \(dayPeriod: .with12s(.narrow))
.with12s(.wide)): \(dayPeriod: .with12s(.wide))
Era
.abbreviated: \(era: .abbreviated)
.narrow: \(era: .narrow)
.wide: \(era: .wide)
Hour
.defaultDigits(clock: .twelveHour, hourCycle: .oneBased)): \(hour: .defaultDigits(clock: .twelveHour, hourCycle: .oneBased))
.defaultDigits(clock: .twelveHour, hourCycle: .zeroBased)): \(hour: .defaultDigits(clock: .twelveHour, hourCycle: .zeroBased))
.defaultDigits(clock: .twentyFourHour, hourCycle: .oneBased)): \(hour: .defaultDigits(clock: .twentyFourHour, hourCycle: .oneBased))
.defaultDigits(clock: .twentyFourHour, hourCycle: .zeroBased)): \(hour: .defaultDigits(clock: .twentyFourHour, hourCycle: .zeroBased))
Minute
.defaultDigits: \(minute: .defaultDigits)
.twoDigits: \(minute: .twoDigits)
Month
.abbreviated: \(month: .abbreviated)
.defaultDigits: \(month: .defaultDigits)
.narrow: \(month: .narrow)
.twoDigits: \(month: .twoDigits)
.wide: \(month: .wide)
""")
let groupTwo = Date.FormatString("""
Second
.defaultDigits: \(second: .defaultDigits)
.twoDigits: \(second: .twoDigits)
Second Fraction
.fractional(10)): \(secondFraction: .fractional(10))
.milliseconds(10)): \(secondFraction: .milliseconds(10))
Standalone Month
.abbreviated: \(standaloneMonth: .abbreviated)
.defaultDigits: \(standaloneMonth: .defaultDigits)
.narrow: \(standaloneMonth: .narrow)
.twoDigits: \(standaloneMonth: .twoDigits)
.wide: \(standaloneMonth: .wide)
""")
let groupThree = Date.FormatString("""
Standalone Quarter
.abbreviated: \(standaloneQuarter: .abbreviated)
.narrow: \(standaloneQuarter: .narrow)
.oneDigit: \(standaloneQuarter: .oneDigit)
.twoDigits: \(standaloneQuarter: .twoDigits)
.wide: \(standaloneQuarter: .wide)
Standalone Weekday
.abbreviated: \(standaloneWeekday: .abbreviated)
.narrow: \(standaloneWeekday: .narrow)
.oneDigit: \(standaloneWeekday: .oneDigit)
.short: \(standaloneWeekday: .short)
.wide: \(standaloneWeekday: .wide)
Time Zone
.exemplarLocation: \(timeZone: .exemplarLocation)
.genericLocation: \(timeZone: .genericLocation)
.identifier(.long)): \(timeZone: .identifier(.long))
.identifier(.short)): \(timeZone: .identifier(.short))
.iso8601(.long)): \(timeZone: .iso8601(.long))
.iso8601(.short)): \(timeZone: .iso8601(.short))
.localizedGMT(.long)): \(timeZone: .localizedGMT(.long))
.localizedGMT(.short)): \(timeZone: .localizedGMT(.short))
.specificName(.long)): \(timeZone: .specificName(.long))
.specificName(.short)): \(timeZone: .specificName(.short))
Week
.defaultDigits: \(week: .defaultDigits)
.twoDigits: \(week: .twoDigits)
.weekOfMonth: \(week: .weekOfMonth)
Weekday
.abbreviated: \(weekday: .abbreviated)
.narrow: \(weekday: .narrow)
.oneDigit: \(weekday: .oneDigit)
.short: \(weekday: .short)
.twoDigits: \(weekday: .twoDigits)
.wide: \(weekday: .wide)
Year
.defaultDigits: \(year: .defaultDigits)
.extended()): \(year: .extended())
.extended(minimumLength: 4)): \(year: .extended(minimumLength: 4))
.padded(10)): \(year: .padded(10))
.relatedGregorian()): \(year: .relatedGregorian())
.relatedGregorian(minimumLength: 4)): \(year: .relatedGregorian(minimumLength: 4))
.twoDigits: \(year: .twoDigits)
Year For Week of Year
.defaultDigits: \(yearForWeekOfYear: .defaultDigits)
.padded(4)): \(yearForWeekOfYear: .padded(4))
.twoDigits): \(yearForWeekOfYear: .twoDigits)
""")
let verbatimGroupOne = Date.VerbatimFormatStyle(
format: groupOne,
timeZone: TimeZone.current,
calendar: .current
)
let verbatimGroupTwo = Date.VerbatimFormatStyle(
format: groupTwo,
timeZone: TimeZone.current,
calendar: .current
)
let verbatimGroupThree = Date.VerbatimFormatStyle(
format: groupThree,
timeZone: TimeZone.current,
calendar: .current
)
print(verbatimGroupOne.format(twosday))
print(verbatimGroupTwo.format(twosday))
print(verbatimGroupThree.format(twosday))
/* OUTPUT
Cyclic Year
.abbreviated: 2022
.narrow: 02022
.wide: 2022
Day
.defaultDigits: 22
.julianModified: 2459633x
.julianModified(minimumLength: 2): 2459633
.ordinalOfDayInMonth: 4
.twoDigits: 22
Day of Year
.defaultDigits: 53
.threeDigits: 053
.twoDigits: 53
Day Period
.conversational(.abbreviated)): AM
.conversational(.narrow)): AM
.conversational(.wide)): AM
.standard(.abbreviated)): AM
.standard(.narrow)): AM
.standard(.wide)): AM
.with12s(.abbreviated)): AM
.with12s(.narrow)): AM
.with12s(.wide)): AM
Era
.abbreviated: CE
.narrow: CE
.wide: CE
Hour
.defaultDigits(clock: .twelveHour, hourCycle: .oneBased)): 2
.defaultDigits(clock: .twelveHour, hourCycle: .zeroBased)): 2
.defaultDigits(clock: .twentyFourHour, hourCycle: .oneBased)): 2
.defaultDigits(clock: .twentyFourHour, hourCycle: .zeroBased)): 2
Minute
.defaultDigits: 22
.twoDigits: 22
Month
.abbreviated: M02
.defaultDigits: 2
.narrow: 2
.twoDigits: 02
.wide: M02
Second
.defaultDigits: 22
.twoDigits: 22
Second Fraction
.fractional(10)): 000000000
.milliseconds(10)): 008542000
Standalone Month
.abbreviated: M02
.defaultDigits: 2
.narrow: 2
.twoDigits: 02
.wide: M02
Standalone Quarter
.abbreviated: Q1
.narrow: 1
.oneDigit: 1
.twoDigits: 01
.wide: Q1
Standalone Weekday
.abbreviated: Tue
.narrow: T
.oneDigit: 3
.short: Tue
.wide: Tue
Time Zone
.exemplarLocation: Edmonton
.genericLocation: Edmonton
.identifier(.long)): America/Edmonton
.identifier(.short)): caedm
.iso8601(.long)): -07:00
.iso8601(.short)): -0700
.localizedGMT(.long)): GMT-07:00
.localizedGMT(.short)): GMT-7
.specificName(.long)): GMT-07:00
.specificName(.short)): GMT-7
Week
.defaultDigits: 9
.twoDigits: 09
.weekOfMonth: 4
Weekday
.abbreviated: Tue
.narrow: T
.oneDigit: 3
.short: Tue
.twoDigits: 03
.wide: Tue
Year
.defaultDigits: 2022
.extended()): 2022
.extended(minimumLength: 4)): 2022
.padded(10)): 0000002022
.relatedGregorian()): 2022
.relatedGregorian(minimumLength: 4)): 2022
.twoDigits: 22
Year For Week of Year
.defaultDigits: 2022
.padded(4)): 2022
.twoDigits): 22
*/
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.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
// MARK: - Measurement.FormatStyle
let gForce = Measurement(value: 1.0, unit: UnitAcceleration.gravity)
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
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"
// MARK: Customizing Item Display
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, [Date]>.init(memberStyle: .dateTime.year())
importantDates.formatted(yearStyle) // "2000 and 1969"
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."
// Parsing
let parsingStyle = PersonNameComponents.FormatStyle(style: .long)
// namePrefix: Dr givenName: Elizabeth middleName: Jillian familyName: Smith nameSuffix: Esq.
try? parsingStyle.parseStrategy.parse("Dr Elizabeth Jillian Smith Esq.")
try? PersonNameComponents("Dr Elizabeth Jillian Smith Esq.", strategy: parsingStyle.parseStrategy)
try? PersonNameComponents("Dr Elizabeth Jillian Smith Esq.")
import Foundation
let terabyte: Int64 = 1_000_000_000_000
let formatter = ByteCountFormatter()
formatter.countStyle = .binary
formatter.allowedUnits
formatter.includesActualByteCount
formatter.countStyle = .file
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: .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"
// .gb, .tb, .pb, .eb, .zb, and .ybOrHigher cause a FatalError (Feedback FB10031442)
// FIXED IN iOS 16
// terabyte.formatted(.byteCount(style: .file, allowedUnits: .gb))
// Use the MeasurementFormatStyle instead (UnitInformationStorage)
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
// MARK: - Customizing Existing FormatStyles
struct ToYen: FormatStyle {
typealias FormatInput = Decimal
typealias FormatOutput = String
static let multiplier = 100.0
static let yenCurrencyCode = "jpy"
let formatter: Decimal.FormatStyle.Currency
var attributed: ToYen.AttributedStyle = AttributedStyle()
init(locale: Locale? = nil) {
formatter = Decimal.FormatStyle.Currency(code: Self.yenCurrencyCode)
.scale(Self.multiplier)
.locale(locale ?? .current)
}
func format(_ value: Decimal) -> String {
formatter.format(value)
}
// This is an optional protocol method, but needed to support locale switching
func locale(_ locale: Locale) -> ToYen {
.init(locale: locale)
}
}
// MARK: Add Attributed Style support
extension ToYen {
struct AttributedStyle: FormatStyle {
typealias FormatInput = Decimal
typealias FormatOutput = AttributedString
func format(_ value: Decimal) -> AttributedString {
ToYen().formatter.attributed.format(value)
}
}
}
// MARK: Extend `FormatStyle` to simplify access
extension FormatStyle where Self == ToYen {
static var toYen: ToYen { .init() }
}
// MARK: - Usage Examples
Decimal(30.0)
Decimal(30).formatted(ToYen()) // "¥3,000"
Decimal(30).formatted(.toYen) // "¥3,000")
Decimal(30).formatted(.toYen.locale(Locale(identifier: "zh_CN"))) // "JP¥3,000"
Decimal(30).formatted(.toYen.attributed)
// MARK: - Creating a FormatStyle for a custom data type
struct ISBN {
let prefix: String
let registrationGroup: String
let registrant: String
let publication: String
let checkDigit: String
}
extension ISBN {
func formatted() -> String {
ISBN.FormatStyle().format(self)
}
func formatted<F: Foundation.FormatStyle>(_ format: F) -> F.FormatOutput where F.FormatInput == ISBN {
format.format(self)
}
}
extension ISBN {
struct FormatStyle: Codable, Sendable, Hashable {
enum DelimiterStrategy: Codable {
case hyphen
case none
}
let strategy: DelimiterStrategy
init(delimiter strategy: DelimiterStrategy = .hyphen) {
self.strategy = strategy
}
}
}
// MARK: - ISBN.FormatStyle instance methods
extension ISBN.FormatStyle {
func delimiter(_ strategy: DelimiterStrategy = .hyphen) -> Self {
.init(delimiter: strategy)
}
}
// MARK: - ISBN.FormatStyle `FormatStyle` conforma1tion
extension ISBN.FormatStyle: FormatStyle {
func format(_ value: ISBN) -> String {
switch strategy {
case .hyphen:
return "\(value.prefix)-\(value.registrationGroup)-\(value.registrant)-\(value.publication)-\(value.checkDigit)"
case .none:
return "\(value.prefix)\(value.registrationGroup)\(value.registrant)\(value.publication)\(value.checkDigit)"
}
}
}
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
extension FormatStyle where Self == ISBN.FormatStyle {
static var isbn: Self { .init() }
}
let isbn = ISBN(prefix: "978", registrationGroup: "17", registrant: "85889", publication: "01", checkDigit: "1")
isbn.formatted() // "978-17-85889-01-1"
isbn.formatted(.isbn) // "978-17-85889-01-1"
isbn.formatted(.isbn.delimiter(.none)) // "9781785889011"
isbn.formatted(ISBN.FormatStyle()) // "978-17-85889-01-1"
isbn.formatted(ISBN.FormatStyle(delimiter: .none)) // "9781785889011"
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