0

I am attempting to take a string from JSON data and set it to a variable. My problem is that the variable shows as empty. I am using JSONDecoder to retrieve the JSON data and setting the string to a variable outside of the function. I then want to use that variable inside of another function

When I print the variable it still shows up as blank even after the function has loaded. Within the function the string appears correctly.

Code:

var filmTitle = ""

override func viewDidLoad() {
    super.viewDidLoad()


loadFilms()
print(self.filmTitle) //Prints as an empty string

}

func loadFilms() {

    let id = filmId
    let apiKey = "97a0d64910120cbeae9df9cb675ad235"
    let url = URL(string: "https://api.themoviedb.org/3/movie/\(id)?api_key=\(apiKey)&language=en-US")
    let request = URLRequest(
        url: url! as URL,
        cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalCacheData,
        timeoutInterval: 10 )

    let session = URLSession (
        configuration: URLSessionConfiguration.default,
        delegate: nil,
        delegateQueue: OperationQueue.main
    )

    let task = session.dataTask(with: request, completionHandler: { (dataOrNil, response, error) in
        if let data = dataOrNil {
            do { let details = try! JSONDecoder().decode(Details.self, from: data)
        self.filmTitle = details.title
        print(self.filmTitle) //string prints correctly

                        }
                    }
                })


    task.resume()

}

What am I missing to correctly set the string to the variable?

JSharpp
  • 459
  • 1
  • 9
  • 21
  • Your code is fine. It's just that the data is loaded asynchronously so the `print` in `viewDidLoad` is called long before the data is loaded. – rmaddy Nov 24 '18 at 01:42
  • How can I fix this since I need to use the data for another func. – JSharpp Nov 24 '18 at 01:43
  • I updated the question to declare that I plan to use the variable inside of another function. The new function is also decoding JSON data. I first need the filmTitle in order to run the other func. – JSharpp Nov 24 '18 at 03:23

3 Answers3

0

Loading data from the internet is an asynchronous method. The print statement is being called before loadFilms() has completed.

Use a callback to get the data after it has completed.

func loadFilms(completion: @escaping (Details?, Error?) -> Void) { 
    //...

 let task = session.dataTask(with: request, completionHandler: { (dataOrNil, response, error) in
    if let data = dataOrNil {
        do { let details = try JSONDecoder().decode(Details.self, from: data)
     completion(details, nil) 


           } catch { 
               completion(nil, error) 
                }
            })

}

At the call site:

override func viewDidLoad() { 

   loadFilms { details, error in 

     if error { //* Handle Error */ } 

     self.filmTitle = details.title
     print(filmTitle)

    }

}
SirCJ
  • 505
  • 6
  • 11
0

Web request are asynchronous and from the CP's perspective, take a long time to complete. When you call this:

override func viewDidLoad() {
    super.viewDidLoad()

    loadFilms()
    print(self.filmTitle) // loadFilms() hasn't finished so `filmTitle` is empty
}

It's better to set a property observer on filmTitle:

var filmTitle: String? = nil {
    didSet {
        print(filmTitle)

        Dispatch.main.async {
            // update your GUI
        }
    }
}
Code Different
  • 90,614
  • 16
  • 144
  • 163
0

The solution to this problem was to reload the collection view that the array was being sent to within the decoder function after the data was set to the array.

JSharpp
  • 459
  • 1
  • 9
  • 21