Daily coverage of WWDC21.
A Swift by Sundell spin-off.

A first look at Swift’s new AttributedString

Published at 08:30 GMT, 08 Jun 2021
Written by: John Sundell

This year, Apple is introducing a first-class Swift type for representing attributed strings — texts that are either styled, or marked up with other attributes, such as links. That means that Swift developers no longer need to use Objective-C’s NSAttributedString to accomplish that task, which has some really neat benefits.

Value semantics

Before we even start looking at the actual APIs and capabilities of this new AttributedString type, one big difference between it and its Objective-C counterpart is that the Swift version is a value type. That means that it’ll be passed, copied, and mutated just like other values, through Swift’s value semantics.

For example, when using NSAttributedString, in order to perform a simple mutation (in this case by appending a new attributed string to an existing one) we have to first make a mutable copy of the string, perform our append, and then — to avoid accidentally sharing mutable state — we have to remember to convert our mutable string back to an immutable one before returning it:

func appendLink(
    to string: NSAttributedString,
    label: String,
    url: URL
) -> NSAttributedString {
    let link = NSAttributedString(string: label, attributes: [.link: url])

    let copy = NSMutableAttributedString(attributedString: string)
    copy.append(link)
    return NSAttributedString(attributedString: copy)
}

Now let’s take a look at what the exact same logic looks like when using the new AttributedString type instead:

func appendLink(
    to string: AttributedString,
    label: String,
    url: URL
) -> AttributedString {
    var attributes = AttributeContainer()
    attributes.link = url

    let link = AttributedString(label, attributes: attributes)

    var string = string
    string.append(link)
    return string
}

At first glance, it might seem like the new API is actually somewhat of a downgrade, given that it requires a bit more code to accomplish the same task — but a huge benefit is that Swift will now automatically ensure that our mutable state stays local, and will take care of all of the copying and mutation logic for us, giving us a much safer and less error-prone result.

SwiftUI integration

Plus, AttributedString values can be directly passed to SwiftUI’s Text type, which makes it much easier to render texts with mixed styling or other kinds of attributes when building UIs using SwiftUI:

struct WelcomeView: View {
    var user: User
    var websiteURL: URL

    var body: some View {
        Text(attributedString)
    }

    private var attributedString: AttributedString {
        var string = AttributedString("""
        Welcome \(user.name)! Find out more about
        """)

        string.append(AttributedString(" "))

        return appendLink(
            to: string,
            label: "this app here",
            url: websiteURL
        )
    }
}

Markdown support

But that’s not all, because this year both NSAttributedString and the new AttributedString type are getting built-in support for Markdown. While only a basic set of Markdown features seem to be supported at this time, it’s still a very welcome change. So, rather than having to construct our string manually (like we did above), we can now use Markdown syntax instead:

struct WelcomeView: View {
    ...

    private var attributedString: AttributedString {
        try! AttributedString(
            markdown: """
            Welcome **\(user.name)**! \
            Find out *more* about [this app here](\(websiteURL)).
            """
        )
    }
}

The above kind of try! unwrapping should only be used in contexts where we’re 100% sure that our Markdown will always be valid, since our app will crash if that’s not the case. For other types of situations, using something like the do try catch pattern is a much better option.

Note that this new API is not a replacement for Markdown-to-HTML renders (such as Ink), but rather a convenience API for creating attributed strings using a more limited version of the full Markdown spec.

Conclusion

While this was just a very quick first look at the new AttributedString type, I’m sure that it’s going to be really useful for many developers working on Apple’s platforms — especially those who (like me) build a large part of our UIs using SwiftUI these days.

For more information about AttributedString, check out its official documentation.

Thanks for reading!

Written by: John Sundell
RaycastRaycast

Take the macOS Spotlight experience to the next level: Create Jira issues, manage GitHub pull requests and control other tools with a few keystrokes. Automate every-day tasks with scripts and boost your developer productivity by downloading Raycast for free.