Skip to content

A powerful Swift library for converting Markdown text to NSAttributedString with full formatting support. Cross-platform compatible for iOS, macOS, tvOS, and watchOS.

License

Notifications You must be signed in to change notification settings

SergeiKriukov/MarkdownToAttributedString

Repository files navigation

MarkdownToAttributedString

Swift Platform License

A powerful Swift library for converting Markdown text to NSAttributedString with full formatting support. Perfect for iOS, macOS, tvOS, and watchOS applications that need to display rich text from Markdown sources.

image

✨ Features

  • 🎯 Complete Markdown Support - Headers, bold, italic, strikethrough, code, lists, links, images, blockquotes
  • 🎨 Customizable Styling - Configure fonts, sizes, colors, and weights for each element
  • 📱 Cross-Platform - Works on iOS, macOS, tvOS, and watchOS
  • 🚀 High Performance - Optimized parsing and conversion
  • 🔧 Easy Integration - Simple API with Swift Package Manager support
  • 📝 RTF Export - Convert to Rich Text Format for document sharing
  • 📄 Plain Text Export - Extract clean text without formatting

🚀 Quick Start

Installation

Add MarkdownToAttributedString to your project using Swift Package Manager:

dependencies: [
    .package(url: "https://github.com/SergeiKriukov/MarkdownToAttributedString.git", from: "1.0.0")
]

Basic Usage

import MarkdownToAttributedString

let markdown = """
# Welcome to MarkdownToAttributedString

This is **bold** and *italic* text with `inline code`.
You can also use ~~strikethrough~~ formatting.

## Features
- Easy to use
- Highly customizable
- Cross-platform support

> This is a blockquote with **bold** text inside.
"""

// Convert with default styling
let attributedString = markdown.toAttributedString()

// Or use custom configuration
let customConfig = MarkdownConfiguration(
    h1: .init(fontSize: 28, fontWeight: .bold),
    bold: .init(fontWeight: .bold, isItalic: false)
)
let customAttributedString = markdown.toAttributedString(configuration: customConfig)

🎨 Customization

Styling Configuration

let config = MarkdownConfiguration(
    // Header styles
    h1: .init(fontSize: 24, fontWeight: .bold),
    h2: .init(fontSize: 20, fontWeight: .semibold),
    h3: .init(fontSize: 18, fontWeight: .medium),

    // Text formatting
    bold: .init(fontWeight: .bold),
    italic: .init(isItalic: true),
    strikethrough: .init(),
    code: .init(fontSize: 14, fontWeight: .regular),

    // List and blockquote styling
    listPrefix: .init(fontWeight: .semibold),
    blockquote: .init()
)

let attributedString = markdown.toAttributedString(configuration: config)

Available Style Properties

  • fontSize - Text size in points
  • fontWeight - Font weight (regular, bold, semibold, light)
  • isItalic - Italic text styling
  • foregroundColor - Text color (iOS/macOS)

📋 Supported Markdown Elements

Element Example Status
Headers # H1, ## H2, ### H3
Bold **bold text**
Italic *italic text*
Strikethrough ~~strikethrough~~
Inline Code `code`
Code Blocks ```code```
Links [text](url)
Images ![alt](url)
Lists - item, 1. item
Blockquotes > quote text
Line Breaks \n

🔧 Advanced Usage

Manual Conversion

import MarkdownToAttributedString

let parser = MarkdownParser()
let converter = AttributedStringConverter()

// Parse markdown into elements
let elements = parser.parse(markdown)

// Convert to attributed string
let attributedString = converter.convert(elements)

Export to RTF

let attributedString = markdown.toAttributedString()

// Export to RTF data
let rtfData = try attributedString.data(
    from: NSRange(location: 0, length: attributedString.length),
    documentAttributes: [.documentType: NSAttributedString.DocumentType.rtf]
)

// Save to file
try rtfData.write(to: fileURL)

Extract Plain Text

let attributedString = markdown.toAttributedString()
let plainText = attributedString.string
// Result: "Welcome to MarkdownToAttributedString\nThis is bold and italic text..."

📱 Platform Support

Platform Minimum Version Status
iOS 15.0+
macOS 12.0+
tvOS 15.0+
watchOS 8.0+

🧪 Testing

The library includes comprehensive tests for all supported Markdown elements:

swift test

📖 Examples

iOS App Integration

