Reputation: 4060
I want to dynamically deliver content and display hyperlinks, but it can’t be delivered dynamically and doesn’t work
let linkTitle = "Apple Link"
let linkURL = "http://www.apple.com"
let string = "[Apple Link](http://www.apple.com)"
Text(string) // Not working
Text("[Apple Link](http://www.apple.com)") // Working
Text("[\(linkTitle)](http://www.apple.com)") // Working
Text("[\(linkTitle)](\(linkURL))") // Not working
Upvotes: 26
Views: 8053
Reputation: 452
Here is an improved version of workingdog support Ukraine's answer. The String extension uses the option to preserve the '\n' in the markdown string.
extension String {
var toMarkdownString: AttributedString {
do {
return try AttributedString(markdown: self,
options: AttributedString.MarkdownParsingOptions(interpretedSyntax: .inlineOnlyPreservingWhitespace))
} catch {
return AttributedString("Error parsing markdown: \(error)")
}
}
}
Upvotes: 0
Reputation: 53
You can create an attributed string by passing a standard String or Data instance that contains Markdown to initializers like init(markdown:options:baseURL:). The attributed string creates attributes by parsing the markup in the string.
do {
let thankYouString = try AttributedString(
markdown:"**Thank you!** Please visit our [website](https://example.com)")
} catch {
print("Couldn't parse: \(error)")
}
Localized strings that you load from strings files with initializers like init(localized:options:table:bundle:locale:comment:) can also contain Markdown to add styling. In addition, these localized attributed string initializers can apply the replacementIndex attribute, which allows you to determine the range of replacement strings, whose order may vary between languages.
LocalizedStringKey
initThe behavior where LocalizedStringKey
effectively works as markdown is undocumented, and so we probably should not use LocalizedStringKey
inits as this is not a guaranteed pattern. Future updates might change this, and so it's better to use the more appropriate component AttributedString
.
You can check apple's documentation on this to see for yourself, but LocalizedStringKey
documentation does not mention markdown.
Text(.init(dynamicString))
Text(LocalizedStringKey(markdownStr))
AttributedString
is the right answerAs a few people mentioned, using AttributedString(markdown: dynamicString)
for dynamic strings is the right answer.
Using Text with a static string (i.e. Text("[Apple Link](http://www.apple.com)")
) will work fine, but dynamic strings need to be typed to properly parse as markdown. @kgaidis explains this well.
try! AttributedString(markdown: dynamicString)
will strip the string of \n
characters. In order to preserve these characters, you can pass options in the init like so:
try! AttributedString(
markdown: dynamicString,
options: AttributedString.MarkdownParsingOptions(interpretedSyntax: .inlineOnlyPreservingWhitespace)
// \n will be stripped unless this option is added.
)
Upvotes: 3
Reputation: 816
To define markdown string separately and then pass it to text, we should explicitly convert it to a localized string key. Because otherwise, SwiftUI will just treat this as a regular string and will not parse it internally.
let markdownStr = "Hello, **world**! Check out our [website](https: //example.com)!"
Text (LocalizedStringKey (markdownStr))
Upvotes: 2
Reputation: 18924
Add another .init
in text.
struct ContentView: View {
let linkTitle = "Apple Link"
let linkURL = "http://www.apple.com"
let string = "[Apple Link](http://www.apple.com)"
var body: some View {
Text(.init(string)) // <== Here!
Text("[Apple Link](http://www.apple.com)") // Working
Text("[\(linkTitle)](http://www.apple.com)") // Working
Text(.init("[\(linkTitle)](\(linkURL))")) // <== Here!
}
}
Upvotes: 23
Reputation: 15589
Wrap the string in AttributedString(markdown: my_string_here)
:
let string: String = "[Apple Link](http://www.apple.com)"
Text(try! AttributedString(markdown: string))
extension String {
func toMarkdown() -> AttributedString {
do {
return try AttributedString(markdown: self)
} catch {
print("Error parsing Markdown for string \(self): \(error)")
return AttributedString(self)
}
}
}
SwiftUI Text
has multiple initializers.
For String
:
init<S>(_ content: S) where S : StringProtocol
For AttributedString
:
init(_ attributedContent: AttributedString)
When you declare a static string, Swift is able to guess whether the intent is to use a String
or AttributedString
(Markdown). However, when you use a dynamic string, Swift needs help in figuring out your intent.
As a result, with a dynamic string, you have to explicitly convert your String
into an AttributedString
:
try! AttributedString(markdown: string)
Upvotes: 38
Reputation: 36323
you can try this taken from: How to show HTML or Markdown in a SwiftUI Text? halfway down the page.
extension String {
func markdownToAttributed() -> AttributedString {
do {
return try AttributedString(markdown: self) /// convert to AttributedString
} catch {
return AttributedString("Error parsing markdown: \(error)")
}
}
}
struct ContentView: View {
let linkTitle = "Apple Link"
let linkURL = "https://www.apple.com"
let string = "[Apple Link](https://www.apple.com)"
@State var textWithMarkdown = "[Apple Link](https://www.apple.com)"
var body: some View {
VStack {
Text(textWithMarkdown.markdownToAttributed()) // <-- this works
Text(string) // Not working
Text("[Apple Link](http://www.apple.com)") // Working
Text("[\(linkTitle)](http://www.apple.com)") // Working
Text("[\(linkTitle)](\(linkURL))") // Not working
}
}
}
Upvotes: 7