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.
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.