import SwiftUI
import MarkdownToAttributedString

struct ContentView: View {
    @State private var markdown = "# Hello World\nThis is **bold** text"
    
    var body: some View {
        VStack {
            TextEditor(text: $markdown)
                .frame(height: 200)
            
            Text(AttributedString(markdown.toAttributedString()))
                .frame(maxWidth: .infinity, alignment: .leading)
        }
    }
}

macOS App with Export

import AppKit
import MarkdownToAttributedString

class DocumentController: NSDocumentController {
    func exportToRTF(_ markdown: String) {
        let attributedString = markdown.toAttributedString()
        
        let savePanel = NSSavePanel()
        savePanel.allowedContentTypes = [.rtf]
        
        savePanel.begin { response in
            if response == .OK, let url = savePanel.url {
                do {
                    let rtfData = try attributedString.data(
                        from: NSRange(location: 0, length: attributedString.length),
                        documentAttributes: [.documentType: NSAttributedString.DocumentType.rtf]
                    )
                    try rtfData.write(to: url)
                } catch {
                    print("Export failed: \(error)")
                }
            }
        }
    }
}

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Setup

  1. Clone the repository
  2. Open in Xcode or use Swift Package Manager
  3. Run tests: swift test
  4. Build the example: swift build

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🗺️ Roadmap & Future Features

🚀 Version 2.1.0 - Typography & Accessibility (Q2 2025)

✨ Dynamic Type Support

Автоматическая адаптация размера шрифта под системные настройки пользователя.

// Будущий API
let config = MarkdownConfiguration.dynamicType(
    h1: .init(fontWeight: .bold),
    body: .init(fontSize: .body) // .largeTitle, .title1, .body, .caption etc.
)

let attributedString = markdown.toAttributedString(
    configuration: config,
    traitCollection: UITraitCollection(preferredContentSizeCategory: .extraLarge)
)

Реализация:

  • Расширение MarkdownStyle с FontSize enum
  • Добавление метода dynamicType() в MarkdownConfiguration
  • Интеграция с UITraitCollection для автоматической адаптации

🎯 Custom Bullets for Lists

Возможность использовать кастомные маркеры вместо стандартных.

let config = MarkdownConfiguration(
    listPrefix: .init(
        unorderedSymbols: ["🍎", "🍌", "🍇"], // Для разных уровней вложенности
        orderedStyle: .circledNumbers // .numbers, .letters, .roman, .circledNumbers
    )
)

// Markdown: - Item 1\n  - Item 2\n    - Item 3
// Результат: 🍎 Item 1\n  🍌 Item 2\n    🍇 Item 3

Реализация:

  • Добавление unorderedSymbols и orderedStyle в MarkdownConfiguration
  • Расширение MarkdownElementType.unorderedList с уровнем вложенности
  • Логика выбора символа по уровню в AttributedStringConverter

🔮 Version 2.2.0 - Advanced Markdown (Q3 2025)

📝 Front Matter / YAML Support

Поддержка метаданных в начале Markdown файлов.

---
title: "My Document"
author: "John Doe"
date: "2025-10-04"
tags: ["markdown", "swift"]
---

# Document Content
This is the actual content...
if let frontMatter = markdown.frontMatter {
    print("Title: \(frontMatter["title"] ?? "")")
    print("Author: \(frontMatter["author"] ?? "")")
}

// Или получение чистого контента без метаданных
let cleanContent = markdown.contentWithoutFrontMatter

Реализация:

  • Класс FrontMatterParser для парсинга YAML
  • Расширение String с методами frontMatter и contentWithoutFrontMatter
  • Поддержка различных форматов разделителей (---, +++)

🔗 Referenced Links & Images

Расширенный синтаксис для ссылок и изображений.

This is a [referenced link][1] and ![referenced image][2].

[1]: https://example.com "Optional title"
[2]: /path/to/image.png "Alt text"

Реализация:

  • Парсинг ссылок в конце документа
  • Создание словаря соответствий
  • Замена referenced синтаксиса на стандартный

📏 Line & Paragraph Spacing

Точный контроль типографики.

let config = MarkdownConfiguration(
    h1: .init(lineSpacing: 8, paragraphSpacing: 16),
    body: .init(lineSpacing: 4, paragraphSpacing: 8),
    blockquote: .init(lineSpacing: 2, paragraphSpacing: 12)
)

