Problem using closures and async

Hey CodeCrew!

I need your help trying to apply what I’ve learned here without any success…

My app should be able to use json to transfer data on an online database when given a product code and return the details found.

I have created my model as follow:

struct Product() {
var code:Int?
var productName:String?
var brand:String?
}

I have created my controller like this:

import Foundation
import Alamofire
import SwiftyJSON

class RetrieveProductFromWebsiteModel {
    
    var productDirectory = Product()
    
    func retriveProduct(productSku: Int, completion: @escaping (Product) -> Void) {
        let urlString = "https://example.com/file.json"
        DispatchQueue.main.async {
            let request = AF.request(urlString)
            request.responseJSON { (response) in
                guard let _ = response.data else {
                    return
                }
                let json = try? JSON(data: response.data!)
                
                self.productDirectory.code = json?["code"].int
                self.productDirectory.productName = json?["product_name"].string
                self.productDirectory.brandName = json?["brand-name"].string
            }
            completion(self.productDirectory)
        }
    }
}

And in my View Controller:

  • UIButton
  • UITextField
import UIKit

class ViewController: UIViewController {
    
    var model = RetrieveProductFromWebsiteModel()
    var product:Product?
    
    @IBOutlet weak var jsonTextView: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    @IBAction func showJson(_ sender: Any) {
        
        let productCode = 123456    // Note: this code will be variable in next integration

        DispatchQueue.main.async {
            self.model.retriveProduct(productSku: productCode, completion: { (product) in
                print(product.code)
                print(product.productName)
                self.jsonTextView.text  = "Name is " + (product.productName ?? "") + "\n"
            })
        }
    }
}

My problem is that "Name is " gets printed (and “nil / nil” in the console) the first time I click the button, the second time it is finally printing the correct values.

So I presume I am not using the DispatchQueue.main.async as I should… but how?

Many thanks in advance!

First of all you should be retrieving data first and then once you have it back use the DispatchQueue.

Your fetch to get data should NOT be on the main thread inside retrieveProducts.

But I’m not exactly sure how to use AlamoFire or SwiftJSON

Many thanks @MikaelaCaron for your swift answer :wink:

I have read a bit more about DispatchQueue threads and Sync/Async and I think I have a bit more understanding now how it could work…

However, I did not managed to get it working so I have explored the Protocol/Delegate method as I think it is what I am looking for my app.

But I still have the same problem:

  • my button and textfield are populated on the View Controller
  • I click on my button and the IBAction fire my self.model.retriveProduct() function
  • it gets executed before my data are available so I get Product() nil
  • I hit again and my values gets populated properly…

I “kind” of understand what is happening but I don’t know how to resolve the problem.

Really looking forward for anyone input to understanding the whole process.

In my final app, I would like the user to look for an item by code, the system to check if available in app database, if not gets it populated by API source and added to the app database.
So I think I really need to understand how to display the fetched data in the view! :stuck_out_tongue: