Processing Markdown in Swift for iOS and macOS: from parsing to rendering
Markdown is one of the most popular markup languages for text formatting. In the Swift ecosystem (iOS/macOS), there are several powerful libraries that allow you to parse, convert, and display Markdown. In this article, we will look at two main libraries: Down (fast and flexible) and SwiftMarkdown (official library from Apple). You will learn how to integrate them into your projects, convert Markdown to NSAttributedString, HTML, and display it in SwiftUI.
1. Down Library (cmark-based)
Down is a Swift wrapper around the popular C parser cmark. It provides high performance and supports all basic Markdown elements (headings, lists, code, links, images, tables, etc.).
Installation
Add the package via Swift Package Manager:
// Package.swiftdependencies: [ .package(url: "https://github.com/johnxnguyen/Down.git", from: "0.11.0")]Example: converting Markdown to HTML
import Down
let markdownString = "# Hello, world!
This is **bold text** and *italic*."let down = Down(markdownString: markdownString)let html = try? down.toHTML()print(html ?? "")// Output: <h1>Hello, world!</h1><p>This is <strong>bold text</strong> and <em>italic</em>.</p>Rendering to NSAttributedString
Down can directly create an NSAttributedString for display in UILabel, UITextView, or NSTextView:
let attributedString = try? down.toAttributedString()// Use in UITextView:textView.attributedText = attributedStringSwiftUI Support
For SwiftUI, you can create a wrapper that displays AttributedString (iOS 15+):
struct MarkdownView: View { let markdown: String
var body: some View { if let nsAttr = try? Down(markdownString: markdown).toAttributedString() { Text(AttributedString(nsAttr)) } else { Text(markdown) } }}2. SwiftMarkdown (from Apple)
SwiftMarkdown is an official Apple library, part of swift-markdown. It provides a full Markdown parser with customization capabilities and generation of various formats (HTML, CommonMark, AST).
Installation
// Package.swiftdependencies: [ .package(url: "https://github.com/apple/swift-markdown.git", branch: "main")]Parsing and working with AST
The library builds an abstract syntax tree (AST) that can be analyzed and modified:
import Markdown
let source = """# Documentation
List of functions:- `foo()` — first function- `bar()` — second function"""
let document = Document(parsing: source)let heading = document.child(at: 0) as? Headingprint(heading?.plainText ?? "") // "Documentation"
// Traversing all elementsfor child in document.children { if let list = child as? UnorderedList { for item in list.children { print((item as? ListItem)?.plainText ?? "") } }}HTML Generation
let html = document.format(options: [.unsafeTags])print(html)// Output: <h1>Documentation</h1>...<ul><li><code>foo()</code> — first function</li>...Rendering via AttributedString (iOS 15+)
SwiftMarkdown supports direct conversion to AttributedString using styles:
import Markdownimport SwiftUI
struct ContentView: View { let markdown = "**Important**: click *here*."
var body: some