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?
You are using a lot of bad practices
NSURL
, NSData
, NSArray
, NSDictionary
and AnyObject
(for JSON data) in Swift. Use native types..mutableContainers
in Swift. The option is pointless. Omit the options
parameterData(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