anz blog

文字列内でOptional型を埋め込んだ時のアレをStringInterpolationをいじって回避したった

2019-04-10 #Swift

ざっくりまとめ。

  • StringInterpolation と聞いてピンときますか?
    • StringInterpolation とは文字列補間という意味らしくて、文字列内で変数の値を展開してくれる例のアレです
    • "text: \(text)" こういう感じのやつです
  • この展開は便利だけれど Optional の場合ちょっと困ります
    • text: Optional("nullable") 上記の例で text が Optional だとこうなる
  • Swift 5 から StringInterpolation をカスタマイズできるのでこの問題をサクッと回避できます!

です。

環境

  • Xcode v10.2
  • Swift v5

問題

let text: String? = "nullable"
let num: Int? = nil

print("text: \(text) - num: \(num)")

これで出力されるものがどうなるかわかりますか?
Swift を嗜んでおられる人ならわかりますね。
「あーあの問題でしょ(笑)」みたいな

text: Optional("nullable") - num: nil

正解はこう出力されます。
でも期待しているものはおそらくそうではないはず。

text: nullable - num:

こういうのを期待して書いたはずです。(多分

(まぁ今の Xcode だと warning を出してくれるのでやるまでもく修正しちゃうんですけどね(笑))

回避策

要は Optional だからそうなるので、Unwrap してあげてから出力させればいいということです。
やり方はいくつかあるのですけど、1行で書くとしたら...

print("text: \(text ?? "") - num: \(num ?? 0)")

こういう感じです。
(num が nil で 0 と出ちゃうのでちょっと違いますけど)

StringInterpolation をカスタマイズして回避する

ここからが本題です。
Swift 5 ではその StringInterpolation をカスタマイズ出来るようになりました。
既存の挙動を変えることも出来るし、全く別物も作れたりします。
それで、その機能をつかえば上記問題(?)も回避できるのでは!?という話です。

extension String.StringInterpolation {

    mutating func appendInterpolation(_ value: CustomStringConvertible?) {

        guard let value = value else {
            return
        }

        appendLiteral(String(describing: value))
    }
}

let text: String? = "nullable"
let num: Int? = nil

print("text: \(text) - num: \(num)")

この出力は

text: nullable - num:

こうなります!💪
これで、Unwrap しなくても気軽に埋め込めるようにできますね!

これが良いのかどうかっていう議論は別でありますけど😇

参考