Expressible by String Interpolation
่ฎไธๅๅๅฅๅฏไปฅ็จ String Interpolation ไพๅๅงๅ็ฉไปถ (Initialization)ใ
ๅ่จญ MyType ๆฏไธๅ้ตๅพช ExpressibleByStringInterpolation ็ๅๅฅ๏ผๅ Swift ๆๅฐไธๅ้่ก็จๅผ็ขผ๏ผ
// count: 123456789012 34567
let s: MyType = "The time is \(time) now." // interpolated literal
// โฐโโโโ L โโโโฏโฐโโIโโโฏโฐโLโโฏ
// L: Literal (ๅ
ฑ 17 ๅๅญๆฏ)
// I: Interpolation (ๅ
ฑ 1 ๅ)
่ชๅ่ฝๅ็บ๏ผ
// ---------------------------------------------------------
// interpolated literal -> MyType.StringInterpolation
// ---------------------------------------------------------
// โญ๏ธ 1. (init) ๅ
ๅๅงๅ็จไพ็ตๅๅญไธฒ็ interpolation ็ฉไปถ๏ผ
var interpolation = MyType.StringInterpolation(
literalCapacity : 17,
interpolationCount: 1
)
// โญ๏ธ 2. (configure) ๆ นๆๅ interpolated literal ๅฅ็จ็ธๅฐๆ็็ตๅๆนๆณ๏ผ
// let s: MyType = "The time is \(time) now."
// โฐโโโโ L โโโโฏโฐโโIโโโฏโฐโLโโฏ
interpolation.appendLiteral("The time is ") // L
interpolation.appendInterpolation(time) // I
interpolation.appendLiteral(" now.") // L
// ---------------------------------------------------------
// MyType.StringInterpolation -> MyType
// ---------------------------------------------------------
// โญ๏ธ 3. ไฝฟ็จ MyType(stringInterpolation:) ๅณๅๆฐ็ฉไปถ
let s = MyType(stringInterpolation: interpolation)
ๆดๅ้็จๅฐฑๆฏๅฐ interpolated literal ่ฝ็บ MyType.StringInterpolation๏ผ็ถๅพๅๅฐ้ๅ MyType.StringInterpolation ็ฉไปถ่ฝ็บ MyType ๅณๅใ
้ไนๅฐฑๆฏ็บไป้บผ็ถๆๅ่ฆๅฐ MyType ้ตๅพชๆผ ExpressibleByStringInterpolation ๅๅฎๆ๏ผๅฟ ้ ๅฏฆ็พไธๅๆขไปถ๏ผ
MyType.StringInterpolation (้ตๅพช StringInterpolationProtocol)๏ผ
.init(literalCapacity:interpolationCount:) (โญ๏ธ 1)
.appendLiteral()ใ.appendInterpolation() (โญ๏ธ 2)
MyType๏ผ
.init(stringLiteral:) (โญ๏ธ ExpressibleByStringLiteral)
.init(stringInterpolation:) (โญ๏ธ 3)
็ถๆๅไฝฟ็จไธๅ่ชๆณ๏ผ
// โญ๏ธ 1. conform to `ExpressibleByStringInterpolation`
extension MyType: ExpressibleByStringInterpolation {
// โญ๏ธ 2. implement init(stringLiteral:)
init(stringLiteral value: String) {
// ...
}
}
Swift ๆ่ชๅ๏ผ
ไฝฟ็จ DefaultStringInterpolation ็ถไฝๆฏ MyType.StringInterpolationใ
ไฝฟ็จ้ ่จญ็ .init(stringInterpolation:)๏ผ็ดๆฅๅฐ interpolated literal ไธ็ตฆ .init(stringLiteral:) ่็ใ
ๆญคๆ๏ผMyType ๅฐฑๆ่ชๅ้ตๅพช ExpressibleByStringInterpolation๏ผ่็ก้ๅๅๅ ถไป่จญๅฎใ่ซ็ไธ้ข็็ฏไพ๏ผ
// --------------------
// declarations
// --------------------
// File
struct File {}
// Path
struct Path {
var string: String
}
// loadFile()
@discardableResult
func loadFile(_ path: Path) -> File {
print("file: \(path.string) loaded")
return File()
}
// ----------------------------------------
// ExpressibleByStringInterpolation
// ----------------------------------------
// โญ๏ธ 1. conform to `ExpressibleByStringInterpolation`
extension Path: ExpressibleByStringInterpolation {
// โญ๏ธ 2. implement init(stringLiteral:)
init(stringLiteral value: String) {
self.string = value
}
}
// -------------
// main
// -------------
// Path(string:)
loadFile(Path(string: "~/documents/article.md"))
// file: ~/documents/article.md loaded
// โญ๏ธ `Path` conforms to `ExpressibleByStringLiteral` (automatically)
loadFile("~/documents/article.md")
// file: ~/documents/article.md loaded
let username = "joe"
// โญ๏ธ `Path` conforms to `ExpressibleByStringInterpolation`
let path: Path = "/users/\(username)/file.txt"
loadFile(path)
// file: /users/joe/file.txt loaded
// ---------------------
// declarations
// ---------------------
struct HTMLComponent {
let description: String
}
// โญ๏ธ for: print()
extension HTMLComponent: CustomStringConvertible {}
// โญ๏ธ ExpressibleByStringInterpolation
// -----------------------------------
// 1. conforms to `ExpressibleByStringLiteral`
// 2. conforms to `ExpressibleByStringInterpolation`
extension HTMLComponent: ExpressibleByStringInterpolation {
// โญ๏ธ 1. ExpressibleByStringLiteral
init(stringLiteral value: String) {
description = value
}
// โญ๏ธ 2.1 ExpressibleByStringInterpolation
init(stringInterpolation: StringInterpolation) {
description = stringInterpolation.output
}
// โญ๏ธ 2.2 ExpressibleByStringInterpolation
struct StringInterpolation: StringInterpolationProtocol {
// start with an empty string
var output = ""
// --------------------------------------------------
// .init(literalCapacity:interpolationCount:)
// --------------------------------------------------
// allocate enough space to hold twice the amount of literal text
init(literalCapacity: Int, interpolationCount: Int) {
// ๅฐฑไธๅๅญไธฒ่ๅทฒ๏ผๆๅฟ
่ฆ้ ็็ฉบ้ๅ๏ผ
output.reserveCapacity(literalCapacity * 2)
}
// ----------------------
// append literal
// ----------------------
// string literal
mutating func appendLiteral(_ literal: String) {
print("appending (literal): \(literal)")
output.append(literal)
}
// -----------------------------
// append interpolations
// -----------------------------
// twitter link
mutating func appendInterpolation(twitter: String) {
print("appending (twitter): \(twitter)")
output.append("<a href=\"https://twitter/\(twitter)\">twitter</a>")
}
// email address
mutating func appendInterpolation(email: String) {
print("appending (email ): \(email)")
output.append("<a href=\"mailto:\(email)\">\(email)</a>")
}
}
}
// ------------
// main
// ------------
let text: HTMLComponent = "follow me on \(twitter: "twostraws"), or email me at \(email: "paul@hackingwithswift.com")."
print(text)
// follow me on <a href="https://twitter/twostraws">twitter</a>,
// or email me at <a href="mailto:paul@hackingwithswift.com">paul@hackingwithswift.com</a>.
// ------------
// log
// ------------
/*
appending (literal): follow me on
appending (twitter): twostraws
appending (literal): , or email me at
appending (email ): paul@hackingwithswift.com
appending (literal): .
*/
import Foundation // for `NSAttributedString`, `NSMutableAttributedString`
import UIKit // for `UIFont`
// ColoredString
struct ColoredString {
// the final attributed string, once all interpolations have finished
let value: NSAttributedString
}
// ๐
ฟ๏ธ ExpressibleByStringInterpolation
extension ColoredString: ExpressibleByStringInterpolation {
// --------------------------------------------
// โญ๏ธ `ExpressibleByStringLiteral` requirements
// --------------------------------------------
// 1. associatedtype: `StringInterpolation`
// 2. init(stringLiteral:)
// 3. init(stringInterpolation:)
// โญ๏ธ 2.
// - create an instance from a literal string
init(stringLiteral value: String) {
self.value = NSAttributedString(string: value)
}
// โญ๏ธ 3.
// - create an instance from an interpolated string
init(stringInterpolation: StringInterpolation) {
self.value = stringInterpolation.output
}
// -------------------------------------------
// โญ๏ธ 1. associatedtype: `StringInterpolation`
// -------------------------------------------
// assembles an attributed string from various interpolations
struct StringInterpolation: StringInterpolationProtocol {
// this is where we store the attributed string as we're building it
var output = NSMutableAttributedString()
// default attribute to use for text
var baseAttributes: [NSAttributedString.Key: Any] = [
.font: UIFont(name: "Georgia-Italic", size: 64) ?? .systemFont(ofSize: 64),
.foregroundColor: UIColor.white
]
// --------------------------------------------
// โญ๏ธ `StringInterpolationProtocol` requirments
// --------------------------------------------
// โญ๏ธ 1.1 can be used as a performance optimization.
init(literalCapacity: Int, interpolationCount: Int) { }
// โญ๏ธ 1.2 called when appending some raw text
mutating func appendLiteral(_ literal: String) {
// give it our base styling
let attributedString = NSAttributedString(
string : literal,
attributes: baseAttributes
)
// add it to our scratchpad string
output.append(attributedString)
}
// โญ๏ธ 1.3 called when appending a colored message
mutating func appendInterpolation(_ str: String, _ color: UIColor) {
// take a copy of our base attributes and apply the color
var attributes = baseAttributes
attributes[.foregroundColor] = color
// wrap it in a new attributed string and add it to our scratchpad
let attributedString = NSAttributedString(
string : str,
attributes: attributes
)
// append to our scratchpad string
output.append(attributedString)
}
}
}
// custom type initialized with string interpolatiopn
let str: ColoredString = "\("Red", .red), \("Orange", .orange), \("Blue", .blue)"
NSHipster โฉ ExpressibleโByStringโInterpolation
Sundell โฉ Making types expressible by string interpolation (๐ไพไธ)
Last updated