SwiftUI attributed strings
I’ve been working on a pure SwiftUI feed reader which uses Text elements for most of the content, but using the iOS 14 SDK I had not yet implemented embedded links, as the Text element did not support attributed strings. This has changed with the new iOS 15 SDK, which adds an AttributedString struct which can be passed to Text elements in place of a String.
In addition it is now possible to pass String values with a subset of Markdown formatting to Text elements directly, to create an AttributedString, and to create the Foundation basis NSAttributedString. But this does not always work as I expected.
When I pass a String literal with Markdown I get the expected behavior; thus
Text("**This** is a _test_ with a [link](https://elegantnewt.blog).")
yields the expected result
This is a test with a link.
However if I pass a reference to the same String to the Text element then the Markdown is not parsed:
let str = "**This** is a _test_ with a [link](https://elegantnewt.blog)."
Text(str)
results in
**This** is a _test_ with a [link](https://elegantnewt.blog).
If I use a string literal but try to inject the URL via interpolation I find that the link text is correct but is not marked as a link:
let url = "https://elegantnewt.blog"
Text("**This** is a _test_ with a [link](\(url)).")
produces
This is a test with a link.
However, if I create an AttributedString instance using the same input, and then pass that to the Text element, I do get the correct result, although I need to verify that the construction succeeds:
let markdown = "**This** is a _test_ with a [link](\(url))."
if let att = try? AttributedString(markdown: markdown) {
Text(att)
}
works as expected:
This is a test with a link.
As an alternative, if I construct an NSAttributedString from the same Markdown string reference, I again get the correct rendering:
if let nsAtt = try? NSAttributedString(markdown: markdown),
let att = try? AttributedString(nsAtt, including: AttributeScopes.SwiftUIAttributes.self) {
Text(att)
}
also yields
This is a test with a link.
But I need to be careful about how I construct that NSAttributedString. For example, if I build it from the corresponding HTML markup then the bold and italic modifiers aren’t rendered.
let html = "<b>This</b> is a <i>test</i> with a <a href=\"https://elegantnewt.blog\">link</a>."
let data = Data(html.utf8)
if let htmlNSAtt = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil),
let htmlAtt = try? AttributedString(htmlNSAtt, including: AttributeScopes.SwiftUIAttributes.self) {
Text(htmlAtt)
}
results in
This is a test with a link.
Obviously I need to compare the NSAttributedString instances to determine the exact attributes being used in these different cases.