Are you looking to add some style to your text in Swift with attributed string? You want to highlight important words, add links, or create beautifully formatted text, then you will want to learn about AttributedString and NSAttributedString. This goes beyonde basic text styling with font in Swift.
NSAttributedString is a class part of the Foundation framework and has been around since the early days of iOS/macOS. It’s Objective-C based and while it’s very powerful, it can be a bit verbose to use. You’ll still find it in many APIs and it’s particularly useful when working with UIKit or AppKit.
AttributedString, is a new struct introduced in iOS 15, is the modern Swift-native implementation. It’s more type-safe and easier to work with in Swift code. It is build to work especially with text views in SwiftUI. However, it is much more limited than NSAttributedString in terms of styling options.
In this guide, I’ll focus primarily on NSAttributedString as it’s still widely used and supported across all iOS/macOS versions. Plus, understanding NSAttributedString will help you better appreciate the improvements made in the newer AttributedString.
Creating and Modifying NSAttributedString
At its core, NSAttributedString is a string that lets you attach attributes (like font, color, or styling) to specific ranges of text. Unlike regular strings that maintain the same formatting throughout, NSAttributedString allows you to create rich text with varying styles within a single string.
Here’s a simple example that creates a string with a red foreground color and bold font:
let attributedString = NSAttributedString(
string: "Hello World",
attributes: [
.foregroundColor: UIColor.red,
.font: UIFont.boldSystemFont(ofSize: 16)
]
)
You pass the base string and a dictionary of attributes. The key value are the attribute names. In the above example, the key is .font
and its value is UIFont.boldSystemFont(ofSize: 16)
. You can add as many attributes as you want.
You can see the rendered attributed string in comparison to the default body text:

Adding Partial Styling
If you only want to change the styling of a subrange, you can use
NSMutableAttributedString:
let mutableString = NSMutableAttributedString(string: "This is an attributed string with multiple styles.",
attributes: [.font: UIFont.systemFont(ofSize: 17)])
Then you can add attributes to a range of the string. The following makes the foreground color of the words “This is an” blue:
mutableString.addAttribute(
.foregroundColor,
value: UIColor.blue,
range: NSRange(location: 0, length: 11)
)
You can get the range of a string like so:
let range = (mutableString.addAttribute as NSString).range(of: "This is an")
This will give you a range that has a starting location 0
and a length of 11 for the 11 characters.

You can add more addtional attributes for different parts of the string like so:
attributedString.addAttribute(
.font,
value: UIFont.boldSystemFont(ofSize: 24),
range: NSRange(location: 21, length: 21)
)

If you add attributes to ranges that already have a previous value, the newly added attributes will overwrite the old one. For example below code will change the styling of “an” from blue to red:
attributedString.setAttributes([.foregroundColor : UIColor.red],
range: NSRange(location: 8, length: 20))

Basic Styling Attributes
How to Change Color of NSAttribuedString
You can change the foreground and background color with the following attributes:
let text = NSMutableAttributedString(string: "Styling Text in Swift")
// Adding color
text.addAttributes([
.foregroundColor: UIColor.red
], range: NSRange(location: 8, length: 4))
// Adding background color
text.addAttributes([
.backgroundColor: UIColor.yellow
], range: NSRange(location: 13, length: 5))
How to Change the Font of NSAttribuedString
Here’s how to apply common text attributes, like font size, weight and traits:
let heavyFontPart = NSAttributedString(
string: "heavy font",
attributes: [
.font: UIFont.systemFont(ofSize: 20, weight: .heavy)
]
)
let boldfontPart = NSAttributedString(
string: "bold font",
attributes: [
.font: UIFont.boldSystemFont(ofSize: 20)
]
)
let italicFontPart = NSAttributedString(
string: "italic font",
attributes: [
.font: UIFont.italicSystemFont(ofSize: 20)
]
)

