The Beauty of Swift 5 String Interpolation

Thanks to SE-0228, you can control exactly how you want string interpolations to print. Thank you Brent for giving this to us. It’s a doozy! Let me share some examples.

Consider printing optionals. You use:

"There's \(value1) and \(value2)"

And get an instant warning:

You can click the fixes to suppress the warning but you’ll still get an output that looks like this: “There’s Optional(23) and nil”.

"There's \(String(describing: value1)) and \(String(describing: value2))"

Now you can strip the “Optional” and produce “There’s 23 and nil”, allowing you to print values directly:

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

// There's 23 and nil
"There's \(value1, default: "nil") and \(value2, default: "nil")"

You might create a set of styles and custom interpolation to support consistent presentation for optional output:

extension String.StringInterpolation {
    /// Optional Interpolation Styles
    public enum OptionalStyle {
        /// Includes the word `Optional` for both `some` and `none` cases
        case descriptive
        /// Strips the word `Optional` for both `some` and `none` cases
        case stripped
        /// Uses system interpolation, which includes the word `Optional` for
        /// `some` cases but not `none`.
        case `default`
    }
    
    /// Interpolates optional values using a supplied `optStyle` style
    public mutating func appendInterpolation(_ value: T?, optStyle style: String.StringInterpolation.OptionalStyle) {
        switch style {
        // Includes the word `Optional` for both `some` and `none` cases
        case .descriptive:
            if value == nil {
                appendLiteral("Optional(nil)")
            } else {
                appendLiteral(String(describing: value))
            }
        // Strips the word `Optional` for both `some` and `none` cases
        case .stripped:
            if let value = value {
                appendInterpolation(value)
            } else {
                appendLiteral("nil")
            }
        // Uses system interpolation, which includes the word `Optional` for
        // `some` cases but not `none`.
        default:
            appendLiteral(String(describing: value))
        }
    }
    
    /// Interpolates an optional using "stripped" interpolation, omitting
    /// the word "Optional" from both `.some` and `.none` cases
    public mutating func appendInterpolation(describing value: T?) {
        appendInterpolation(value, optStyle: .stripped)
    }

}

// "There's Optional(23) and nil"
"There's \(value1, optStyle: .default) and \(value2, optStyle: .default)"

// "There's Optional(23) and Optional(nil)"
"There's \(value1, optStyle: .descriptive) and \(value2, optStyle: .descriptive)"

// "There's 23 and nil"
"There's \(describing: value1) and \(describing: value2)"

Interpolation isn’t limited to tweaking optionals. Say you want to control whether a string is added without having to use a ternary expression with an empty string:

// Include on success (Thanks, Nate Cook)
extension String.StringInterpolation {
    /// Provides Boolean-guided interpolation that succeeds only when the condition
    /// evaluates to true.
    mutating func appendInterpolation(if condition: @autoclosure () -> Bool, _ literal: StringLiteralType) {
        guard condition() else { return }
        appendLiteral(literal)
    }
}

// Old
"Cheese Sandwich \(isStarred ? "(*)" : "")"

// New
"Cheese Sandwich \(if: isStarred, "(*)")"

There’s lots more you can do and it’s all exciting and fun.

2 Comments

  • Great post on a simple example of this new Swift 5 feature. I posted this blog post on my local iOS developer FB group.

    However I have a question, regarding calling `appendLiteral` in your example. When or should we call this `appendLiteral` method directly in the appendInterpolation method implementation? There is a comment saying that we should not call this `appendLiteral` method directly in the code example in the SE.

  • Great blog post about a simple example for the new String Interpolation use cases in Swift 5. I posted this post on our local iOS developer FB group.

    However I saw a comment in the code example in the SE saying that we shouldn’t call `appendLiteral` method directly but I see you call it in your appendInterpolation method implementation. So I want to ask you should we call or when can we call this method directly?