Skip to content

Commit

Permalink
Ensure HTML parsing always occurs on the main thread (#767)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinswart authored Jul 30, 2019
1 parent 10094d1 commit b41d2b8
Showing 1 changed file with 61 additions and 40 deletions.
101 changes: 61 additions & 40 deletions Library/String+SimpleHTML.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,44 +21,55 @@ public extension String {
bold optionalBold: Attributes? = nil,
italic optionalItalic: Attributes? = nil
) -> NSAttributedString? {
let baseFont = (base[NSAttributedString.Key.font] as? UIFont) ?? UIFont.systemFont(ofSize: 12.0)
func parsedHtml() -> NSAttributedString? {
let baseFont = (base[NSAttributedString.Key.font] as? UIFont) ?? UIFont.systemFont(ofSize: 12.0)

// If bold or italic are not specified we can derive them from `font`.
let bold = optionalBold ?? [NSAttributedString.Key.font: baseFont.bolded]
let italic = optionalItalic ?? [NSAttributedString.Key.font: baseFont.italicized]

guard let data = self.data(using: String.Encoding.utf8) else { return nil }

let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue
]
guard let string = try? NSMutableAttributedString(data: data, options: options, documentAttributes: nil)
else {
return nil
}

// If bold or italic are not specified we can derive them from `font`.
let bold = optionalBold ?? [NSAttributedString.Key.font: baseFont.bolded]
let italic = optionalItalic ?? [NSAttributedString.Key.font: baseFont.italicized]
// Sub all bold and italic fonts in the attributed html string
let stringRange = NSRange(location: 0, length: string.length)
string.beginEditing()
string
.enumerateAttribute(NSAttributedString.Key.font, in: stringRange, options: []) { value, range, _ in

guard let data = self.data(using: String.Encoding.utf8) else { return nil }
guard let htmlFont = value as? UIFont else { return }
let newAttributes: Attributes

let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue
]
guard let string = try? NSMutableAttributedString(data: data, options: options, documentAttributes: nil)
else {
return nil
}
if htmlFont.fontDescriptor.symbolicTraits.contains(.traitBold) {
newAttributes = bold
} else if htmlFont.fontDescriptor.symbolicTraits.contains(.traitItalic) {
newAttributes = italic
} else {
newAttributes = base
}

// Sub all bold and italic fonts in the attributed html string
let stringRange = NSRange(location: 0, length: string.length)
string.beginEditing()
string.enumerateAttribute(NSAttributedString.Key.font, in: stringRange, options: []) { value, range, _ in
string.addAttributes(newAttributes, range: range)
}
string.endEditing()

guard let htmlFont = value as? UIFont else { return }
let newAttributes: Attributes
return string
}

if htmlFont.fontDescriptor.symbolicTraits.contains(.traitBold) {
newAttributes = bold
} else if htmlFont.fontDescriptor.symbolicTraits.contains(.traitItalic) {
newAttributes = italic
} else {
newAttributes = base
if Thread.isMainThread {
return parsedHtml()
} else {
return DispatchQueue.main.sync {
parsedHtml()
}

string.addAttributes(newAttributes, range: range)
}
string.endEditing()

return string
}

/**
Expand Down Expand Up @@ -93,19 +104,29 @@ public extension String {
- returns: A string with all HTML stripped.
*/
func htmlStripped(trimWhitespace: Bool = true) -> String? {
guard let data = self.data(using: String.Encoding.utf8) else { return nil }
func parsedHtml() -> String? {
guard let data = self.data(using: String.Encoding.utf8) else { return nil }

let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue
]
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue
]

let string = try? NSAttributedString(data: data, options: options, documentAttributes: nil)
let result = string?.string
let string = try? NSAttributedString(data: data, options: options, documentAttributes: nil)
let result = string?.string

if trimWhitespace {
return result?.trimmingCharacters(in: .whitespacesAndNewlines)
if trimWhitespace {
return result?.trimmingCharacters(in: .whitespacesAndNewlines)
}
return result
}

if Thread.isMainThread {
return parsedHtml()
} else {
return DispatchQueue.main.sync {
parsedHtml()
}
}
return result
}
}

0 comments on commit b41d2b8

Please sign in to comment.