StrikeThrough and Underline Text
You can add attributes for strikethourgh and underline styles, where you can chose between single, double, and thick:
let strikeThroughPart = NSAttributedString(
string: "strike through text",
attributes: [
.strikethroughStyle: NSUnderlineStyle.single.rawValue,
.strikethroughColor: UIColor.blue
]
)
let underlinePart = NSAttributedString(
string: "underline text",
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.underlineColor: UIColor.red,
]
)

Adding Clickable Links and Paragraph Styles
You can add a link to a substring with the link attributes. The value should either be a NSURL or String tpye. In the following example, I linked to the Apple website:
let linkText = NSMutableAttributedString(string: "Visit Apple's website")
// Adding a link
let linkRange = NSRange(location: 6, length: 6)
linkText.addAttribute(.link,
value: "https://www.apple.com",
range: linkRange)

Note that the rendered styling is defined by the view where this is used. Even if you set a differnt foreground color, the styling will be internally handled be the view e.g. UITextView/NSTextView.
Text Alignment and Spacing with Paragraph Styles
Styling for text alignment and spacing is handled with paragraph styles. The following creates a styling with center alignmed text and a line spacing of 10:
let style = NSMutableParagraphStyle()
style.alignment = .center
style.lineSpacing = 10
You add this style to the attributes dictionary of NSAttributedString:
You can create customize paragraph formatting:
let firstParagraphAttributes: [NSAttributedString.Key : Any] = [.paragraphStyle: style,
.font: UIFont.systemFont(ofSize: 17),
.foregroundColor: UIColor.blue]
let firstParagraphText = """
First Paragraph
This paragraph demonstrates center alignment with increased line spacing.
"""
let firstParagraph = NSAttributedString(string: firstParagraphText,
attributes: firstParagraphAttributes)

Other parameters that you can change per paragraph are:
style.firstLineHeadIndent = 40 // Indent first line
style.headIndent = 20 // Indent other lines
style.minimumLineHeight = 25
style.maximumLineHeight = 30
style.lineSpacing = 10
style.paragraphSpacing = 20 // Space after paragraph
style.paragraphSpacingBefore = 10 // Space before paragraph
You can also work with paragraph styles if you want to create lists:
let paragraphStyle = NSMutableParagraphStyle()
let textList = NSTextList(markerFormat: .disc, options: 1) // Bullet format
paragraphStyle.textLists = [textList]

Adding Images and Attachments
Here’s something cool – you can even embed images in your attributed strings! This is perfect for creating rich content:
let attachment = NSTextAttachment()
attachment.image = UIImage(systemName: "star.fill")
attachment.bounds = CGRect(x: 0, y: -5, width: 20, height: 20)
let imageString = NSAttributedString(attachment: attachment)
let textString = NSAttributedString(string: " Rating: 4.5/5")
let combination = NSMutableAttributedString()
combination.append(imageString)
combination.append(textString)
return combination
Pro tip: When working with text attachments, pay attention to the bounds property. The y value can be used to adjust the vertical alignment of your image relative to the text baseline.
Using NSAttributedString with SwiftUI and UIKit
When it comes to implementing NSAttributedString in your iOS apps, you have different approaches depending on whether you’re using UIKit or SwiftUI. Let me show you how to handle both frameworks effectively.
Using NSAttributedString with UIKit
In UIKit, several UI components directly support NSAttributedString through their attributedText
property. Here’s how you can use it with UILabel:
// Using with UILabel
let label = UILabel()
let attributedText = NSAttributedString(
string: "Hello World",
attributes: [
.foregroundColor: UIColor.blue,
.font: UIFont.boldSystemFont(ofSize: 20)
]
)
label.attributedText = attributedText
You can also set an attributed string to UITextView
// Using with UITextView
let textView = UITextView() // wil use Textkit 2 per defaul
textView.attributedText = attributedText
The actual rendering is done by the view which might ignore certain attributes. It might also be different for TextKit 1 or 2 and if you are on macOS or iOS.
Attributed String in SwiftUI
SwiftUI introduced its own AttributedString
type in iOS 15, which is more Swift-friendly than NSAttributedString. Here is a simple example in SwiftUI:
struct ContentView: View {
var body: some View {
Text(makeAttributedString())
}
func makeAttributedString() -> AttributedString {
var attributedString = AttributedString("Hello World")
attributedString.foregroundColor = .blue
attributedString.font = .system(.title, design: .rounded)
if let range = attributedString.range(of: "World") {
attributedString[range].backgroundColor = .green
}
return attributedString
}
}
AttributedString is very limited. For example header styles are not supported. and only supports the following stylings:
- font (including bold and italic)
- foregroundcolor
- backgroundColor
- strikethroughStyle
- underlineStyle
You can also use markdown with AttributedString. But the markdown support is very basic and limited to the following:
let thankYouString = try AttributedString(markdown:"This is a **bold** and *italic* and `code` with ~~strikethrough~~. [click this link](https://www.apple.com)")

