Decoding HTML in Swift causes BAD_ACCESS on iOS device - html

Following the great example of How do I decode HTML entities in swift? I have managed to decode a HTML entity. However, running my app in iOS simulator causes no errors while testing it on a real device do.
I'm getting the following error:
Thread 1: EXC_BAD_ACCESS (code=1, address=0xc)
On this line:
let attributedString = NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil, error: nil)!
How do you go about solving this?

Probably you are not running the code in the main thread. I am not sure how it is working in the simulator. Anyway try putting that code in a main thread block like
dispatch_async(dispatch_get_main_queue(),
{
let attributedString = NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil, error: nil)
let decodedString = attributedString?.string
})
It should work properly.

Related

NSAttributed string from html without hanging UI thread

I am trying to load some html text into an NSAttributedString and I am seeing the UI hang.
Looking around I am not sure if that is possible as its looks like it may have to run on the main thread: NSAttributedString from HTML in the background thread
In swift 3 iOS 10 I am able to run this without exception
let myString = // some html content
DispatchQueue.global(qos: .background).async {
let data = myString.data(using: .utf8)
let options: [String: Any] = [
NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue
]
do {
let attributedString = try NSAttributedString(data: data!, options: options, documentAttributes: nil)
DispatchQueue.main.async {
() -> Void in
self.label.attributedText = attributedString
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
A couple things.
Not sure why it doesn't crash. But I suspect that its still running on the main thread as the UI still locks up for a few seconds.
There are images in the html. Wondering if that is was is causing most of the delay. I still want to display the images but wondering if I can pull them out and just display the text initially and load the images on a background thread. Not sure if there is anything built into NSAttributedString to pull out images, or doI have to parse them out manually.
Is there any way at all to get this data to load on a background thread so I can not lock up the UI when using an NSAttributedString initialized with html data?
Just came across the same situation, I just figure out setting value for "Timeout" key can reduce the Unresponding time.
var options = [NSAttributedString.DocumentReadingOptionKey : Any]()
options[.documentType] = NSAttributedString.DocumentType.html
options[.characterEncoding] = String.Encoding.utf8.rawValue
options[.timeout] = 5.0 //Important!You can adjust this value to suitable value.
I think the following line of code always runs in the main queue.There is no way to avoid UI hanging completely while using this function.
NSAttributedString(data: data!, options: options, documentAttributes: nil)

Extra argument 'error' in call? But I don't have an 'error' variable at all?

I don't have an error variable anywhere, is this a bug in Xcode? I'm very confused on what could be wrong. I've searched all of stack overflow for an answer on this, found nothing. I've tried for several hours to figure out why I'm getting this error for the line of func parseJson( data: NSData){
I'm a beginner who studies Swift day and night.
Here is my code below:
func parseJson( data: NSData){
do{
let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: [])
if let unwrappedJson: AnyObject = json{
parseSongs(unwrappedJson)
}
}catch{
}
}
Clean the project and build
If that doesn't word
Restart xcode
Rewrite the code

SwiftyJSON: Calling segue but connection tries fetching data

My ViewController is set to update JSON data on a server. That works perfectly. But when I set my VC to perform a segue back to another segue it crashes with the following statement:
fatal error: unexpectedly found nil while unwrapping an Optional value
This happens at this line:
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSArray
The error happens on the "UpdateVC" and the segue points to "ListVC".
The ending part of that statement is doing a forced unwrapping:
as! NSArray
since it throws an exception, it means that either:
the return value is nil
the return value cannot be cast to NSArray
I can't say what the correct way to handle that would be, because it depends on your app logic. If the cast should (ideally) always succeed, then you probably have a bug in your app. On the other hand, if it's possible that the cast doesn't succeed, then the best way is to protect that with an optional binding:
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSArray {
// do something
}

Swift 2.0 How to parse JSON?

I am coding a hangman game and am loading the possible words into my app using json text files. I tried to follow the examples of others on this website but I am getting errors from Xcode.
I tried the following code based on another answer:
import Foundation
var error: NSError?
let jsonData: NSData = /* get your json data */
let jsonDict = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &error) as NSDictionary
But I got an errors on line 4 with jsonDict that said "call can throw but is not marked with try, and the error is not handled" and "Type JSONReadingOptions does not conform to protocol NilLiteralConvertible".
Here is the JSON File I would like to parse:
{
“wordList” : {
“difficulty” : “Easy”
“list” : [
“fireplace”,
“apple”,
“january”,
“tooth”,
“cookies”,
“mysterious”,
“essential”,
“magenta",
“darling”,
“pterodactyl”
]}}
I would like to be able to go into my list array and get values. Thank you very much for any help!
In Swift 2 you need to use the new error handling API instead of passing a reference to an NSError:
do {
let jsonDict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions(rawValue: 0)) as? NSDictionary
if let jsonDict = jsonDict {
// work with dictionary here
} else {
// more error handling
}
} catch let error as NSError {
// error handling
}
You also can't pass nil as value to the options parameter, you need to pass a value of type NSJSONReadingOptions.
That said, the most common approach for parsing JSON in Swift is currently using third party libraries, such as Argo because they can save you a lot of code that is necessary to validate and safely cast the content of your JSON data to the correct Swift types.

