String Interpolation in LocalizedStringKey

The easiest way to display text in SwiftUI is to use Text view initializer that accepts a string literal, like in the example below.

Text("Hello World!")

When we create Text with a string literal, the initializer that is actually called is the one that accepts a LocalizedStringKey: init(_:tableName:bundle:comment:). This happens because LocalizedStringKey conforms to ExpressibleByStringLiteral protocol.

SwiftUI assumes that if we have a static string inside a text view, we'll likely want to localize it. SwiftUI performs a search for localizations on our behalf and falls back to displaying the key (the string literal that we passed to the initializer), if no localizations were found.

LocalizedStringKey can also be created from a string interpolation, like in the following example.

Text("Your next appointment is on \(appointmentDate).")

This functionality allows us to insert variables, images, formatted dates and even display rich text with an AttributedString or text modifiers.

In this article we'll explore what is possible to achieve by leveraging string interpolation in LocalizedStringKey and how to avoid common pitfalls.

Supported types for LocalizedStringKey interpolation

To know what types we can interpolate inside LocalizedStringKey we can check different appendInterpolation() methods it supports in documentation for LocalizedStringKey.StringInterpolation. We never call these methods manually, the compiler calls them for us when we create a LocalizedStringKey with interpolation.

Notice that if we try to interpolate something that is not supported, we will get a compile-time error. For example, if we try to insert a Button inside a LocalizedStringKey, the error will say: Instance method 'appendInterpolation' requires that 'Button<Text>' conform to '_FormatSpecifiable'.

There are many different types that can be interpolated inside a LocalizedStringKey, here I would like to go over some of the most interesting use cases.

Images

LocalizedStringKey interpolation supports appending Image views. We can interpolate symbol images or regular images.

// Interpolate a symbol image
Text("Delete item \(Image(systemName: "trash"))")

// Interpolate a regular image
Text("Completed task \(Image("checkmark"))")

Formatted data

We can interpolate data formatted using FormatStyle, such as dates, lists, numbers, measurements and more.

// Interpolate a formatted date
Text("Today is \(Date(), format: .dateTime.month().day()).")

// Interpolate a formatted list of items
let listOfFriends = ["Peter", "Jane", "Max", "Kate"]
Text("You should invite \(listOfFriends, format: .list(type: .and)).")

// Interpolate a formatted measurement
let remainingDistance = Measurement<UnitLength>(value: 20, unit: .miles)
Text("""
    The remaining distance is \(
        remainingDistance,
        format: .measurement(width: .wide, usage: .road)
    ).
""")

AttributedString

To display rich text, we can use an AttributedString. SwiftUI doesn't support all the attributes supported by UIKit and AppKit at the moment, but there are still some useful styles that we can set. For the list of supported attributes in SwiftUI we can check AttributeScopes.SwiftUIAttributes. The attributes that are not included in SwiftUI scope will be ignored by the text view.

For example, to add a background color to a portion of text, we can create an attributed string for that portion and add backgroundColor attribute. Then we can interpolate that string inside a LocalizedStringKey.

struct ContentView: View {
    var date: AttributedString {
        var result = AttributedString(
            Date().formatted(.dateTime.month().day())
        )
        result.backgroundColor = .blue
        return result
    }

    var body: some View {
        Text("Today is \(date).")
    }
}

Text

Another way to display rich text is to interpolate Text with some modifiers inside another Text. We have to make sure that we use modifiers that return Text and not some View. We can find the list of text modifiers in Text view documentation in "Styling the View’s Text" section.

Text("""
You should arrive at least \(Text("30 min").bold()) in advance.
""")

Text interpolation as shown in the example above is usually preferred over text concatenation where we use + sign to combine text views. Text interpolation can be more efficient and has better support for localization, since the order of the words can vary in different languages.

LocalizedStringKey interpolation vs DefaultStringInterpolation

I would like to point out that string interpolation inside LocalizedStringKey does not have the same functionality as string interpolation inside a regular Swift String.

For the list of types that we can interpolate inside LocalizedStringKey we can check appendInterpolation() methods in LocalizedStringKey.StringInterpolation documentation. We can compare them to appendInterpolation() methods for regular strings in documentation for DefaultStringInterpolation.

Even if we can interpolate the same type inside a LocalizedStringKey and a String, the result might not always be the same. As an example, we can compare what we get by interpolating an Image view inside a String versus inside a LocalizedStringKey.

If we want to save the string with interpolated image in a variable and pass it to our text view, then we need to be careful. If we don't indicate the variable type, it will be treated as a regular string and will be initialized with DefaultStringInterpolation instead of LocalizedStringKey.StringInterpolation.

struct ContentView: View {
    // Regular Swift `String` will be created with `DefaultStringInterpolation`,
    // and image description instead of the actual image
    // will be appended to the string.
    let textString = "Delete item \(Image(systemName: "trash"))"

    var body: some View {
        Text(textString)
    }
}

The result of passing a string variable with interpolated image to SwiftUI Text view can be surprising. We can see that instead of the SF symbol Text is displaying the image description. This is how DefaultStringInterpolation for regular strings works.

Screenshot of text view displaying a string with image description.

To correct this issue, we need to indicate that we are creating a LocalizedStringKey, not a regular string.

struct ContentView: View {
    // `LocalizedStringKey` will be created with `LocalizedStringKey.StringInterpolation`,
    // and image will be appended correctly.
    let textString: LocalizedStringKey = "Delete item \(Image(systemName: "trash"))"

    var body: some View {
        Text(textString)
    }
}

As expected, SwiftUI will display a text view with an SF Symbol inside.

Screenshot of text view displaying a string and a symbol image.