How to show HTML or Markdown in a SwiftUI Text?

Since I have found another solution I would like to share it with you.

Create a new View Representable

struct HTMLText: UIViewRepresentable {

   let html: String
    
   func makeUIView(context: UIViewRepresentableContext<Self>) -> UILabel {
        let label = UILabel()
        DispatchQueue.main.async {
            let data = Data(self.html.utf8)
            if let attributedString = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) {
                label.attributedText = attributedString
            }
        }

        return label
    }
    
    func updateUIView(_ uiView: UILabel, context: Context) {}
}

And use it later like this:

HTMLText(html: "<h1>Your html string</h1>")

Text can just display Strings. You can use a UIViewRepresentable with an UILabel and attributedText.

Probably attributedText text support will come later for SwiftUI.Text.


If you don't need to specifically use a Text view. You can create a UIViewRepresentable that shows a WKWebView and simple call loadHTMLString().

import WebKit
import SwiftUI

struct HTMLStringView: UIViewRepresentable {
    let htmlContent: String

    func makeUIView(context: Context) -> WKWebView {
        return WKWebView()
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        uiView.loadHTMLString(htmlContent, baseURL: nil)
    }
}

In your body simple call this object like this:

import SwiftUI

struct Test: View {
    var body: some View {
        VStack {
            Text("Testing HTML Content")
            Spacer()
            HTMLStringView(htmlContent: "<h1>This is HTML String</h1>")
            Spacer()
        }
    }
}

struct Test_Previews: PreviewProvider {
    static var previews: some View {
        Test()
    }
}

iOS 15 (beta)

Text now supports basic Markdown!

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Regular")
            Text("*Italics*")
            Text("**Bold**")
            Text("~Strikethrough~")
            Text("`Code`")
            Text("[Link](https://apple.com)")
            Text("***[They](https://apple.com) ~are~ `combinable`***")
        }
    }
}

Result:

Markdown result


However, if you store a String that contains Markdown in a property, it doesn't render. I'm pretty sure this is a bug.

struct ContentView: View {
    @State var textWithMarkdown = "***[They](https://apple.com) ~are~ `combinable`***"
    var body: some View {
        Text(textWithMarkdown)
    }
}

Result:

Text does not render Markdown and renders the raw String instead

You can work around this by converting textWithMarkdown to an AttributedString, using init(markdown:options:baseURL:).

struct ContentView: View {
    @State var textWithMarkdown = "***[They](https://apple.com) ~are~ `combinable`***"
    var body: some View {
        Text(textWithMarkdown.markdownToAttributed()) /// pass in AttributedString to Text
    }
}

extension String {
    func markdownToAttributed() -> AttributedString {
        do {
            return try AttributedString(markdown: self) /// convert to AttributedString
        } catch {
            return AttributedString("Error parsing markdown: \(error)")
        }
    }
}

Result:

Markdown rendered