Home > front end >  Convert html links inside text to clickable links - SwiftUI
Convert html links inside text to clickable links - SwiftUI

Time:01-24

Is there any up to date way to convert links inside the text? I'm getting this kind of text from an API:

var someText: String = "with banks (for example to the sometimes controversial but leading exchange <a href="https://www.coingecko.com/en/exchanges/bitfinex">Bitfinex</a>)."

How can I convert that link inside to a clickable link with the proper name, in the example above: Bitfinex ?

The text could contain multiple links. SwiftUI now supports markdown, manually I could do it like:

Text("[Privacy Policy](https://example.com)")

but how do I do it for a received text from api with multiple links?

Looking for a Swift 5 & SwiftUI 3 solution.

CodePudding user response:

What you have is an html string. You can create a TextView UIViewRepresentable of an UITextView and convert your html string into an NSAttributedString using options NSAttributedString.DocumentType.html. Don't forget to set the textview dataDetectorTypes to .link. Something like:

import SwiftUI
struct TextView: UIViewRepresentable {
    var html: String
    @State var font: UIFont?

    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.dataDetectorTypes = .link
        return textView
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.attributedText = html.htmlToAttributedString
        uiView.font = font
    }
}

You will need to add those helpers

extension Data {
    var htmlToAttributedString: NSAttributedString? {
        do {
            return try NSAttributedString(data: self, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
        } catch {
            print("error:", error)
            return  nil
        }
    }
}

extension StringProtocol {
    var data: Data { .init(utf8) }
    var htmlToAttributedString: NSAttributedString? { data.htmlToAttributedString }
}

Usage:

struct ContentView: View {
    var html = #"with banks (for example to the sometimes controversial but leading exchange <a href="https://www.coingecko.com/en/exchanges/bitfinex">Bitfinex</a>. For more info <a href="https://www.google.com/">Google</a>).""#
    var body: some View {
        TextView(
            html: html,
            font: .systemFont(ofSize: 32)
        ).frame(
            minWidth: 0,
            maxWidth: .infinity,
            minHeight: 0,
            maxHeight: .infinity
        )
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

CodePudding user response:

"...to display the text as it is, with the links inside but converted (clickable)...", you could try this approach, which lets you use the SwiftUI Text view.

struct ContentView: View {
    var someText = #"with banks (for example to the sometimes controversial but leading exchange <a href="https://www.coingecko.com/en/exchanges/bitfinex">Bitfinex</a>). For more info <a href="https://www.google.com/">Google</a>. For bugs see <a href="https://stackoverflow.com/">Stack Overflow</a>.""#
    
    var body: some View {
        Text(LinksMaker.attributedString(from: someText))
    }
}

class LinksMaker {
    static func attributedString(from str: String) -> AttributedString {
        var text = [AttributedString]()
        if let theData = str.data(using: .utf8) {
            let theString = try! NSAttributedString(data: theData, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil)
            theString.enumerateAttributes(in: NSRange(location: 0, length: theString.length), options: []) { (attrs, range, _) in
                if let rangex = Range(range, in: theString.string) {
                    var txt = String(theString.string[rangex])
                    if let url = attrs[.link] as? URL {
                        txt = "[\(txt)](\(url.absoluteString))"
                    }
                    text.append(try! AttributedString(markdown: txt))
                }
            }
        }
        return text.reduce("", { x,y in x   " "   y})
    }
}
  •  Tags:  
  • Related