しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

SwiftのExpressibleByStringLiteralについて調べる

SwiftではExpressibleByStringLiteralに準拠させた構造体は文字列リテラルで初期化をする事が可能です。

struct MyString: ExpressibleByStringLiteral {
    let body: String
    
    init(stringLiteral value: String) {
        body = value
    }
}

初期化処理は下の通りです。

let myString: MyString = "Hello"
print(myString) // → MyString(body: "Hello")

()で数字などを埋め込むためにはExpressibleByStringInterpolationに準拠する必要があります。

struct MyString: ExpressibleByStringInterpolation {
    let body: String
    
    init(stringLiteral value: String) {
        body = value
    }
}

let myString: MyString = "Hello \(123)"

数字や文字列などを埋め込みですがStringInterpolationを拡張する事でラベル付きで行う事が可能です。

extension String.StringInterpolation {
    mutating func appendInterpolation(reversed value: String) {
        self.appendInterpolation(String(value.reversed()))
    }
}

let myString: MyString = "Hello \(reversed: "abc")"
print(myString) // → MyString(body: "Hello cba")

下のように文字列以外を埋め込む事も可能です。

extension String.StringInterpolation {
    mutating func appendInterpolation(_ value: Bool) {
        self.appendInterpolation(value ? "1" : "0")
    }
}

SwiftUIのTextに画像やTextを埋め込める機能もこの仕組みを利用しています。

struct ContentView: View {
    var body: some View {
        Text("Hello, world! \(Text("BOLD").bold()) \(Image(systemName: "globe"))")
    }
}

アプリを起動すると以下のようにTextや画像が埋め込まれた表示になります。

展開できている理由ですが、Textの引数がStringではなくLocalizedStringKeyというExpressibleByStringInterpolationに準拠した構造体になっているためです。
そのため下のように明示的にStringを渡すとTextや画像が期待通り展開されません。

Text("Hello, world! \(Text("BOLD").bold()) \(Image(systemName: "globe")) \(false)" as String)

ただLocalizedStringKeyはBoolなどの埋め込みには対応していない為、埋め込もうとすると下のようなエラーになります。

Text("Hello, world! \(Text("BOLD").bold()) \(Image(systemName: "globe")) \(false)")
// → Instance method 'appendInterpolation(_:formatter:)' requires that 'Bool' inherit from 'NSObject' というエラー

LocalizedStringKeyをBoolに対応させたい場合、下のようにLocalizedStringKey.StringInterpolationにappendInterpolationメソッドを追加します。

extension LocalizedStringKey.StringInterpolation {
    mutating func appendInterpolation(_ value: Bool) {
        self.appendInterpolation(value ? "1" : "0")
    }
}

起動すると下のような表示になります。

参考URL

[Swift] SwiftUIの不思議な機能を実現する変数展開"\()"について調べた - Qiita

Swift 5 で String Interpolation をカスタマイズする - Qiita