Parsing fetched JSON to dictionary in Swift 3

2019-04-15 05:09发布

问题:

At some point in my app the user supplies a movie name. The controller will get more info about the movie from OMDB and store it. I run into problems getting converting the JSON from the url into a dictionary. Here's my code:

@IBAction func addMovieButtonPressed(_ sender: Any) {
    // get title from the text field and prepare it for insertion into the url
    let movieTitle = movieTitleField.text!.replacingOccurrences(of: " ", with: "+")
    // get data for the movie the user named
    let movieData = getMovieData(movieTitle: movieTitle)
    // debug print statement
    print(movieData)
    // reset text field for possible new entry
    movieTitleField.text = ""
}

// function that retrieves the info about the movie and converts it to a dictionary
private func getMovieData(movieTitle: String) -> [String: Any] {

    // declare a dictionary for the parsed JSON to go in
    var movieData = [String: Any]()

    // prepare the url in proper type
    let url = URL(string: "http://www.omdbapi.com/?t=\(movieTitle)")

    // get the JSON from OMDB, parse it and store it into movieData
    URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
        guard let data = data, error == nil else { return }
        do {
            movieData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
        } catch let error as NSError {
            print(error)
        }
    }).resume()

    // return the data
    return movieData
}

I have tried many variations of the URLSession part that I found on Stackoverflow and I know that the data is successfully fetched:

However I don't know how to properly get at it and turn it into a dictionary I can then use in the rest of my app. The print statement in the IBAction function always returns an empty dictionary.

What am I doing wrong?

回答1:

Look into completion blocks and asynchronous functions. Your getMovieData is returning before the datatask's completion handler is called.

Your function, instead of returning, will call the completion block passed in and should change to:

private func getMovieData(movieTitle: String, completion: @escaping ([String:Any]) -> Void) {

    // declare a dictionary for the parsed JSON to go in
    var movieData = [String: Any]()

    // prepare the url in proper type
    let url = URL(string: "http://www.omdbapi.com/?t=\(movieTitle)")

    // get the JSON from OMDB, parse it and store it into movieData
    URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
        guard let data = data, error == nil else { return }
        do {
            movieData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
            completion(movieData)
        } catch let error as NSError {
            print(error)
        }
    }).resume()
}