In WWDC 2021 Apple announced to add Markdown support to the Foundation (hello AttributedString
) and SwiftUI frameworks (hello Text
) starting with iOS 15.
Here I share three little surprises with you. Those are not obvious as they are not part of the documentation.
1. The supported specification
A nice surprise is that SwiftUI supports GitHub Flavored Markdown (GFM). This becomes obvious when trying
Text("Hello ~~World~~")
Because strikethrough, any text wrapped in two tildes ( ~ ), is not supported in CommonMark.
An Apple engineer confirmed the use of GFM in the Apple Developer forum.
Yes, it is GitHub flavored markdown. AttributedString converts both inline and block styles. SwiftUI renders inline styles (but not images at this time). We use the fantastic cmark-gfm library to parse the markdown string.
The last statement about using cmark-gfm library might or might not be true anymore considering Apple's investment in swift-markdown. swift-markdown
is a Swift package for parsing, building, editing, and analyzing Markdown documents.
2. Using String variables is NOT straightforward
Using a string literal in Text
works.
Text("**Hello** *World*") // :)
Fine, but using a string variable does not!
struct MarkdownTest: View {
var markdown = "**Hello** *World*"
var body: some View {
VStack {
Text(markdown) // shows **Hello *World* :(
}
}
}
String interpolation does not help.
Text("\(markdown)") // shows **Hello *World* :(
The trick is to use init(_:)) of LocalizedStringKey
.
Text(.init(markdown)) // :)
Text
implicitly looks up a localized string when you provide a string literal. When you use the initializer Text("Hello")
, SwiftUI creates a LocalizedStringKey
for you and uses that to look up a localization of the Hello string. This works because LocalizedStringKey
conforms to ExpressibleByStringLiteral
.
You must use the same initializer to mimic the behavior and ensure that markdown formatting is displayed correctly.
3. Line Breaks require use of MarkdownParsingOptions
in AttributedString
You might run into the situation that you have an AttributedString
with line breaks (\n
), but those line breaks are not displayed.
The trick is to add the .inlineOnlyPreservingWhitespace
option when initializing the AttributedString
.
Example:
struct MarkdownTest: View {
let attributedString = try! AttributedString(markdown: "**Hello** ~~*World*~~\n!", options: .init(interpretedSyntax: .inlineOnlyPreservingWhitespace))
var body: some View {
Text(attributedString)
}
}
Using line breaks in a regular string is no problem.