How do I access the data when I am using NSURLSession?

I am new to iOS development. I am using Swift and I have very little experience with Objective-C, so some of the other possibly related answers are tricky to understand. I am trying to understand how to use NSURLSession to get some data from a JSON file on the Web. I found some useful information about getting a file from a URL, but like this other StackOverflow user (NSURLSessionDataTask dataTaskWithURL completion handler not getting called), I heard that NSURLConnection was not the current way to get data, so I'm trying to use NSURLSession.
When I am getting my JSON from the bundle, I am using this extension to
Dictionary (I am pretty sure I got this code from a tutorial):
static func loadJSONFromBundle(filename: String) -> Dictionary<String, AnyObject>? {
let path = NSBundle.mainBundle().pathForResource(filename, ofType: ".json")
if !path {
println("Could not find level file: \(filename)")
return nil
}
var error: NSError?
let data: NSData? = NSData(contentsOfFile: path, options: NSDataReadingOptions(),
error: &error)
if !data {
println("Could not load level file: \(filename), error: \(error!)")
return nil
}
let dictionary: AnyObject! = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions(), error: &error)
if !dictionary {
println("Level file '\(filename)' is not valid JSON: \(error!)")
return nil
}
return dictionary as? Dictionary<String, AnyObject>
}
I'd like to do something similar for getting a dictionary from a JSON file that is on the web because I don't anticipate wanting to include all of my JSON files in the bundle. So far, I have this:
static func loadJSONFromWeb(urlstring: String) -> Dictionary<String, AnyObject>? {
let url = NSURL(string: urlstring)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: NSOperationQueue())
var error: NSError?
//I think I am using the completionHandler incorrectly. I'd like to access the data from the download
let task = session.downloadTaskWithRequest(NSURLRequest(URL: url), {(url, response, error) in println("The response is: \(response)")
})
task.resume()
//Isn't this contentsOfURL thing supposed to go with the connection stuff rather than the session stuff?
//How can I do this with a session? How can I create and use a completionHandler? This way seems clunky.
let data: NSData? = NSData(contentsOfURL: url)
if !data {
println("Could not load data from file: \(url), error: \(error!)")
return nil
}
println("The data is: \(data)")
let dictionary: AnyObject! = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions(), error: &error)
if !dictionary {
println("The file at '\(url)' is not valid JSON, error: \(error!)")
return nil
}
return dictionary as? Dictionary<String, AnyObject>
}
I think that my actual question that most needs answering is this: Where
is the data? I don't think I am using sessions and tasks correctly. I feel like I'm
starting a session to connect to a specific URL and using resume() to
start the download task I want to make happen, but I don't know how to
get the data from that JSON file.
If I need to use a completionHandler and a request in a way similar to what I found here:
(popViewControllerAnimated work slow inside NSURLSessionDataTask) can someone please explain how the 'data' in the completionHandler relates to the data in the fie I am trying to read/download? I am a bit baffled by the completionHandler and how to use it properly.
I looked at the documentation for NSData as well, but I didn't see anything that helped me understand how to get data from my session (or how to initialize an instance of NSData given my session). As far as I can tell form the documentation for NSURLDownloadTask, the task itself is not how I can access the data. It looks like the data comes from the session and task through the completionHandler.
EDIT:
I also looked at the documentation for NSURLSessionDownloadDelegate, but I could really use an example in Swift with some explanation about how to use the delegate. This led me to the URL Loading System Programming Guide. I'm guessing the benefits of using a session must be huge if the documentation is this complicated. I'm going to keep looking for more information on the URL Loading System.
I found this answer helpful (but I'm so new I can't upvote anything yet): https://stackoverflow.com/a/22177659/3842582 It helped me see that I am probably going to need to learn to use a delegate.
I also looked at the URL Loading System Programming Guide. I think what I really need is help with a completionHandler. How can I get my data? Or, am I already doing it correctly using NSData(contentsOfURL: url) (because I don't think I am).
Thank you in advance for any help you can offer!
First, let data: NSData? = NSData(contentsOfURL: url) will return your JSON synchronously. Did you try that and get this working simply? That way you can get started with the rest of your processing while figuring out NSURLSession.
If you're going to use NSURLSession, or a lot of other things in iOS, you need to learn delegates. Fortunately, they're easy. In terms of syntax you just add it to your class declaration like you were inheriting from it. What that does is say that you are going to implement at least the required functions for the delegate. These are callback functions which are generally pretty well documented. It is quite straightforward once you understand it.
If this is not a "heavyweight" project that really needs NSURLSession, you should look at this Swift library. Besides being a really nice way to deal with JSON there is a synchronous call to directly load the JSON from a url. https://github.com/dankogai/swift-json/
Why is NSURLConnection not the correct way to get data? You just should be careful with synchronous requests. Here is an example of how to get data from an url.
func synchronousExampleRequest() -> NSDictionary {
//creating the request
let url: NSURL! = NSURL(string: "http://exampledomain/apiexample.json")
var request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
var error: NSError?
var response: NSURLResponse?
let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
error = nil
let resultDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
return resultDictionary
}