Convert NSAttributedString into Data for storage

You need to specify what kind of document data you would like to convert your attributed string to:


.txt    // Plain Text Document Type (Simple Text)
.html   // HTML  Text Document Type (Hypertext Markup Language) 
.rtf    // RTF   Text Document Type (Rich text format document)
.rtfd   // RTFD  Text Document Type (Rich text format document with attachment)

update Xcode 10.2 • Swift 5 or later

let textView = UITextView()
textView.attributedText = .init(string: "abc",
                                attributes: [.font: UIFont(name: "Helvetica", size: 16)!])
if let attributedText = textView.attributedText {
    do {
        let htmlData = try attributedText.data(from: .init(location: 0, length: attributedText.length),
                                               documentAttributes: [.documentType: NSAttributedString.DocumentType.html])
        let htmlString = String(data: htmlData, encoding: .utf8) ?? ""
        print(htmlString)
    } catch {
        print(error)
    }
}

Expanding on that:

extension NSAttributedString {

    convenience init(data: Data, documentType: DocumentType, encoding: String.Encoding = .utf8) throws {
        try self.init(attributedString: .init(data: data, options: [.documentType: documentType, .characterEncoding: encoding.rawValue], documentAttributes: nil))
    }

    func data(_ documentType: DocumentType) -> Data {
        // Discussion
        // Raises an rangeException if any part of range lies beyond the end of the receiver’s characters.
        // Therefore passing a valid range allow us to force unwrap the result
        try! data(from: .init(location: 0, length: length),
                  documentAttributes: [.documentType: documentType])
    }

    var text: Data { data(.plain) }
    var html: Data { data(.html)  }
    var rtf:  Data { data(.rtf)   }
    var rtfd: Data { data(.rtfd)  }
}

Usage:

let textView = UITextView()
textView.attributedText = .init(string: "abc", attributes: [.font: UIFont(name: "Helvetica", size: 16)!])
if let textData = textView.attributedText?.text {
    let text = String(data: textData, encoding: .utf8) ?? ""
    print(text)  // abc
}
if let htmlData = textView.attributedText?.html {
    let html = String(data: htmlData, encoding: .utf8) ?? ""
    print(html)  // /* <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" ...
}

This will print

abc
/* <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Generator" content="Cocoa HTML Writer">
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 16.0px Helvetica}
span.s1 {font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 16.00pt}
</style>
</head>
<body>
<p class="p1"><span class="s1">abc</span></p>
</body>
</html>
*/