Trouble populating UIPickerView with JSON array using Alamofire | Swift

552 Views Asked by At

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?

1

There are 1 best solutions below

5
vadian On BEST ANSWER

You are using a lot of bad practices

  • Don't use NSURL, NSData, NSArray, NSDictionary and AnyObject (for JSON data) in Swift. Use native types.
  • Don't use .mutableContainersin Swift. The option is pointless. Omit the options parameter
  • Swift variable names start with a lowercase letter.
  • Never ever load data from a remote URL with synchronous Data(contentsOf. Use asynchronous URLSession

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