NSRegularExpression Check Content of HTML String - html

I get html string from server. If it's empty, we don't do anything. Otherwise, we show them on UIWebView. I can easily check with .isEmpty in a simple if statement.
Services.getBusinessProfile(countryCode: countryCode, companyId: companyData.cId) { (req, html) in
if !html.isEmpty {
// rest of the code
Problem is, sometimes I get empty tag:
<span style=\"font-family:HelveticaNeue; font-size: 16\"></span>
How can I check the content of this tag? I think I have to use NSRegularExpression for this, as this thread: NSRegularExpression to extract text between two XML tags. But I have no clue how to use it.

If you just need to retrieve the substring between the first span tag in your html text you can do it using range of string upperBound and lowerBound to get your substring as follow:
let htmlString = "<span style=\"font-family:HelveticaNeue; font-size: 16\">Indonesia</span>"
if let lower = htmlString.range(of: "<span style=\"font-family:HelveticaNeue; font-size: 16\">")?.upperBound,
let upper = htmlString.range(of: "</span>", range: lower..<htmlString.endIndex)?.lowerBound {
let text = htmlString[lower..<upper] // "Indonesia"
}

In Swift you can validate if the String that contains the HTML text will result in an empty String once parsed:
func isEmptyOrBlank(htmlStr: String) -> Bool {
let htmlData = htmlStr.data(using: String.Encoding.unicode)
do {
let attributedText = try NSAttributedString(
data: htmlData!,
options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil
)
// Remove any whitespace and new line characters
let result = attributedText.string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
return result.isEmpty
} catch let e as NSError {
// Unable to parse, so assume it's empty
return true
}
}

Related

Parsing HTML and then styling it with attributedString, how do I handle superscripts?

My API is giving me back some information for a description that is HTML. When I give this as data to an attributed string, it shows text that is some default tiny times new roman and does not match the styling of the app. I decided to use attributed string to style it to use the default system font and a specific font size. However this changes the size of all the text, including the superscript "sup" elements that were in the HTML, resulting in some ugly text (line spacing is off). How do I handle setting the font size for only the regular text? Or another way, how do I go back and specifically make each superscript element a smaller font size?
Below is the component for handling the HTML:
struct HTMLText: UIViewRepresentable {
var html: String
var width: CGFloat
func makeUIView(context: UIViewRepresentableContext<Self>) -> UILabel {
let label = UILabel()
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
label.preferredMaxLayoutWidth = width
DispatchQueue.main.async {
label.attributedText = formatAttributedString()
}
return label
}
func updateUIView(_ uiView: UILabel, context: Context) {
DispatchQueue.main.async {
uiView.attributedText = formatAttributedString()
}
}
func formatAttributedString() -> NSMutableAttributedString? {
let data = Data(self.html.utf8)
guard let attributedString = try? NSMutableAttributedString(
data: data,
options: [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
],
documentAttributes: nil
) else {
return nil
}
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.minimumLineHeight = 20
paragraphStyle.maximumLineHeight = 20
let attributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15),
NSAttributedString.Key.paragraphStyle: paragraphStyle
]
attributedString.addAttributes(attributes, range: NSRange(location: 0, length: attributedString.length))
// possible solution to preserve sub/superscript styling
// add a key right behind each superscript
// then do a replace to remove it and store its position.
// then add an attribute to make it a superscript at each position
// For loop to go over attributed string and specifically set a smaller font size for superscript text and do it all in one go?
return attributedString
//return NSMutableAttributedString(string: attributedString.string, attributes: attributes)
}
I am HOPING that I don't have to do this manually with a loop or something and that I'm missing some easy property or method for this. Also ideally, the solution should not introduce any dependency on other packages just for parsing this HTML.
Don't use a UILabel, use Text's AttributedString constructor. See: How to use Attributed String in SwiftUI

Unable to show new paragraph for html data into Textview in iOS Swift

I am working on iOS (Swift) application. I am getting some server response like below.
"description":"This is sample text to show in UI. When doing everyday activities.\u003cbr /\u003eclass is a strong predictor of life, and again sample text here.\u003cbr /\u003eSample text can show here also."
So, Above text has 3 paragraphs, I am trying to displaying them in Textview, But it is showing in plain with new line instead of New Paragraph.
override func viewDidLoad() {
super.viewDidLoad()
let description = jsonResponse["description"] as! String
self.textView.attributedText = description.htmlAttributedString()
}
extension String {
func htmlAttributedString() -> NSAttributedString? {
guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
guard let html = try? NSMutableAttributedString(
data: data,
options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil) else { return nil }
return html
}
}
The issue is it is showing text, But like new line showing instead of new paragraph. How to fix this?
I have fixed this issue by following, And after conversion server response into json serialization, the special characters code showing as
So, I have fixed like below
let description = jsonResponse["description"] as! String
let formattedString = description.replacingOccurrences(of: "<br />", with: " \n\n")
self.textView.text = formattedString

How to get imageURL out of HTML tag

I need to strip out the HTML tag from the URL which i m getting in the response from server.
I have tried following code but it's not helping me. Any Ideas??
The String i m geting
"test Discussion for images"
I have added the extension to retrieve the HTTP string but i m not getting URL accurate.
extension String {
func removeHTMLTag() -> String {
return self.replacingOccurrences(of: "<[^>]+>", with: "", options: String.CompareOptions.regularExpression, range: nil)
}
}
Using SwiftSoup you can get imageURL
let doc = try SwiftSoup.parse("<div id=div2><p>How are you?</p><div id=div3><img src=http://example.com/test.png></div></div>");
for element in try doc.select("img").array(){
try print(element.attr("src"))
}
**Output**
//http://example.com/test.png
You can try this.
func removeHTMLTags(for regex: String!, in text: String!) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = text as NSString
let results = regex.matches(in: text, range: NSMakeRange(0, nsString.length))
return results.map { nsString.substring(with: $0.range)}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
Call this function -
let text = "test Discussion for images<div><img src=\"http:\\\\45.35.4.250\\MvestUploadContainer\\7e3ba5ba-000b-4dc4-ae6e-a4bc8d59088c.png\"><br></div>"
let url = removeHTMLTags(for: "(http[^\\s]+(jpg|jpeg|png)\\b)", in: String(text))
print(url[0])
Output - http:\\45.35.4.250\MvestUploadContainer\7e3ba5ba-000b-4dc4-ae6e-a4bc8d59088c.png

How to display HTML content in UITextview in iOS?

From my response, I am getting the HTML tags with some strings. I am displaying the dynamic contents in my table view. I've converted HTML tags and displaying in the table view. But the problem is, facing the table view flickering issue heavily and it affects the performance also.
Can you please suggest me, if there is any other way to improve the performance.
I am thinking to add the HTML tags manually and replace the tags based on the response. Bcoz I don't want to affect my performance. please advice.
Eg: How to get the Customer Support ?.
The below code is for converting the html code,
var encryptData : String = inputString
if inputString.contains("<") {
encryptData = inputString.htmlToString // Converting HTML to text
}
else { return encryptData } // Normal string
extension String {
var htmlToAttributedString: NSAttributedString? {
guard let data = data(using: .utf8) else { return NSAttributedString() }
do {
return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil)
} catch {
return NSAttributedString()
}
}
var htmlToString: String {
return htmlToAttributedString?.string ?? ""
}
}
Try this Approach
Cell with NSAttributedString makes the scrolling of UITableView slow
It will help you out for table view flickering. Thanks

