-2

How can I pass my JSON response (data) from the getStories() function to a new variable (var stories) which is outside of my function and after that to be able to use that new variable into my CollectionViewController ?

Here is my code:

import UIKit

class StoryCollectionViewController: UICollectionViewController {

    // MARK: - ***** Properties *****
    private var stories: Story!

    // MARK: - ***** Life Cycles *****
    override func viewDidLoad() {
        super.viewDidLoad()

        // Register cell class
        collectionView?.register(UINib.init(nibName: "StoryCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: Constants.reuseIdentifier)

        // Get Stories
        getStories()

        print(stories.storyID) // Here should return 15 but return NIL and I don't know why
    }

    // MARK: ***** UICollectionView DataSource Methods *****
    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Constants.reuseIdentifier, for: indexPath) as! StoryCollectionViewCell

        DispatchQueue.main.async {
            //cell.storyImageView.imageFromServerURL(urlString: self.posterUrl)
            //cell.storyNameLabel.text = self.stories?.name
        }

        return cell
    }

    //MARK: - ***** CollectionView Delegate Methods *****
    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        self.performSegue(withIdentifier: "showStoryDetails", sender: self.stories)
    }

    // Get Stories from Amazon API server
    func getStories(){

        Spinner.show("Loading Stories")

        let defaultSession = URLSession(configuration: .default)

        if let urlComponents = URLComponents(string: Constants.baseURL) {

            guard let url = urlComponents.url else { return }

            let dataTask = defaultSession.dataTask(with: url) { (data, _, error) in

                if error != nil {

                    self.showMessage(message: error.debugDescription)
                } else if let data = data {

                    do {
                        let data = try JSONDecoder().decode(ModelJson.self, from: data)

                        self.stories = data.result // This object "stories" is empty outside of this function ..........

                        print("Story ID: \(self.stories.storyID!)") // Here return 15 which is correct
                    } catch {
                        self.showMessage(message: error.localizedDescription)
                    }
                }
                Spinner.hide()
            }
            dataTask.resume()
        }
    }
}

Here is a screen to show you that my JSON response contains data and return 15 in console: JSON response contains data

Thank you guys if you are reading this. Have a great day !

Florentin Lupascu
  • 754
  • 11
  • 30
  • The code works fine and **asynchronously**. Delete the `print` line in `viewDidLoad` and do the things you want to do in the completion handler after or instead of the *working* `print` line. And don't forget to reload the collection view on the main thread. And why is the collection view optional? – vadian Oct 21 '18 at 11:08
  • Modify the function to take a completion handler: `func getStories(completion: (Story) -> Void)` – Code Different Oct 21 '18 at 11:11

2 Answers2

0

You can use closure in getStories function like this:

func getStories(CompletionHandler: @escaping (Story)->()) {
     // 
}

and after data get called, call this closure like this:

CompletionHandler(data.result)

Then, when calling this method, you can handle your object inside the closure and update your controller as you need.

getStories { [weak self] (stories) in
    self?.stories = stories
}
0

The getStories method contains. asynchronous operation. It means that you get response not immediately. You can't get stories after calling the getStories method. Do it after you set stories property. Example:

func getStories(completion: @escaping () -> Void) {
    Spinner.show("Loading Stories")

    let defaultSession = URLSession(configuration: .default)

    if let urlComponents = URLComponents(string: Constants.baseURL) {
        guard let url = urlComponents.url else { return }

        let dataTask = defaultSession.dataTask(with: url) { (data, _, error) in
            if error != nil {

                self.showMessage(message: error.debugDescription)
            } else if let data = data {
                do {
                    let data = try JSONDecoder().decode(ModelJson.self, from: data)

                    self.stories = data.result
                } catch {
                    self.showMessage(message: error.localizedDescription)
                }
            }
            Spinner.hide()
            completion()
        }
        dataTask.resume()
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    getStories(completion: { [weak self] in
        self?.collectionView.reloadData()
    })
}
Ilya Kharabet
  • 4,203
  • 3
  • 15
  • 29