Реализация:

  • Добавление lineSpacing и paragraphSpacing в MarkdownStyle
  • Применение NSMutableParagraphStyle в AttributedStringConverter

🌟 Version 2.3.0 - Rich Content (Q4 2025)

🖼️ Bundle Images Support

Поддержка изображений из app bundle.

![App Icon](<AppIcon>)
![Custom Asset](<my-custom-image>)
let config = MarkdownConfiguration(
    image: .init(
        bundle: Bundle.main,
        placeholder: "🖼️" // Для изображений, которые не найдены
    )
)

Реализация:

  • Расширение MarkdownElementType.image с bundle поддержкой
  • Использование NSTextAttachment для встраивания изображений
  • Fallback на placeholder для отсутствующих изображений

📊 Tables Support

Базовая поддержка таблиц Markdown.

| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
| Cell 1   | Cell 2   | Cell 3   |
| Cell 4   | Cell 5   | Cell 6   |
let config = MarkdownConfiguration(
    table: .init(
        headerStyle: .bold,
        borderColor: .gray,
        cellPadding: 8
    )
)

Реализация:

  • Парсер для table синтаксиса
  • Создание NSTextTable для табличного представления
  • Кастомизация стилей заголовков и ячеек

🎨 Version 3.0.0 - Advanced Customization (Q1 2026)

🎭 Rules-Based Engine

Расширяемая система правил для кастомных элементов.

// Создание кастомного правила
let mentionRule = CharacterRule(
    pattern: "@(\\w+)",
    style: .foregroundColor(.blue)
)

// Добавление в конфигурацию
let config = MarkdownConfiguration(
    customRules: [mentionRule]
)

// Markdown: Hello @username!
// Результат: Hello @username! (с синим цветом)

Реализация:

  • Протокол MarkdownRule для кастомных правил
  • Интеграция в основной парсер
  • Поддержка регулярных выражений и стилей

🎨 Font Style Enum

Более точный контроль стилей шрифтов.

let config = MarkdownConfiguration(
    h1: .init(fontStyle: .boldItalic, fontSize: 24),
    code: .init(fontStyle: .monospaced),
    link: .init(fontStyle: .underlined)
)

Реализация:

  • Enum FontStyle (.normal, .bold, .italic, .boldItalic, .monospaced, .underlined)
  • Расширение MarkdownStyle с fontStyle свойством
  • Применение через UIFontDescriptor и NSFontDescriptor

🌙 Dark Mode & Theme Support

Полная поддержка тем и цветовых схем.

let lightConfig = MarkdownConfiguration.lightTheme()
let darkConfig = MarkdownConfiguration.darkTheme()

// Или кастомная тема
let customTheme = MarkdownTheme(
    background: .systemBackground,
    text: .label,
    accent: .systemBlue
)

Реализация:

  • Класс MarkdownTheme для цветовых схем
  • Методы lightTheme() и darkTheme() в MarkdownConfiguration
  • Интеграция с UITraitCollection и NSAppearance

🔗 Similar Projects

If you're looking for alternative Swift Markdown libraries, check out these excellent projects:

  • Author: Simon Fairbairn
  • Focus: Comprehensive Markdown parsing with advanced customization
  • Key Features:
    • Rules-based processing engine
    • Front Matter / YAML support
    • Dynamic Type support
    • SpriteKit integration
    • Extensive styling options
  • Platforms: iOS, macOS, tvOS, watchOS

MarkdownKit ⭐ 871

  • Author: Bruno Oliveira
  • Focus: Lightweight and extensible Markdown parser
  • Key Features:
    • Regular expression-based parsing
    • Custom element support
    • CocoaPods, Carthage, and SPM support
    • Extensible architecture
  • Platforms: iOS, macOS

Note: MarkdownToAttributedString was inspired by studying these projects and aims to provide a modern, Swift-first approach with comprehensive cross-platform support and detailed customization options.


🙏 Acknowledgments

  • Inspired by the need for easy Markdown to NSAttributedString conversion
  • Built with Swift's powerful string processing capabilities
  • Thanks to the Swift community for excellent documentation and examples

📞 Support


Made with ❤️ for the Swift community

About

A powerful Swift library for converting Markdown text to NSAttributedString with full formatting support. Cross-platform compatible for iOS, macOS, tvOS, and watchOS.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

No packages published

Languages