# AttributedString's Codable format and what it has to do with Unicode

DevFeed: [AttributedString's Codable format and what it has to do with Unicode](<https://devfeed.tech/articles/attributedstring-s-codable-format-and-what-it-has-to-do-with-unicode-21710.md>)

Original publisher: [Read original article](<https://oleb.net/2022/attributedstring-codable/>)

Author: Ole Begemann

Published: 2022-04-27T13:28:03Z

Content type: article

Language: en

Sources: [Ole Begemann](<https://devfeed.tech/sources/ole-begemann.md>)

Topics: [Swift](<https://devfeed.tech/topics/swift.md>), [JSON](<https://devfeed.tech/topics/json.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [code](<https://devfeed.tech/tags/code.md>), [json](<https://devfeed.tech/tags/json.md>), [serialization](<https://devfeed.tech/tags/serialization.md>), [swift](<https://devfeed.tech/tags/swift.md>)

## AI overview

The article examines how to encode Swift AttributedString values and explains why character-based or UTF-8 byte-based formatting ranges can become invalid across Unicode versions, Swift versions, operating systems, or normalization changes. It discusses storing UTF-8 bytes as a safer alternative.

## Source excerpt

Here's a simple AttributedString with some formatting: import Foundation let str = try! AttributedString( markdown: "Café **Sol**", options: .init(interpretedSyntax: .inlineOnly) ) AttributedString is Codable. If your task was to design the encoding format for an attributed string, what would you come up with? Something like this seems reasonable (in JSON with comments): { "text": "Café Sol", "runs": [ { // start..<end in Character offsets "range": [5, 8], "attrs": { "strong": true } } ] } This stores the text alongside an array of runs of formatting attributes. Each run consists of a character range and an attribute dictionary. Unicode is complicated But this format is bad and can break in various ways. The problem is that the character offsets that define the runs aren't guaranteed to be stable. The definition of what constitutes a Character, i.e. a user-perceived character, or a Unicode grapheme cluster, can and does change in new Unicode versions. If we decoded an attributed string that had been serialized on a different OS version (before Swift 5.6, Swift used the OS's Unicode library for determining character boundaries), or by code compiled with a different Swift version (since Swift 5.6, Swift uses its own grapheme breaking algorithm that will be updated alongside the Unicode standard)1, the character ranges might no longer represent the original intent, or even become invalid. Update April 11, 2024: See this Swift forum post I wrote for an example where the Unicode rules for grapheme cluster segmentation changed for flag emoji. This change caused a corresponding change in how Swift counts the Characters in a string containing consecutive flags, such as "🇦🇷🇯🇵". Normalization forms So let's use UTF-8 byte offsets for the ranges, I hear you say. This avoids the first issue but still isn't safe, because some characters, such as the é in the example string, have more than one representation in Unicode: it can be either the standalone character é (Latin small let