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.

Trying to get caught up, despite the busy schedule we did manage to fit in a swim in a pond yesterday.

Achieved the goal of swimming every day, but came one G&T short of finishing the gin. Homeward bound…

Crows have been using small rocks on the path to break snail shells, I saw several similar tableaus yesterday.

Setting up a GitHub account was easy, now I have to set up two-factor authentication, personal access tokens, and keychain access. There should be a better way to manage this without sacrificing security.