I found this extension on Stackoverflow. However, there was an error. How do I fix this error?
Cannot invoke initializer for type 'NSAttributedString' with an argument list of type '(data: NSData, options: [String : AnyObject], documentAttributes: NilLiteralConvertible, error: NilLiteralConvertible)'
Error is in "let attributedString".
extension String {
init(htmlEncodedString: String) {
let encodedData = htmlEncodedString.dataUsingEncoding(NSUTF8StringEncoding)!
let attributedOptions : [String: AnyObject] = [
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
]
let attributedString = NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil, error: nil)! //ERROR HERE!
self.init(attributedString.string)
}
}
Looking at the docs I believe you have the correct method, but note that Swift 2 has error handling, so you would need to do:
extension String {
init(htmlEncodedString: String) {
do {
let encodedData = htmlEncodedString.dataUsingEncoding(NSUTF8StringEncoding)!
let attributedOptions : [String: AnyObject] = [
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
]
let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
self.init(attributedString.string)
} catch {
fatalError("Unhandled error: \(error)")
}
}
}
I've tested that in a playground and it happily compiles.
Related
I want to create an string extension to create a string from a html code.
Therefore I use this code:
NSAttributedString(data: self, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
Sadly there appears the error message "Type 'String' has no member 'documentType'".
Using Xcode 9.3, Swift 4.1:
extension String {
func htmlStr(data: Data) {
do {
let outputStr = try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
print(outputStr)
} catch (let err) {
print(err.localizedDescription)
}
}
}
I'm trying to convert HTML to NSAttributedString to show it in UILabel / UITextView.
The application often crashes during this conversion. I was looking for a solution and find out a lot of answers here, but unfortunately none of them solve my problem.
UPDATE:
Because it's hard to describe and reproduce the crash, I'm providing the code that will crash every time
let url = URL(string: "https://www.fksoftware.sk/stackoverflow/html_parse_crash.json")!
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
guard let dict = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any] else {
return
}
guard let dataDict = dict["data"] as? [String: Any],
let htmlString = dataDict["performance_description_original"] as? String
else {
return
}
do {
let attStr = try NSAttributedString(data: Data(htmlString.utf8), options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
print(attStr.string)
} catch let error as NSError {
print("error:", error)
}
}.resume()
Crash:
WebThread (11): EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
There is not additional info in the console.
I'm using this way and it works fine, but has a slowness fallback because of the usage of NSHTMLTextDocumentType as i did my research
do {
let attributedOptions:[String: Any] = [
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue]
let date = html.data(using: String.Encoding.utf8, allowLossyConversion: true)!
return try NSAttributedString(data: data, options: attributedOptions , documentAttributes: nil)
} catch let error as NSError {
print("htmo2String \(error)")
}
any ideas how to do it faster or another efficient way to do it!
Maybe you can execute the parse code on a queue...
func parse(_ html: String, completionHandler: #escaping (_ attributedText: NSAttributedString?) -> (Void)) -> Void
{
let htmlData = text.data(using: String.Encoding.utf8, allowLossyConversion: false)
let options: [String: AnyObject] = [
NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType as AnyObject
]
completionHandler(try? NSAttributedString(data: htmlData!, options: options, documentAttributes: nil))
}
And now call the function and wait from response...
let queue: DispatchQueue = DispatchQueue(label: "com.yourcompany.Process./html_converter")
queue.async
{
parse("<p>¡Hola mundo</p>", completionHandler: { (attributtedString: NSAttributedString?) -> (Void) in
if let attributtedString = attributtedString
{
DispatchQueue.main.async
{
print("str:: \(attributtedString)")
}
}
})
}
Did you try o use a UIWebView to present HTML content ?
You can display HTML from a string or from a URL, as you prefer.
Here is an example to display HTML from a string :
string sHTMLContent = "<html><head><body><p>Hello World</p></body></head></html>";
m_WebView.LoadHtmlString(sHTMLContent , null);
Then you can set the size of your Webview to be equal to your textview with constraints. The webview will be scrollable automatically if needed.
Final string extension to show html string efficiently with #Adolfo async idea
with the ability to change font and color ^_^
extension String {
func html2StringAsync(_ fontSize: CGFloat? = nil, color: UIColor? = nil, completionBlock:#escaping (NSAttributedString) ->()) {
let fontSize = fontSize ?? 10
let fontColor = color ?? UIColor.black
let font = "Avenir !important"
let html = "<div style=\"font-family:\(font); font-size:\(fontSize)pt; color:\(fontColor.hexString);\">" + self + "</div>"
if let data = html.data(using: String.Encoding.utf8, allowLossyConversion: true){
DispatchQueue.main.async {
do {
let attributedOptions:[String: Any] = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue]
let attrStr = try NSAttributedString(data: data, options: attributedOptions , documentAttributes: nil)
completionBlock(attrStr)
} catch let error as NSError {
print("htmo2String \(error)")
}
}
}else{
completionBlock(NSAttributedString(string: self))
}
}
}
I have a string that contains in it html code
let htmlString = <p style=\"text-align: right;\"> text and text
and I want to ignore the html codes and have a string with only the text.
thank you.
You can remove html tag from string by using NSAttributedString.
Please find the below code :
let htmlString = "<p style=\"text-align: right;\"> text and text"
do {
let encodedData = htmlString.dataUsingEncoding(NSUTF8StringEncoding)!
let attributedOptions : [String: AnyObject] = [
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
]
let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
print("final strings :",attributedString.string)
} catch {
fatalError("Unhandled error: \(error)")
}
Hope it works for you!!!
You can also create String extension for reusability:
extension String {
init(htmlString: String) {
do {
let encodedData = htmlString.dataUsingEncoding(NSUTF8StringEncoding)!
let attributedOptions : [String: AnyObject] = [
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
]
let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
self.init(attributedString.string)
} catch {
fatalError("Unhandled error: \(error)")
}
}
}
Swift 3.0 - (Xcode 8.2) Update
extension String {
var normalizedHtmlString : String {
do {
if let encodedData = self.data(using: .utf8){
let attributedOptions : [String: AnyObject] = [
NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType as AnyObject,
NSCharacterEncodingDocumentAttribute: NSNumber(value: String.Encoding.utf8.rawValue)
]
let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
if let stringNormalized = String.init(attributedString.string){
return stringNormalized
}
}
}
catch {
assert(false, "Please check string")
//fatalError("Unhandled error: \(error)")
}
return self
}
}
And call the htmlString method :
let yourHtmlString = "<p style=\"text-align: right;\"> text and text"
let decodedString = String(htmlString:yourHtmlString)
You may try using -[NSAttributedString initWithData:options:documentAttributes:error:];
+ (NSAttributedString*)attributedStringForHTMLStrippingWithHTMLString:(NSString*)htmlString error:(NSError**)error
{
NSAttributedString *result = nil;
NSMutableAttributedString *attributedString = nil;
NSData *htmlStringData = [htmlString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *options = #{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)};
attributedString = [[NSMutableAttributedString alloc] initWithData:htmlStringData
options:options
documentAttributes:nil
error:error];
result = [attributedString copy];
return result;
}
+ (NSString*)stripStringOfHTMLTags:(NSString*)htmlString
{
NSString *result = nil;
NSError *error = nil;
NSAttributedString *attributedString = [self attributedStringForHTMLStrippingWithHTMLString:htmlString error:&error];
result = [attributedString string];
return result;
}
Try using a css file like so:
html-file:
<p id="myText" />
css-file:
#myText
{
text-align: right;
}
In Swift, I Decoding HTML using NSAttributedString, see below:
let encodedString = "Phải công nhận rằng kể từ lúc ông Thăng làm bộ trưởng"
let encodedData = encodedString.dataUsingEncoding(NSUTF8StringEncoding)
let attributedOptions = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]
let attributedString = NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil, error: nil)
let decodedString = attributedString.string
println(decodedString)
But the result like this:
Phải công nháºn rằng kể từ lúc ông Thăng là m bá»™
trưởng
The true result must be the same with the encodedString
What's wrong in this method?
You have to specify the used character encoding in the document options:
let encodedString = "Phải công nhận rằng kể từ lúc ông Thăng làm bộ trưởng"
let encodedData = encodedString.data(using: .utf8)!
let attributedOptions : [NSAttributedString.DocumentReadingOptionKey : Any ] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue ]
do {
let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
let decodedString = attributedString.string
print(decodedString)
} catch {
// error ...
}
// Output: Phải công nhận rằng kể từ lúc ông Thăng làm bộ trưởng
(Updated for Swift 4)