0

My app is decoding some JSON data from a PHP web service but the data is not showing on the labels when I click the fetch button for the first time.

In debug I can see that the data is passed to the array but it's not passed to the labels in the view controller.

Can you please help me on this?

Thanks in advance,

Adelmo

Button click

@IBAction func btn_ler(_ sender: Any)
    {

        //txt_matricula.text = ""
        //txt_marca.text = ""
        //txt_modelo.text = ""

        obter_dados_veiculo(matricula: input_matricula.text!)

        for veiculo in a_veiculos
        {
            txt_matricula.text = veiculo.MATRICULA
            txt_modelo.text = veiculo.MODELO
            txt_marca.text = veiculo.MARCA
        }

        //lbl_num_servicos.text = String( a_veiculos.count)
    }

Function to retrieve the JSON

func obter_dados_veiculo (matricula : String)
    {
        //Variável que vai conter o URL
        let v_url = "http://arshome.hopto.org/Domingues/DEV/api_get_veiculos.php?MATRICULA=" + matricula

        let o_url = URL(string: v_url)

        URLSession.shared.dataTask(with: o_url!)
        {
            (data, response, error) in

            do
            {
                let matriculas = try JSONDecoder().decode([s_veiculo].self, from: data!)


                //for matricula in matriculas
                //{
                    //print(matricula.MATRICULA)
                a_veiculos = []
                    a_veiculos.append(contentsOf: matriculas)
                //}
            }
            catch
            {
                print("We have an error!")
                //self.txt_matricula.text = "We have an error!"
            }
            print( a_veiculos.count)
            }.resume()
    }
  • Your call is asynchronous and you are updating your labels _before_ the data has been returned so the array is still. You need to update the lables in the completion handler using DispatchQueue.main.async(). Something like `DispatchQueue.main.async() { txt_matricula.text = veiculo.MATRICULA txt_modelo.text = veiculo.MODELO txt_marca.text = veiculo.MARCA }` after you have appended to your array. See also https://stackoverflow.com/questions/24231680/loading-downloading-image-from-url-on-swift/27712427#27712427 – Joakim Danielson Jun 18 '19 at 09:07
  • Thanks a lot! This is really helpful. it woks like a charm. – Adelmo Silva Jun 18 '19 at 11:36

4 Answers4

1

You have to add a completion closure in your obter_dados_veiculo method like below:

func obter_dados_veiculo (matricula : String, completion:@escaping(Bool,Error?)->())
{
    //Variável que vai conter o URL
    let v_url = "http://arshome.hopto.org/Domingues/DEV/api_get_veiculos.php?MATRICULA=" + matricula

    let o_url = URL(string: v_url)

    URLSession.shared.dataTask(with: o_url!)
    {
        (data, response, error) in

        do
        {
            let matriculas = try JSONDecoder().decode([s_veiculo].self, from: data!)


            //for matricula in matriculas
            //{
            //print(matricula.MATRICULA)
            a_veiculos = []
            a_veiculos.append(contentsOf: matriculas)
            completion(true,nil)
            //}
        }
        catch let error as NSError
        {
            print("We have an error!")
            completion(false,error)
            //self.txt_matricula.text = "We have an error!"
        }
        print( a_veiculos.count)
        }.resume()
}

And call it like below:

@IBAction func btn_ler(_ sender: Any)
{

    //txt_matricula.text = ""
    //txt_marca.text = ""
    //txt_modelo.text = ""

    obter_dados_veiculo(matricula: input_matricula.text!) { (success, error) in
        for veiculo in self.a_veiculos
        {
            txt_matricula.text = veiculo.MATRICULA
            txt_modelo.text = veiculo.MODELO
            txt_marca.text = veiculo.MARCA
        }
    }

    //lbl_num_servicos.text = String( a_veiculos.count)
}
Torongo
  • 1,021
  • 1
  • 7
  • 14
1

Your problem is that you do asynchronous network request, meaning that it is not guaranteed that by the time you are assigning text to labels the network response has been received.

    obter_dados_veiculo(matricula: input_matricula.text!)

    //RESPONSE MAY NOT BE RECEIVED

    for veiculo in a_veiculos
    {
        txt_matricula.text = veiculo.MATRICULA
        txt_modelo.text = veiculo.MODELO
        txt_marca.text = veiculo.MARCA
    }

One of the options you could do is to add property observer to a_veiculos, such as:

var a_veiculos: [s_veiculo] = [] {
    didSet {
        for veiculo in a_veiculos
          {
             txt_matricula.text = veiculo.MATRICULA
             txt_modelo.text = veiculo.MODELO
             txt_marca.text = veiculo.MARCA
          }
    }
}
vdmzz
  • 261
  • 1
  • 4
0

Write another method initializeData() like this.

 func initializeData() {
   for veiculo in a_veiculos
    {
        txt_matricula.text = veiculo.MATRICULA
        txt_modelo.text = veiculo.MODELO
        txt_marca.text = veiculo.MARCA
    }

 }

And call this method when you intiailize your array.

 func obter_dados_veiculo (matricula : String)
     {
    //Variável que vai conter o URL
    let v_url = "http://arshome.hopto.org/Domingues/DEV/api_get_veiculos.php?MATRICULA=" + matricula

    let o_url = URL(string: v_url)

    URLSession.shared.dataTask(with: o_url!)
    {
        (data, response, error) in

        do
        {
            let matriculas = try JSONDecoder().decode([s_veiculo].self, from: data!)


            //for matricula in matriculas
            //{
                //print(matricula.MATRICULA)
            a_veiculos = []
                a_veiculos.append(contentsOf: matriculas)
          initializeData()
            //}
        }
        catch
        {
            print("We have an error!")
            //self.txt_matricula.text = "We have an error!"
        }
        print( a_veiculos.count)
        }.resume()
}
Awais Khan
  • 116
  • 9
0

Use a completion Handler, so that you get an update when your API call is getting finished. And update the UI accordingly. Here is how you can use a Completion Handler.

@IBAction func btn_ler(_ sender: Any)
{

    obter_dados_veiculo(matricula: input_matricula.text!) {
        DispatchQueue.main.async {
            for veiculo in a_veiculos
            {
                txt_matricula.text = veiculo.MATRICULA
                txt_modelo.text = veiculo.MODELO
                txt_marca.text = veiculo.MARCA
            }
        }
    }
}

func obter_dados_veiculo (matricula : String, completionHandler: @escaping () -> Void) {
    //Variável que vai conter o URL
    let v_url = "http://arshome.hopto.org/Domingues/DEV/api_get_veiculos.php?MATRICULA=" + matricula
    let o_url = URL(string: v_url)
    URLSession.shared.dataTask(with: o_url!) {
        (data, response, error) in
        do {
            let matriculas = try JSONDecoder().decode([s_veiculo].self, from: data!)
            a_veiculos = []
            a_veiculos.append(contentsOf: matriculas)
        }
        catch {
            print("We have an error!")
            //self.txt_matricula.text = "We have an error!"
        }
        print( a_veiculos.count)
        }.resume()
}
Shubham
  • 763
  • 5
  • 20