AttributedString Tutorial for Swift: Getting Started

Learn how to format text and create custom styles using iOS 15’s new AttributedString value type as you build a Markdown previewer in SwiftUI. By Ehab Amer.

4 (1) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Saving Custom Attributes

When you saved your string, the formatting showed in the list — but when you relaunch the app, its styling is lost! There’s a simple reason for this. When you saved the string the first time, it was directly added to the data source array. But when you relaunched the app, the data source reloaded everything from a file. The attributed string with all its custom attributes was saved correctly in the file, but the custom attributes aren’t there the second time you launch the app.

The decoder in the data source doesn’t know anything about the attributes scope you created to encode and decode your custom attributes. And in a way, it shouldn’t care about that since it’s a generic decoder.

Saving Fonts

There’s also another issue. If you created some strings with a theme, you’ll notice that the font is also lost in the decoding process. This time, it’s not something you missed. It seems that SwiftUI.Font doesn’t conform to Codable, so its value isn’t stored.

To fix the first issue, you’ll need to wrap an attributed string in another type and configure the attributed string property with a Codable configuration to consider the new scope. And for the second, you’ll just save the selected theme alongside the attributed string and reapply the theme when you add it to the list.

Create a new Swift file in the Models group named CustomAttributedString.swift. Add the following to it:

import SwiftUI

struct CustomAttributedString: Codable, Identifiable, Hashable {
  // 1
  func hash(into hasher: inout Hasher) {
    hasher.combine(textTheme)
    hasher.combine(attributedString)
  }  
  // 2
  var id: Int {
    attributedString.hashValue
  }
  // 3
  var textTheme: TextTheme
  // 4
  @CodableConfiguration(from: \.customAttributes) var attributedString = 
    AttributedString()
  // 5
  init(_ attString: AttributedString, theme: TextTheme) {
    attributedString = attString
    textTheme = theme
  }
  // 6
  var themedString: AttributedString {
    var tempString = attributedString
    tempString.mergeAttributes(textTheme.attributeContainer)
    return tempString
  }
}

Here’s what this does:

  1. Uses the hashing function to generate the hash of the current object by merging the hash values of its two stored properties.
  2. Defines a property, id, that returns the hash value. Identifiable requires this property.
  3. Sets a property for the theme of this string to reapply it whenever you need the string.
  4. Attaches a CodableConfiguration with the AttributeScopes.CustomAttributes type that you created to handle decoding the custom attributes.
  5. Adds an initializer for the new type.
  6. Sets a computed property for the attributed string that reapplies the theme.

Next, you’ll need to apply some changes to accommodate for the new type. In MarkdownView.swift, change the type of dataSource to:

var dataSource: AttributedStringsDataSource<CustomAttributedString>

Then, change the implementation of saveEntry() to:

func saveEntry() {
  let originalAttributedString = convertMarkdown(markdownString)
  let customAttributedString = CustomAttributedString(
    originalAttributedString,
    theme: selectedTheme)
  dataSource.save(customAttributedString)
  cancelEntry()
}

This saves the new type that includes the original attributed string along with the theme instead of the attributed string alone.

Next, open SavedStringsView.swift and change the type of dataSource to:

@ObservedObject var dataSource: 
  AttributedStringsDataSource<CustomAttributedString>

Finally, in the body, change the loop that adds the strings in the list to:

ForEach(dataSource.currentEntries, id: \.id) { item in
  CustomText(item.themedString)
    .padding()
}

This will use the new type in the listing page and recreate the theme attributes in the attributed string for presentation.

Uninstall the app from the simulator by long-tapping the app icon. Then, tap Remove app to delete the previously saved list.

Uninstalling an app from the simulator

Build and run. Save some strings with different themes, then restart the app.

Attributed strings with different themes in the list.

You’ll see that all the styles are correctly applied in the list.

Where to Go From Here?

You can download the completed project files by clicking Download Materials at the top or bottom of the tutorial.

To learn more about AttributedString, have a look at the developer documentation.

You can also check out Apple’s session from WWDC21 that introduces AttributedString. The session references a sample project that illustrates overlapping attributes, localization and even rainbow text!

You should also check out SwiftUI Localization Tutorial for iOS: Getting Started to learn more about localization, pluralizations and grammar.

We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!