1

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!

Community
  • 1
  • 1
PrairieProf
  • 174
  • 11

2 Answers2

0

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/

hcanfly
  • 172
  • 4
  • I do have the `let data = NSData(contentsOfURL: url)` in there, but I was hoping to replace that with an appropriate use of NSURLSession. This currently will let me see the data from the URL, but I thought I should probably use this opportunity to learn more about delegates (or about completionHandlers at least). What are the dangers of passing my data around synchronously in a heavyweight project? I probably should use a safe approach. Can you think of a useful resource for learning more about delegates? Is there a useful tutorial or SO answer you are aware of? Thanks for your help! – PrairieProf Jul 25 '14 at 16:14
  • Swift resource: http://www.raywenderlich.com/75289/swift-tutorial-part-3-tuples-protocols-delegates-table-views – hcanfly Jul 25 '14 at 17:14
  • and objective c tutorial: http://www.tutorialspoint.com/objective_c/objective_c_protocols.htm – hcanfly Jul 25 '14 at 17:17
  • Simply put, if you say that you are a delegate for a particular protocol you are simply promising to implement at least the required functions in the protocol. These are callbacks. For things like NSURLSession they are usually progress indicators or a partial download (downloading the data in chunks). In these cases you might update a progress indicator for the first, and add the newly downloaded data to that already downloaded. – hcanfly Jul 25 '14 at 17:25
  • last sample: http://www.raywenderlich.com/67081/cookbook-using-nsurlsession. This is a good, short example. If you are going to be doing much session work I would strongly suggest AFNetworking. It wraps NSURLSession and other stuff. Very good. It's what we use at work: http://www.raywenderlich.com/59255/afnetworking-2-0-tutorial – hcanfly Jul 25 '14 at 17:32
0

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
        }
e a
  • 103
  • 9