Swift 条件字符串插值

3
我在需要根据某些条件(在这种情况下是isFavorite)构建字符串的时候,常常会陷入编写以下类似代码的困境:
let me = Contact(name: "Stefan", isFavorite: true)

var message = "Contact \(me.name)"
if me.isFavorite {
    message.append(" is a favorite contact")
}

这里使用了四行代码,或者是一个复杂的三元操作符(if ? then : else),来完成如此简单的任务。我总觉得用这种方式做事情太过冗余...

在 Swift 中有没有更优雅的方法实现呢?

3个回答

7

Swift 5

确实有一个方法,我在 UIKonf 2019 上听到了 Erica Sadun 的分享,她介绍了一种使用 Swift 5 字符串插值 来一行代码实现的方法。只需使用以下可重复使用的扩展即可:

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

它能够将您的四行代码转化为一行:

"Contact: \(me.name)\(if: me.isFavorite, " is a favorite contact")"

你可以在 Erica 的文章中了解到 Swift 字符串插值的不同可能性:https://ericasadun.com/2018/12/12/the-beauty-of-swift-5-string-interpolation/

Playground

开始动手尝试吧:

import Foundation

// *****************************************************************************
// Many thanks to Erica Sadun for describing the new possibilities of string
// interpolation in Swift 5 
// *****************************************************************************


// *****************************************************************************
// Conditional Interpolation

struct Contact {
    let name: String
    let isFavorite: Bool
}
let me = Contact(name: "Stefan", isFavorite: true)

// ***************************************************************** Swift 4

var message = "Contact \(me.name)"
if me.isFavorite {
    message.append(" is favorite")
}
message // we need 4 lines to construct this message!!!
"Contact: \(me.name)\(me.isFavorite ? " is favorite" : "")" // or a complex ternary operator

// ***************************************************************** Swift 5

extension String.StringInterpolation {

    mutating func appendInterpolation(if condition: @autoclosure () -> Bool, _ literal: StringLiteralType) {
        guard condition() else { return }
        appendLiteral(literal)
    }
}
"Contact: \(me.name)\(if: me.isFavorite, " is favorite")" // simple and clean - no extras

// *****************************************************************************
// Optional Interpolation

let optionalMe: Contact? = Contact(name: "Stefan", isFavorite: true)

// ***************************************************************** Swift 4

"Contact: \(optionalMe?.name)" // shows warning

// ***************************************************************** Swift 5

// Interpolate nil value
extension String.StringInterpolation {
    /// Provides `Optional` string interpolation without forcing the
    /// use of `String(describing:)`.
    public mutating func appendInterpolation<T>(_ value: T?, default defaultValue: String) {
        if let value = value {
            appendInterpolation(value)
        } else {
            appendLiteral(defaultValue)
        }
    }
}
let nilContact: Contact? = nil
"Contact: \(nilContact?.name, default: "nil")"

// Strip `Optional`
extension String.StringInterpolation {
    /// Interpolates an optional using "stripped" interpolation, omitting
    /// the word "Optional" from both `.some` and `.none` cases
    public mutating func appendInterpolation<T>(describing value: T?) {
        if let value = value {
            appendInterpolation(value)
        } else {
            appendLiteral("nil")
        }
    }

}
"Contact: \(describing: optionalMe?.name)"

我只是想与社区分享我的所学。除了回答自己的问题,我不知道在StackOverflow上如何做到这一点。Erica的文章真的很有趣。试试看吧;-) - blackjacx
3
自问自答的问题是被鼓励的:https://stackoverflow.com/help/self-answer - Martin R
@blackjacx 接受你的答案。 - RajeshKumar R

5

你可以使用三目运算符

let msg = "Contact \(me.name)" + (me.isFavorite ? " is a favorite contact" : "")

0
我倾向于创建第二个可能为空的 String 变量,并且无条件地将其附加...
let me = Contact(name: "Stefan", isFavorite: true)
let favorite = me.isFavorite ? " is a favorite contact" : ""

var message = "Contact \(me.name)\(favorite)"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接