iosswiftalamofireuipickerviewuipickerviewcontroller

Trouble populating UIPickerView with JSON array using Alamofire | Swift


Essentially I have a textField that when pressed needs to open a UIPickerView with a selection that comes from JSON

I have separately worked on triggering a UIPickerView when selecting a UItextField and creating arrays from JSON in Swift but am having some trouble putting together.

For the JSON I am using Almofire simply because it simplifies the process and the UIPickerView is written programmatically.

The JSON I am working with looks like this:

[{“model”:”model1”},{“model":"model2”},
{“model":"model3”},{“model":"model4”},{“model":"model5”},{“model":"model6”}]

The Almofire so far looks like this:

        let url = NSURL(string: "https://www.test.com/test/test")

        let data = NSData(contentsOf: url! as URL)
        var tmpValues = try! JSONSerialization.jsonObject(with: data! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSArray
        tmpValues = tmpValues as NSArray
        reloadInputViews()


        for candidate in tmpValues {
            if let cdict = candidate as? NSDictionary {

                //model is the column name in sql/json
                let model = cdict["model"]
                self.values.append(model! as AnyObject)


            }
        }

Triggering the textField to open a UIPickerView is done using the following code:

import UIKit

class ViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {


    @IBOutlet weak var TextField: UITextField!

    let model = ["model1","model2"]

    var pickerview = UIPickerView()

    override func viewDidLoad() {
        super.viewDidLoad()


        TextField.inputView = pickerview
        TextField.textAlignment = .center
        TextField.placeholder = "Select Your Model"

        pickerview.delegate = self
        pickerview.dataSource = self

        // Do any additional setup after loading the view, typically from a nib.
    }

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return Names.count
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return Names[row]
    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        TextField.text = Names[row]
    }

}

How can I replace the hard-coded array with the JSON response?


Solution

  • You are using a lot of bad practices

    The most efficient solution is to decode the JSON with Decodable

    Outside of the class declare the struct

    struct Model : Decodable {
        let model : String
    }
    

    Declare the picker source as variable and in plural form

    var models = [String]()
    

    In viewDidLoad insert at the end

    let url = URL(string: "https://www.test.com/test/test")!
    let dataTask = URLSession.shared.dataTask(with: url) { data, _, error in
        if let error = error { print(error); return }
        do { 
            let result = try JSONDecoder().decode([Model].self, from: data!)
            self.models = result.map{$0.model}
            DispatchQueue.main.async {
              self.pickerview.reloadAllComponents()
            }
        } catch { print(error) }
    }
    dataTask.resume()
    

    Even with JSONSerialization (without the Model struct) it's pretty straightforward

    let url = URL(string: "https://www.test.com/test/test")!
    let dataTask = URLSession.shared.dataTask(with: url) { data, _, error in
        if let error = error { print(error); return }
        do { 
            if let result = try JSONSerialization.jsonObject(with: data!) as? [[String:String]]
                self.models = result.compactMap{$0["model"]}
                DispatchQueue.main.async {
                  self.pickerview.reloadAllComponents()
                }
            }
        } catch { print(error) }
    }
    dataTask.resume()
    

    The picker data source methods are

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return models.count
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return models[row]
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        TextField.text = models[row]
    }
    

    Alamofire is overkill for a simple GET request