Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot convert value of type NSAttributedString.DocumentAttributeKey to .DocumentReadingOptionKey

I found this string extension somewhere on SO that allows me to turn html code into an attributed string:

func html2AttributedString() -> NSAttributedString {     return try! NSAttributedString(data: self.data(using: String.Encoding.unicode, allowLossyConversion: true)!, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) } 

It worked fine in Swift 3, but with Swift 4, Xcode complains:

Cannot convert value of type 'NSAttributedString.DocumentAttributeKey' to expected dictionary key type 'NSAttributedString.DocumentReadingOptionKey'

How do I fix this?

like image 657
LinusGeffarth Avatar asked Jun 06 '17 12:06

LinusGeffarth


2 Answers

You need to pass one of the available NSAttributedString DocumentType options:


Hypertext Markup Language (HTML) document.

static let html: NSAttributedString.DocumentType 

Plain text document.

static let plain: NSAttributedString.DocumentType 

Rich text format document.

static let rtf: NSAttributedString.DocumentType 

Rich text format with attachments document.

static let rtfd: NSAttributedString.DocumentType 

In this case you will need to pass the first one (html) NSAttributedString.DocumentType.html

So the extension updated to Swift 4 should look like this:

extension NSAttributedString {     convenience init(data: Data, documentType: DocumentType, encoding: String.Encoding = .utf8) throws {         try self.init(data: data,                       options: [.documentType: documentType,                                 .characterEncoding: encoding.rawValue],                       documentAttributes: nil)     }     convenience init(html data: Data) throws {         try self.init(data: data, documentType: .html)     }     convenience init(txt data: Data) throws {         try self.init(data: data, documentType: .plain)     }     convenience init(rtf data: Data) throws {         try self.init(data: data, documentType: .rtf)     }     convenience init(rtfd data: Data) throws {         try self.init(data: data, documentType: .rtfd)     } } 

extension StringProtocol {     var data: Data { return Data(utf8) }     var htmlToAttributedString: NSAttributedString? {         do {             return try .init(html: data)         } catch {             print("html error:", error)             return nil         }     }     var htmlDataToString: String? {         return htmlToAttributedString?.string     } } 

extension Data {     var htmlToAttributedString: NSAttributedString? {         do {             return try .init(html: self)         } catch {             print("html error:", error)             return nil         }      } } 

Playground Testing

let htmlString = "<style type=\"text/css\">#red{color:#F00}#green{color:#0F0}#blue{color: #00F; font-weight: Bold; font-size: 32}</style><span id=\"red\" >Red</span><span id=\"green\" > Green </span><span id=\"blue\">Blue</span>"  let htmlData = Data(htmlString.utf8)  htmlString.htmlToAttributedString htmlData.htmlToAttributedString 

Discussion The HTML importer should not be called from a background thread (that is, the options dictionary includes documentType with a value of html). It will try to synchronize with the main thread, fail, and time out. Calling it from the main thread works (but can still time out if the HTML contains references to external resources, which should be avoided at all costs). The HTML import mechanism is meant for implementing something like markdown (that is, text styles, colors, and so on), not for general HTML import

like image 53
Leo Dabus Avatar answered Sep 21 '22 21:09

Leo Dabus


Had this after automatic conversion to Swift 4. Was fixed by changing from:

NSMutableAttributedString(data: data,     options: [NSAttributedString.DocumentAttributeKey.documentType : NSAttributedString.DocumentType.html],     documentAttributes: nil) 

to:

NSMutableAttributedString(data: data,    options: [.documentType : NSAttributedString.DocumentType.html],    documentAttributes: nil) { 
like image 28
Vitalii Avatar answered Sep 19 '22 21:09

Vitalii