SwiftUI Text Markdown dynamic string not working

Viewed 1623

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
            
4 Answers

Short Answer

Wrap the string in AttributedString(markdown: my_string_here):

let string: String = "[Apple Link](http://www.apple.com)"
Text(try! AttributedString(markdown: string))

Extension

extension String {
  func toMarkdown() -> AttributedString {
    do {
      return try AttributedString(markdown: self)
    } catch {
      print("Error parsing Markdown for string \(self): \(error)")
      return AttributedString(self)
    }
  }
}

Long Answer

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)

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
        }

    }
}

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!
    }
}

is possible with Link or UIApplicarion.shared.open

struct ContentView: View {

let linkTitle = "Apple Link"
let linkURL = "http://www.apple.com"

var body: some View {
   
    VStack {
        Link(destination: URL(string: linkURL)!) {
            Text(linkTitle)
        }.padding()
        
        Text(linkTitle).onTapGesture {
            UIApplication.shared.open(URL(string: linkURL)!)
        }.foregroundColor(.blue)
    }
                
   
}

}

Related