0

I have function call in viewDidLoad method just like that

override func viewDidLoad(){
super.viewDidLoad()
    getSubCategories()
}

And the func definition is below

func getSubCategories(){        
    let parameters = xx
    let url = URL(string: "xx")!
    let session = URLSession.shared
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    
    do {

        request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
    } catch let error {
        print(error.localizedDescription)
    }
    
    let task = session.dataTask(with: request as URLRequest, completionHandler: { [self] data, response, error in
        do {
            if (try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any]) != nil {

The function executes until here then jumps to the task.resume skipping the code after that

        subCategoryModel = try JSONDecoder().decode(SubCategoryModel.self, from: data)
        print(subCategoryModel!)
            }
        } catch let error {
            print(error)
        }
    })
    task.resume()

before decoding the data the collectionViews start reloading and show the error that values are nil. I have set the delegate and datasource in viewdidload. How do I write the code that collectionsviews are loaded after the decoding process.

  • session is Asynchronous call, task.resume kick starts the call to get your data from server, once you receive your data closure block is called, and then you need to handle your scenarios. https://stackoverflow.com/questions/25203556/returning-data-from-async-call-in-swift-function – Tushar Sharma Feb 27 '21 at 11:17
  • Just call `reloadData` on the collection view (on the main thread) after the line `print(subCategoryModel!)`. And all your `JSONSerialization` options are pointless. The server doesn't care about pretty printed JSON and `mutableContainers` is completely useless in Swift. – vadian Feb 27 '21 at 11:30

1 Answers1

0

The DataTask is running on the background. So it is not in parallel with the rest of the code. if you want the part below to be running after, do something like this:

you class that will wait:

class Observable<T> {
    var didChange: ((T?) -> Void)?
    var value: T? {
        didSet {
            // This is for the code to go back to the main thread before running
            DispatchQueue.main.async { 
                self.didChange?(self.value)
            }
        }
    }

    init(_ value: T? = nil) {
        self.value = value
    }

    deinit {
        didChange = nil
    }
}

Now on your ViewController:

class MyViewController: UIViewController {
    var observable = Observable<Data?>()

    override func viewDidLoad(){
        super.viewDidLoad()
        getSubCategories()
    }

    func getSubCategories() {
        obervable.didSet = { data in 
             // your code that will run after goes here
        }
        // your code that will run before goes here

        // your request
        let task = session.dataTask(with: request as URLRequest, completionHandler: { [self] data, response, error in
            do {
                if (try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any]) != nil {

                 // this will trigger the code that needs to run after and give you the data you need
                 self.obervable.value = data
                }
            } catch {
            }
         }
    }
}