This document describes the current public API of MarkdownToAttributedString.
High-level facade for parsing and converting Markdown.
public class MarkdownToAttributedString {
public init()
public init(configuration: MarkdownConfiguration)
public func convert(_ markdown: String) -> NSAttributedString
public func parse(_ markdown: String) -> [MarkdownElement]
public func convert(_ elements: [MarkdownElement]) -> NSAttributedString
}Use this type when you want one simple entry point for both parsing and rendering.
Parses Markdown into block-level MarkdownElement values with richer child metadata.
public class MarkdownParser {
public init()
public func parse(_ markdown: String) -> [MarkdownElement]
}Converts parsed elements into NSAttributedString.
public class AttributedStringConverter {
public init(configuration: MarkdownConfiguration = .default)
public func convert(_ markdown: String) -> NSAttributedString
public func convert(_ elements: [MarkdownElement]) -> NSAttributedString
}This class is public and can be used directly when you want to control the parse/render pipeline manually.
Configuration for all supported Markdown styles.
public struct MarkdownConfiguration {
public let text: MarkdownStyle
public let h1: MarkdownStyle
public let h2: MarkdownStyle
public let h3: MarkdownStyle
public let h4: MarkdownStyle
public let h5: MarkdownStyle
public let h6: MarkdownStyle
public let bold: MarkdownStyle
public let italic: MarkdownStyle
public let strikethrough: MarkdownStyle
public let code: MarkdownStyle
public let codeBlock: MarkdownStyle
public let link: MarkdownStyle
public let listPrefix: MarkdownStyle
public let blockquote: MarkdownStyle
public init(...)
public static let `default`: MarkdownConfiguration
}Default values currently match the implementation in MarkdownElement.swift:
MarkdownConfiguration(
text: .init(),
h1: .init(fontSize: 24, fontWeight: .bold),
h2: .init(fontSize: 18, fontWeight: .bold),
h3: .init(fontSize: 15, fontWeight: .bold),
h4: .init(fontSize: 13, fontWeight: .bold),
h5: .init(fontSize: 11, fontWeight: .bold),
h6: .init(fontSize: 10, fontWeight: .bold),
bold: .init(fontWeight: .bold),
italic: .init(isItalic: true),
strikethrough: .init(),
code: .init(),
codeBlock: .init(),
link: .init(),
listPrefix: .init(fontWeight: .semibold),
blockquote: .init()
)Style configuration for a single Markdown element class.
public struct MarkdownStyle {
public let fontSize: CGFloat
public let fontWeight: FontWeight
public let isItalic: Bool
public let foregroundColor: MarkdownColor?
public init(
fontSize: CGFloat = 12,
fontWeight: FontWeight = .regular,
isItalic: Bool = false,
foregroundColor: MarkdownColor? = nil
)
}public enum FontWeight {
case regular
case bold
case medium
case semibold
case light
}MarkdownColor is a platform typealias:
UIColoron iOS, tvOS, and watchOSNSColoron macOS
public struct MarkdownElement: Equatable {
public let type: MarkdownElementType
public let content: String
public let range: Range<String.Index>?
public let children: [MarkdownElement]
public let info: String?
public let nestingLevel: Int
public init(
type: MarkdownElementType,
content: String,
range: Range<String.Index>? = nil,
children: [MarkdownElement] = [],
info: String? = nil,
nestingLevel: Int = 0
)
}Important fields:
content: normalized textual content for the elementchildren: nested inline elements, used for paragraphs, headers, emphasis, and linksinfo: extra metadata, currently used for fenced code block info strings likeswiftnestingLevel: indentation-derived list or blockquote depth
public enum MarkdownElementType: Equatable {
case text
case header(level: Int)
case bold
case italic
case strikethrough
case code
case codeBlock
case link(title: String, url: String)
case image(title: String, url: String)
case unorderedList(content: [MarkdownElement])
case orderedList(number: Int, content: [MarkdownElement])
case lineBreak
case paragraph
case blockquote(content: [MarkdownElement])
case table(headers: [String], rows: [[String]])
}table exists in the public model but the current parser does not emit table nodes yet.
public extension String {
func toAttributedString(
configuration: MarkdownConfiguration = .default
) -> NSAttributedString
}Example:
let attributed = "# Title".toAttributedString()Current rendering behavior:
- links receive the
.linkattribute when the URL can be converted toURL; - images render as text placeholders like
🖼️ Alt text; - fenced code blocks render only their body, without the fence markers;
- nested emphasis is supported for the common cases covered by the tests;
- block elements are separated with newlines by the converter.
import MarkdownToAttributedString
let parser = MarkdownParser()
let elements = parser.parse("""
# Title
- One
- Two
""")
let converter = AttributedStringConverter(configuration: .default)
let attributedString = converter.convert(elements)- Cache converted output if the same Markdown is rendered often.
- Prefer a focused custom
MarkdownConfigurationover post-processing attributes later. - Add regression tests when changing parser behavior, because Markdown edge cases are easy to break.
Good fit:
- app knowledge bases;
- rich text snippets;
- AI/chat responses with common Markdown;
- export to plain text or RTF through
NSAttributedString.
Not yet implemented:
- tables;
- image attachments;
- full CommonMark or GFM compatibility.