
文字列の補間は以前のバージョンのSwiftで行われていましたが、Swift 5.0ではこの機能が拡張され、より速く、より強力になりました。
この記事では、文字列補間の新しい可能性を検討し、これを独自のコードにどのように適用できるかを検討します。 この記事のソースはここからダウンロードすることもできます。
基本
次のような基本的な文字列補間を使用します。
let age = 38 print("You are \(age)")
私たちはこれを当たり前のことと思っていますが、かつて私たちが対処しなければならなかったものと比べると、それは大きな軽減でした。
[NSString stringWithFormat:@"%ld", (long)unreadCount];
代替策は次のとおりであったため、パフォーマンスが大幅に向上しました。
let all = s1 + s2 + s3 + s4
はい、最終結果は同じになりますが、Swiftはs1をs2に追加してs5を取得し、s5をs3に追加してs6を取得し、s6をs4に追加してs7を取得してから、すべてを割り当てる必要があります。
文字列の補間はSwift 1.0からほとんど変更されていません。唯一の重要な変更はSwift 2.1で行われ、補間で文字列リテラルを使用する機会を得ました。
print("Hi, \(user ?? "Anonymous")")
ご存知のように、Swiftはコミュニティの提案のおかげで大きく発展しています。 アイデアは議論、開発され、受け入れられるか拒否されるかのいずれかです。
そのため、5年後、Swiftの開発は行補間になりました。 Swift 5.0は、文字列補間プロセスを制御する機能を提供する新しいスーパー機能を導入しました。
試すには、次のシナリオを検討してください。 次のように新しい整数変数を設定すると:
let age = 38
次のように文字列補間を使用できることは非常に明白です。
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) } } }
これで、コードは変数全体をテキストとして出力します。「こんにちは、私は38です。」
文字列としてのデフォルトの日付ビューはあまり魅力的ではないため、同様の手法を使用して日付の書式設定を修正できます。
print("Today's date is \(Date()).")
Swiftが現在の日付を「2019-02-21 23:30:21 +0000」のような形式で表示することがわかります。 独自の日付書式を使用して、より美しくすることができます。
mutating func appendInterpolation(_ value: Date) { let formatter = DateFormatter() formatter.dateStyle = .full let dateString = formatter.string(from: value) appendLiteral(dateString) }
「2019年2月21日23:30:21」のような結果になりました。
注:チームとして一緒に作業する際の混乱を避けるために、おそらくデフォルトのSwiftメソッドをオーバーライドしないでください。 したがって、混乱を避けるために、パラメーターに任意の名前を付けてください。
mutating func appendInterpolation(format value: Int) {
次に、名前付きパラメーターを使用してこのメソッドを呼び出します。
print("Hi, I'm \(format: age).")
これで、メソッドの独自の実装を使用していることが明らかになります。
パラメーターによる補間
この変更により、文字列の補間方法を完全に制御できるようになりました。
たとえば、Twitterメッセージを処理するようにコードを書き換えることができます。
mutating func appendInterpolation(twitter: String) { appendLiteral("<a href=\"https://twitter.com/\(twitter)\">@\(twitter)</a>") }
次のように書くことができます:
print("You should follow me on Twitter: \(twitter: "twostraws").")
しかし、なぜ1つのパラメーターに限定する必要があるのでしょうか? 数値フォーマットの例では、ユーザーに1つの変換パラメーター(.spellOut)を使用するように強制することは意味がありません。したがって、2番目のパラメーターを追加してメソッドを変更します。
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).")
任意のタイプのパラメーターをいくつでも持つことができます。 デフォルト値に@autoclosureを使用する例:
extension String.StringInterpolation { mutating func appendInterpolation(_ values: [String], empty defaultValue: @autoclosure () -> String) { if values.count == 0 { appendLiteral(defaultValue()) } else { appendLiteral(values.joined(separator: ", ")) } } } let names = ["Malcolm", "Jayne", "Kaylee"] print("Crew: \(names, empty: "No one").")
@autoclosure属性を使用すると、デフォルト値として単純な値を使用したり、複雑な関数を呼び出したりできます。 メソッドでは、それらはクロージャーになります。
これで、次のような補間機能を使用せずにコードを書き換えることができると考えているかもしれません。
extension Array where Element == String { func formatted(empty defaultValue: @autoclosure () -> String) -> String { if count == 0 { return defaultValue() } else { return self.joined(separator: ", ") } } } print("Crew: \(names.formatted(empty: "No one")).")
しかし、今では呼び出しが複雑になっています。明らかに何かをフォーマットしようとしているため、これが補間のポイントです。 スウィフトのルールを覚えておいてください-不要な言葉は避けてください。
Erica Sadunは、コードを単純化する方法の非常に短くて美しい例を提供しました。
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 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)")
オブジェクトに関するデバッグ情報に触れないため、文字列補間は便利です。 デバッガで表示したり表示したりすると、そのままのデータが表示されます。
print(hater)
カスタムタイプをいくつかのパラメーターと組み合わせることができます。
extension String.StringInterpolation { mutating func appendInterpolation(_ person: Person, count: Int) { let action = String(repeating: "\(person.action) ", count: count) appendLiteral("\n\(person.type.capitalized)s gonna \(action)") } } let player = Person(type: "player", action: "play") let heartBreaker = Person(type: "heart-breaker", action: "break") let faker = Person(type: "faker", action: "fake") print("Let's sing: \(player, count: 5) \(hater, count: 5) \(heartBreaker, count: 5) \(faker, count: 5)")
もちろん、Swiftのすべての機能を使用して、独自のフォーマットを作成できます。 たとえば、 Encodableオブジェクトを取得して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 data: \(debug: faker)")
可変数のパラメーターなどの機能を使用でき、補間の実装をthrowingとしてマークすることもできます。 たとえば、JSONフォーマットシステムは、エンコードエラーが発生しても反応しませんが、これを修正して将来エラーを分析できます。
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)")
これまでに見てきたのは、文字列補間法の単なる修正です。
補間を使用したカスタムタイプの作成
これまで見てきたように、アプリケーションのデータを本当に便利な方法でフォーマットする方法の問題でしたが、文字列補間を使用して独自の型を作成することもできます。
これを実証するために、文字列補間を使用して文字列から初期化される新しい型を作成します。
struct ColoredString: ExpressibleByStringInterpolation { // - - struct StringInterpolation: StringInterpolationProtocol { // - var output = NSMutableAttributedString() // var baseAttributes: [NSAttributedString.Key: Any] = [.font: UIFont(name: "Georgia-Italic", size: 64) ?? .systemFont(ofSize: 64), .foregroundColor: UIColor.black] // , init(literalCapacity: Int, interpolationCount: Int) { } // , mutating func appendLiteral(_ literal: String) { // , print("Appending \(literal)") // let attributedString = NSAttributedString(string: literal, attributes: baseAttributes) // output.append(attributedString) } // , mutating func appendInterpolation(message: String, color: UIColor) { // print("Appending \(message)") // var coloredAttributes = baseAttributes coloredAttributes[.foregroundColor] = color // - let attributedString = NSAttributedString(string: message, attributes: coloredAttributes) output.append(attributedString) } } // , let value: NSAttributedString // init(stringLiteral value: String) { self.value = NSAttributedString(string: value) } // init(stringInterpolation: StringInterpolation) { self.value = stringInterpolation.output } } let str: ColoredString = "\(message: "Red", color: .red), \(message: "White", color: .white), \(message: "Blue", color: .blue)"
実際、内部には1つの構文糖があります。 最終部分を手動で書くことができます。
var interpolation = ColoredString.StringInterpolation(literalCapacity: 10, interpolationCount: 1) interpolation.appendLiteral("Hello") interpolation.appendInterpolation(message: "Hello", color: .red) interpolation.appendLiteral("Hello") let valentine = ColoredString(stringInterpolation: interpolation)
おわりに
ご覧のように、カスタム文字列補間を使用すると、書式設定を1か所に配置できるため、メソッド呼び出しがより簡単かつ明確になります。 また、必要なタイプを可能な限り自然に作成するための優れた柔軟性も提供します。
これは可能性の1つにすぎず、唯一の可能性ではないことに注意してください。 これは、補間を使用することもあれば、関数などを使用することもあることを意味します。 多くの開発と同様に、問題を解決するための最良の方法を常に選択する必要があります。