Swift 5.0 增强的字符串插值及格式化

Swift 5.0 增强的字符串插值及格式化

字符串插值在Swift早期版本一直存在,但是Swift 5.0之后,这项功能被大规模的改造,更强大,更便捷。

基础用法

Objective-C是这样使用的:

[NSString stringWithFormat:@"%ld", (long)count];

Swift优雅的多了:

let age = 30
print("I'm \(age).")

Swift 5.0的发布,带来了ABI稳定性,这是开发者最渴望看到的,比如原始支持ResultisMultiple(of:),等等。

除此之外,一起看看Swift 5.0是如何格式化字符串的。

首先我们创建这样一个整数:

let age = 30

然后最简单的使用字符串插值的方法:

print("Hi, I'm \(age).")

但是如何进行字符串格式呢?

在Swift 5.0中使用了新的字符串插值方案,我们可以通过扩展:String.StringInterpolation添加自己的自定义插值显示方式,如下展示:

extension String.StringInterpolation {
mutating func appendInterpolation(_ value: Int) {
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut

if let result = formatter.string(from: value as NSNumber) {
appendLiteral(result)
}
}
}

现在代码就会打印这样的结果:Hi,I'm thirty.

我们可以通过同样的技术来调节日期格式,默认的显示格式真心不是我们喜欢的。

print("Today is \(Date()).")

你会看到Swift打印的格式长这个样子:2019-02-24 10:30:22 +0000。是时候再自定义一下了。

mutating func appendInterpolation(_ value: Date) {
let formatter = DateFormatter()
formatter.dateStyle = .full

let dateString = formatter.string(from: value)
appendLiteral(dateString)
}

这看起来好多了,结果会显示这样:February 24, 2019 10:30:22.

带参显示

正如上述,appendInterpolation()是不带参数的,下面继续扩展一下,学习更高级的一些用法。

比如,我们可以用代码包装一下twitter的显示方式:

mutating func appendInterpolation(twitter: String) {
appendLiteral("<a href=\"https://twitter.com/\(twitter)\">@\(twitter)</a>")
}

现在可以这样来打印了:

print("Please follow me on Twitter: \(twitter: "itreefly").")

这才是一个参数,继续扩展更多的参数,比如两个参数的方法:

mutating func appendInterpolation(format value: Int, using style: NumberFormatter.Style) {
let formatter = NumberFormatter()
formatter.numberStyle = style

if let result = formatter.string(from: value as NSNumber) {
appendLiteral(result)
}
}

然后这样来调用:

print("Hi, I'm \(format: age, using: .spellOut).")

你可以随意使用任意的参数,作为示例,我更喜欢使用autoclosures附带一个自定义的值,代码如下:

extension String.StringInterpolation {
mutating func appendInterpolation(_ values: [String], empty defaultValue: @autoclosure () -> String) {
if values.count == 0 {
appendLiteral(defaultValue())
} else {
appendLiteral(values.joined(separator: ", "))
}
}
}

let fruits = ["Apple", "Banana", "Strawberry"]
print("Fruits: \(names, empty: "No one").")

Erica Sadun提供了一个非常”short and sweet”的示例来精简代码:

extension String.StringInterpolation {
mutating func appendInterpolation(if condition: @autoclosure () -> Bool, _ literal: StringLiteralType) {
guard condition() else { return }
appendLiteral(literal)
}
}

let doesSwiftRock = true
print("Swift rocks: \(if: doesSwiftRock, "(*)")")
print("Swift rocks \(doesSwiftRock ? "(*)" : "")")

自定义类型插值显示

针对类型struct定义插值显示,然后直接打印

struct Person {
var type: String
var action: String
}

extension String.StringInterpolation {
mutating func appendInterpolation(_ person: Person) {
appendLiteral("I'm a \(person.type) and I'm gonna \(person.action).")
}
}

let hater = Person(type: "hater", action: "hate")
print("Status check: \(hater)")

使用插值显示的JSON数据:

mutating func appendInterpolation<T: Encodable>(debug value: T) {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted

if let result = try? encoder.encode(value) {
let str = String(decoding: result, as: UTF8.self)
appendLiteral(str)
}
}

如果类型Person适配Encodable,就可以这样打印数据:

print("Here's some josn data: \(debug: hater)")

如何处理错误呢?添加方法后添加throwsprint后面跟上try即可。

mutating func appendInterpolation<T: Encodable>(debug value: T) throws {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted

let result = try encoder.encode(value)
let str = String(decoding: result, as: UTF8.self)
appendLiteral(str)
}

print(try "Status check: \(debug: hater)")

文章翻译整理来自原文:https://www.hackingwithswift.com/articles/178/super-powered-string-interpolation-in-swift-5-0

~ END ~