Converting Between AttributedString and NSAttributedString
Sometimes you need to convert between SwiftUI’s AttributedString and NSAttributedString:
// Converting NSAttributedString to AttributedString
NSAttributedString(attributedString: attributedString)
Note that SwiftUI does not render all attributes. For example, NSList or NSTable will not be shown.
Getting Attributes from NSAttributedString
When working with NSAttributedString, you’ll often need to read and analyze the attributes applied to different parts of the text. Let me show you how to effectively retrieve these attributes in your Swift code.
I will use the following as an example:
let attributedString = NSMutableAttributedString(string: "A text with a background color.")
// Applying foreground color to "text"
attributedString.addAttributes(
[.foregroundColor : UIColor.red],
range: NSRange(location: 2, length: 4)
)
// Applying background color to "text with a background"
attributedString.addAttributes(
[.backgroundColor: UIColor.yellow],
range: NSRange(location: 2, length: 22)
)

Basic Attribute Retrieval
The simplest way to get attributes is using the attributes(at:effectiveRange:) method:
// Get attributes at a specific location
if let attributes = attributedString.attributes(at: 3, effectiveRange: nil) {
if let color = attributes[.foregroundColor] as? UIColor {
print("Text color at position 3 is: (color)")
}
}
Enumerating Single Attributes
If you’re interested in finding all occurrences of a specific attribute (like background color), you can use enumerateAttribute:
let fullRange = NSRange(location: 0, length: attributedString.length)
//Enumerate .backgroundColor to detect highlighted text.
attributedString.enumerateAttribute(.backgroundColor, in: fullRange, options: []) { value, range, _ in
if let color = value as? NSColor {
let text = attributedString.attributedSubstring(from: range)
print("found a background color for text: (text)")
}
}
The closure will be called 3 times, where the second time the range for the yellow background color is given.
Enumerating All Attributes
To examine all attributes in a string, use enumerateAttributes:
let fullRange = NSRange(location: 0, length: attributedString.length)
attributedString.enumerateAttributes(in: fullRange) { attributes, range, _ in
// Check for background color
if let backgroundColor = attributes[.backgroundColor] as? UIColor {
print("Range (range) has background color: (backgroundColor)")
}
// Check for text color
if let textColor = attributes[.foregroundColor] as? UIColor {
print("Range (range) has text color: (textColor)")
}
// Check for font
if let font = attributes[.font] as? UIFont {
print("Range (range) uses font: (font)")
}
}
The closure will be called for all range that have differnt attributes. For the above attributes you will get 4 ranges:

Performance Tips 💡
- Cache attributed strings when possible
- Use NSMutableAttributedString only when you need to modify the text
- Consider using AttributedString for SwiftUI-only apps in iOS 15+
Conclusion
NSAttributedString is an essential tool for any iOS developer looking to create sophisticated text experiences in their apps. By mastering its capabilities, you can create more engaging and visually appealing applications that stand out in the App Store. Start implementing these concepts in your next project, and remember to consider both UIKit and SwiftUI approaches for maximum flexibility and compatibility.
Further Reading: