SkillAgentSearch skills...

SwiftMath

SwiftMath provides a full Swift implementation of iosMath for displaying beautifully rendered math equations in iOS and MacOS applications. It typesets formulae written using LaTeX math mode in a UILabel equivalent class.

Install / Use

/learn @mgriebling/SwiftMath
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

SwiftMath

SwiftMath provides a full Swift implementation of iosMath for displaying beautifully rendered math equations in iOS and MacOS applications. It typesets formulae written using LaTeX in a UILabel equivalent class. It uses the same typesetting rules as LaTeX and so the equations are rendered exactly as LaTeX would render them.

Please also check out SwiftMathDemo for examples of how to use SwiftMath from SwiftUI.

SwiftMath is similar to MathJax or KaTeX for the web but for native iOS or MacOS applications without having to use a UIWebView and Javascript. More importantly, it is significantly faster than using a UIWebView.

SwiftMath is a Swift translation of the latest iosMath v0.9.5 release but includes bug fixes and enhancements like a new \lbar (lambda bar) character and cyrillic alphabet support. The original iosMath test suites have also been translated to Swift and run without errors. Note: Error test conditions are ignored to avoid tagging everything with silly throws. Please let me know of any bugs or bug fixes that you find.

SwiftMath prepackages everything needed for direct access via the Swift Package Manager.

Examples

Here are screenshots of some formulae that were rendered with this library:

x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}

Quadratic Formula Quadratic Formula

f(x) = \int\limits_{-\infty}^\infty\!\hat f(\xi)\,e^{2 \pi i \xi x}\,\mathrm{d}\xi

Calculus Calculus

\frac{1}{n}\sum_{i=1}^{n}x_i \geq \sqrt[n]{\prod_{i=1}^{n}x_i}

AM-GM AM-GM

\frac{1}{\left(\sqrt{\phi \sqrt{5}}-\phi\\right) e^{\frac25 \pi}}
= 1+\frac{e^{-2\pi}} {1 +\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots} } } }

Ramanujan Identity Ramanujan Identity

More examples are included in EXAMPLES

Fonts

Here are previews of the included fonts:

Requirements

SwiftMath works on iOS 11+ or MacOS 12+. It depends on the following Apple frameworks:

  • Foundation.framework
  • CoreGraphics.framework
  • QuartzCore.framework
  • CoreText.framework

Additionally for iOS it requires:

  • UIKit.framework

Additionally for MacOS it requires:

  • AppKit.framework

Installation

Swift Package

SwiftMath is available from SwiftMath. To use it in your code, just add the https://github.com/mgriebling/SwiftMath.git path to XCode's package manager.

Usage

The library provides a class MTMathUILabel which is a UIView that supports rendering math equations. To display an equation simply create an MTMathUILabel as follows:


import SwiftMath

let label = MTMathUILabel()
label.latex = "x = \\frac{-b \\pm \\sqrt{b^2-4ac}}{2a}"

Adding MTMathUILabel as a sub-view of your UIView will render the quadratic formula example shown above.

The following code creates a SwiftUI component called MathView encapsulating the MTMathUILabel:

import SwiftUI
import SwiftMath

struct MathView: UIViewRepresentable {
    var equation: String
    var font: MathFont = .latinModernFont
    var textAlignment: MTTextAlignment = .center
    var fontSize: CGFloat = 30
    var labelMode: MTMathUILabelMode = .text
    var insets: MTEdgeInsets = MTEdgeInsets()

    func makeUIView(context: Context) -> MTMathUILabel {
        let view = MTMathUILabel()
        view.setContentHuggingPriority(.required, for: .vertical)
        view.setContentCompressionResistancePriority(.required, for: .vertical)
        return view
    }

    func updateUIView(_ view: MTMathUILabel, context: Context) {
        view.latex = equation
        let font = MTFontManager().font(withName: font.rawValue, size: fontSize)
        font?.fallbackFont = UIFont.systemFont(ofSize: fontSize)
        view.font = font
        view.textAlignment = textAlignment
        view.labelMode = labelMode
        view.textColor = MTColor(Color.primary)
        view.contentInsets = insets
        view.invalidateIntrinsicContentSize()
    }

    func sizeThatFits(_ proposal: ProposedViewSize, uiView: MTMathUILabel, context: Context) -> CGSize? {
        // Enable line wrapping by passing proposed width to the label
        if let width = proposal.width, width.isFinite, width > 0 {
            uiView.preferredMaxLayoutWidth = width
            let size = uiView.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude))
            return size
        }
        return nil
    }
}

For code that works with SwiftUI running natively under MacOS use the following:

import SwiftUI
import SwiftMath

struct MathView: NSViewRepresentable {
    var equation: String
    var font: MathFont = .latinModernFont
    var textAlignment: MTTextAlignment = .center
    var fontSize: CGFloat = 30
    var labelMode: MTMathUILabelMode = .text
    var insets: MTEdgeInsets = MTEdgeInsets()

    func makeNSView(context: Context) -> MTMathUILabel {
        let view = MTMathUILabel()
        view.setContentHuggingPriority(.required, for: .vertical)
        view.setContentCompressionResistancePriority(.required, for: .vertical)
        return view
    }

    func updateNSView(_ view: MTMathUILabel, context: Context) {
        view.latex = equation
        let font = MTFontManager().font(withName: font.rawValue, size: fontSize)
        font?.fallbackFont = NSFont.systemFont(ofSize: fontSize)
        view.font = font
        view.textAlignment = textAlignment
        view.labelMode = labelMode
        view.textColor = MTColor(Color.primary)
        view.contentInsets = insets
        view.invalidateIntrinsicContentSize()
    }

    func sizeThatFits(_ proposal: ProposedViewSize, nsView: MTMathUILabel, context: Context) -> CGSize? {
        // Enable line wrapping by passing proposed width to the label
        if let width = proposal.width, width.isFinite, width > 0 {
            nsView.preferredMaxLayoutWidth = width
            let size = nsView.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude))
            return size
        }
        return nil
    }
}

Automatic Line Wrapping

SwiftMath supports automatic line wrapping (multiline display) for mathematical content. The implementation uses interatom line breaking which breaks equations at atom boundaries (between mathematical elements) rather than within them, preserving the semantic structure of the mathematics.

Using Line Wrapping with UIKit/AppKit

For direct MTMathUILabel usage, set the preferredMaxLayoutWidth property:

let label = MTMathUILabel()
label.latex = "\\text{Calculer le discriminant }\\Delta=b^{2}-4ac\\text{ avec }a=1\\text{, }b=-1\\text{, }c=-5"
label.font = MTFontManager.fontManager.defaultFont

// Enable line wrapping by setting a maximum width
label.preferredMaxLayoutWidth = 235

You can also use sizeThatFits to calculate the size with a width constraint:

let constrainedSize = label.sizeThatFits(CGSize(width: 235, height: .greatestFiniteMagnitude))

Using Line Wrapping with SwiftUI

The MathView examples above include sizeThatFits() which automatically enables line wrapping when SwiftUI proposes a width constraint. No additional configuration is needed:

VStack(alignment: .leading, spacing: 8) {
    MathView(
        equation: "\\text{Calculer le discriminant }\\Delta=b^{2}-4ac\\text{ avec }a=1\\text{, }b=-1\\text{, }c=-5",
        fontSize: 17,
        labelMode: .text
    )
}
.frame(maxWidth: 235)  // The equation will break across multiple lines

Line Wrapping Behavior and Capabilities

SwiftMath implements two complementary line breaking mechanisms:

1. Interatom Line Breaking (Primary)

Breaks equations between atoms (mathematical elements) when content exceeds the width constraint. This is the preferred method as it maintains semantic integrity.

2. Universal Line Breaking (Fallback)

For very long text within single atoms, breaks at Unicode word boundaries using Core Text with number protection (prevents splitting numbers like "3.14").

See MULTILINE_IMPLEMENTATION_NOTES.md for implementation details and recent changes.

Fully Supported Cases

These atom types work perfectly with interatom line breaking:

✅ Variables and ordinary text:

label.latex = "a b c d e f g h i j k l m n o p"
label.preferredMaxLayoutWidth = 150
// Breaks between individual variables at natural boundaries

✅ Binary operators (+, -, ×, ÷):

label.latex = "a+b+c+d+e+f+g+h"
label.preferredMaxLayoutWidth = 100
// Breaks cleanly: "a+b+c+d+"
//                 "e+f+g+h"

✅ Relations (=, <, >, ≤, ≥, etc.):

label.latex = "a=1, b=2, c=3, d=4, e=5"
label.preferredMaxLayoutWidth = 120
// Breaks after commas and operators

✅ Mixed text and simple math:

label.latex = "\\text{Calculer }\\Delta=b^{2}-4ac\\text{ avec }a=1\\text{, }b=-1"
label.preferredMaxLayoutWidth = 200
// Breaks between text and math atoms naturally

✅ Punctuation (commas, periods):

label.latex = "\\text{First, second, third, fourth, fifth}"
label.preferredMaxLayoutWidth = 150
// Breaks at commas and spaces

✅ Brackets and parentheses (simple):

label.latex = "(a+b)+(c+d)+(e+f)"
label.preferredMaxLayoutWidth = 120
// Breaks between parenthesized groups

✅ Greek letters and symbols:

labe
View on GitHub
GitHub Stars358
CategoryDevelopment
Updated5d ago
Forks65

Languages

Swift

Security Score

100/100

Audited on Mar 26, 2026

No findings