Using the following code migrated from swift3 to swift4,
let options: [NSAttributedString.DocumentReadingOptionKey: AnyHashable] =
[.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue]
let str = try NSAttributedString( data:string!.data(using: String.Encoding.utf8, allowLossyConversion: true
)!, options:options, documentAttributes: nil)
iOS 9+ has no problem, when running iOS 8.3, console output: "dyld: Symbol not found: _NSCharacterEncodingDocumentOption"; It would be passed after commented ".characterEncoding: String.Encoding.utf8.rawValue".
I found the solution. You should to remove .characterEncoding for swift4.
It works on ios8,9,11.
Example:
public func htmlToString() -> String? {
guard let data = data(using: .utf8) else { return nil }
do {
return try NSAttributedString(
data: data,
options: [
.documentType: NSAttributedString.DocumentType.html
],
documentAttributes: nil
).string
} catch let error as NSError {
print(error.localizedDescription)
return nil
}
}
Have a good day!
For all iOS 8, use this way for the key
NSAttributedString.DocumentReadingOptionKey(rawValue: "CharacterEncoding")
instead of putting
NSAttributedString.DocumentReadingOptionKey.characterEncoding
directly.
Note: Don't use version check to call NSAttributedString.DocumentReadingOptionKey.characterEncoding, otherwise, it still doesn't work.
Related
I am trying to get my app version, after following this I tried this code :
import foundation
var config: [String: Any]?
if let infoPlistPath = Bundle.main.url(forResource: "Info", withExtension: "plist") {
do {
let infoPlistData = try Data(contentsOf: infoPlistPath)
if let dict = try PropertyListSerialization.propertyList(from: infoPlistData, options: [], format: nil) as? [String: Any] {
config = dict
}
} catch {
print(error)
}
}
print(config?["CFBundleName"])
// Optional(example-info)
print(config?["CFBundleVersion"])
// Optional(1)
print(config?["CFBundleShortVersionString"])
// Optional(1.0)
but I get always nil as a result.
Does that mean that my app has no version? If so how to set a version to it?
In addition, when I investigate the Info.plist file with Xcode I found that it's empty!
Thanks to #Martin R is comment i solved my problem by linking a new plist file to the project
I'm working on a simple RSS Reader app as a beginner project in Xcode. I currently have it set up that it parses the feed, and places the title, pub date, description and content and displays it in a WebView.
I recently decided to show the description (or a truncated version of the content) in the TableView used to select a post. However, when doing so:
cell.textLabel?.text = item.title?.uppercaseString
cell.detailTextLabel?.text = item.itemDescription //.itemDescription is a String
It shows the raw HTML of the post.
I would like to know how to convert the HTML into plain text for just the TableView's detailed UILabel.
Thanks!
You can add this extension to convert your html code to a regular string:
edit/update:
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.
Xcode 11.4 • Swift 5.2
extension Data {
var html2AttributedString: NSAttributedString? {
do {
return try NSAttributedString(data: self, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
} catch {
print("error:", error)
return nil
}
}
var html2String: String { html2AttributedString?.string ?? "" }
}
extension StringProtocol {
var html2AttributedString: NSAttributedString? {
Data(utf8).html2AttributedString
}
var html2String: String {
html2AttributedString?.string ?? ""
}
}
cell.detailTextLabel?.text = item.itemDescription.html2String
Swift 4, Xcode 9
extension String {
var utfData: Data {
return Data(utf8)
}
var attributedHtmlString: NSAttributedString? {
do {
return try NSAttributedString(data: utfData, options: [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
],
documentAttributes: nil)
} catch {
print("Error:", error)
return nil
}
}
}
extension UILabel {
func setAttributedHtmlText(_ html: String) {
if let attributedText = html.attributedHtmlString {
self.attributedText = attributedText
}
}
}
Here is my suggested answer. Instead of extension, if you want to put inside function.
func decodeString(encodedString:String) -> NSAttributedString?
{
let encodedData = encodedString.dataUsingEncoding(NSUTF8StringEncoding)!
do {
return try NSAttributedString(data: encodedData, options: [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute:NSUTF8StringEncoding], documentAttributes: nil)
} catch let error as NSError {
print(error.localizedDescription)
return nil
}
}
And call that function and cast NSAttributedString to String
let attributedString = self.decodeString(encodedString)
let message = attributedString.string
Please test with this code for the detailTextLabel:
var attrStr = NSAttributedString(
data: item.itemDescription.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true),
options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil,
error: nil)
cell.detailTextLabel?.text = attrStr
Try this solution in swift3
extension String{
func convertHtml() -> NSAttributedString{
guard let data = data(using: .utf8) else { return NSAttributedString() }
do{
return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
}catch{
return NSAttributedString()
}
}
}
To use
self.lblValDesc.attributedText = str_postdescription.convertHtml()
Swift4.0 Extension
extension String {
var html2AttributedString: String? {
guard let data = data(using: .utf8) else { return nil }
do {
return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil).string
} catch let error as NSError {
print(error.localizedDescription)
return nil
}
}
}
i have used Danboz answer, only changed it to return a simple String (not a rich text string):
static func htmlToText(encodedString:String) -> String?
{
let encodedData = encodedString.dataUsingEncoding(NSUTF8StringEncoding)!
do
{
return try NSAttributedString(data: encodedData, options: [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute:NSUTF8StringEncoding], documentAttributes: nil).string
} catch let error as NSError {
print(error.localizedDescription)
return nil
}
}
for me, it works like a charm, thanks Danboz
let content = givenString // html included string
let attrStr = try! NSAttributedString(data: content.data(using: String.Encoding.unicode, allowLossyConversion: true)!,options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],documentAttributes: nil)
self.labelName.attributedText = attrStr
While parsing I use this code
func encode() -> String{
var newStr = String(utf8String: self.cString(using: .utf8)!)
newStr = newStr!.removingPercentEncoding
guard let data = String(utf8String: self.cString(using: .utf8)!)?.data(using: .utf8) else {
return newStr!
}
guard let attributedString = try? NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
return newStr!
}
return attributedString.string
}
the problem is that it removes the \n.
So I do not display the text correctly
That's because you are using NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html
depending on what you are trying to accomplish, you may just ignore that fact or replace "\n" with something else in your String.
iOS 11, Swift 4
Trying to use an activity view controller to share a json string I have just created, and I think I am almost there, but struggling to attach a file with the controller. I got this code.
let encoder = JSONEncoder()
if let jsonData = try? encoder.encode(w2GA) {
if let jsonString = String(data: jsonData, encoding: .utf8) {
let documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let file2ShareURL = documentsDirectoryURL.appendingPathComponent("blah.json")
print(jsonString)
do {
let encodedData = try? JSONEncoder().encode(jsonString)
try encodedData?.write(to: file2ShareURL)
let activityViewController = UIActivityViewController(activityItems: [file2ShareURL], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
} catch {
print(error)
}
}
}
I am getting the error message:
file:///var/mobile/Containers/Data/Application/FD6E9F52-405F-4E66-927F-DCB7EDB0BF25/Documents/blah.json
Attachments Error confirming URL is readable
Pretty sure the file is there? is this some sort of race hazard perhaps? If I replace the URL with the jsonString it works! So I get a jsonString looking like this basically.
[{"imageURL":"http://","latitude":46.819945794990176,"name":"Clue 1","longitude":8.2581710034376599,"hint":"Hint"},{"imageURL":"http://","latitude":47.433033706679716,"name":"Clue 2","longitude":8.8540648624925371,"hint":"Hint"},{"imageURL":"http://","latitude":46.785125219263776,"name":"Clue","longitude":9.6534346734197598,"hint":"Hint"}]
Do you need to save this as a file?
Surely the simplest approach to this is to pass the JSON string as a variable?
Example:
let activityViewController = UIActivityViewController(activityItems: [jsonString], applicationActivities: nil)
EDIT
I've just noticed you've already tried this and it works. Apologies.
Ensure the JSONEncoder isn't throwing an exception and that the file exists before you try to pass the url to the UIActivityViewController
You can also try using the UIActivityItemProvider class as documented here
Ok, found the answer which is of course obvious! The file wasn't being created!
I made two changes, firstly I run the entire code block under the main thread, secondly I changed the lines writing out the json string which in the earlier version I encode twice!
DispatchQueue.main.async {
let encoder = JSONEncoder()
if let jsonData = try? encoder.encode(w2GA) {
if let jsonString = String(data: jsonData, encoding: .utf8) {
var documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let file2ShareURL = documentsDirectoryURL.appendingPathComponent("blah.json")
do {
try jsonString.write(to: file2ShareURL, atomically: false, encoding: .utf8)
} catch {
print(error)
}
do {
let _ = try Data(contentsOf: file2ShareURL)
let activityViewController = UIActivityViewController(activityItems: [file2ShareURL], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
} catch {
print(error)
}
}
}
}
I have this strange error and don't have any idea hot to fix it.
My code was working well in Xcode 7.3. But, after updating to Xcode 8 I got nearly 17 errors and 21 warnings.This might be some syntax change in Swift 3
Solved all those but can't figure out this one : Downcast from 'PHAsset?' to 'PHAsset' only unwraps optionals; did you mean to use '!'?
help please
let valorFoto = tuplesFoto.valorfoto.substring(to: tuplesFoto.valorfoto.characters.index(tuplesFoto.valorfoto.endIndex, offsetBy: -4))
if let asset = PHAsset.fetchAssets(withLocalIdentifiers: [valorFoto], options: nil).firstObject as? PHAsset
{
PHImageManager.default().requestImageData(for: asset, options: nil, resultHandler: { (data: Data?, identificador: String?, orientaciomImage: UIImageOrientation, info: [AnyHashable: Any]?) -> Void in
if let data = data
{
if let image = UIImage(data: data)
{
DispatchQueue.main.async(execute: { () -> Void in
self.images.updateValue(image, forKey: tuplesFoto.valorfoto)
self.colDragAndDrop.reloadData()
})
}
}
})
}
else
{
if let image = UIImage(named: "no_media")
{
images.updateValue(image, forKey: tuplesFoto.valorfoto)
}
}
The line of code:
before
if let asset = PHAsset.fetchAssets(withLocalIdentifiers: [valorFoto], options: nil).firstObject as? PHAsset
after
if let asset = PHAsset.fetchAssets(withLocalIdentifiers: [valorFoto], options: nil).firstObject