String interpolation in attributed text from HTML (Swift 4)

I am displaying text in my app as an attributed string from local HTML files, populating a label, as this gives me formatting flexibility. Why is the usual string interpolation not working in this case, and is there a workaround? The aim is to allow the user-provided username to be contained within the string. It functions well except leaves "(user)" from the HTML file displayed in the label rather than interpolating the username as I expected. I'm still learning so if this is a strange and unworkable way to be doing things anyway then please let me know...
This is my code:
class ArticleViewController: UIViewController {
#IBOutlet weak var contentField: UITextView!
var articleID : String = ""
var user = UserDefaults.standard.object(forKey: "user") ?? "user"
override func viewDidLoad() {
super.viewDidLoad()
if let html = Bundle.main.path(forResource: "\(articleID)", ofType: "html") {
let urlToLoad = URL(fileURLWithPath: html)
let data = NSData(contentsOf: urlToLoad)
if let attributedString = try? NSAttributedString(data: data as! Data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) {
contentField.attributedText = attributedString
}
}
}
}
Thanks for your help!
Why is the usual string interpolation not working in this case
Usual string interpolation works on the String literals in the Swift source files, not on the content of general text files or html files.
You may need to replace the occurrences of (user) in the attributed string. (The basic concept is not different from Carpsen90's answer, but you need to be careful when replacing already attributed string.)
if let htmlURL = Bundle.main.url(forResource: articleID, withExtension: "html") {
do {
let data = try Data(contentsOf: htmlURL)
let attributedString = try NSMutableAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil)
//### When you want to compare the result...
//originalText.attributedText = attributedString
let regex = try! NSRegularExpression(pattern: "\\(user\\)")
let range = NSRange(0..<attributedString.string.utf16.count)
let matches = regex.matches(in: attributedString.string, range: range)
for match in matches.reversed() {
attributedString.replaceCharacters(in: match.range, with: user)
}
contentField.attributedText = attributedString
} catch {
// Do error processing here...
print(error)
}
}
Example.
article.html:
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<i>(user)</i>😀<b>(user)</b>
</body>
</html>
What you can see in the text view:
You'll have to find and replace the occurrences of (user) in attributedString.
This should work:
import Foundation
import UIKit
var myString : NSAttributedString = NSAttributedString(string: "Hello (user), this is a message for you")
let regex = try! NSRegularExpression(pattern: "\\(user\\)", options: .caseInsensitive)
let range = NSMakeRange(0, myString.string.count)
let newString = regex.stringByReplacingMatches(in: myString.string, options: [], range: range, withTemplate: "CJDSW18")
let newAttribuetdString = NSAttributedString(string: newString, attributes: myString.attributes(at: 0, effectiveRange: nil))
print(newAttribuetdString.string)