Mobile app development for iOS and Swift

A simple generic UIPickerDataSource


Recently I had to deal with a lot of UIPicker views in an app for a customer. In order to reduce the amount of code to write I tried to create a generic way of setting up a picker view which can represent any kind of data. There are several posts which pop up when searching for a generic UIPickerDataSource but I wanted to mix it up with something on my own. So let's start!

The Generic Data Source

At first create a GenericRow<T> struct which represents one row inside the UIPickerView.

struct GenericRow<T> {
    let type: T
    let title: String
    public init(type: T, title: String) {
        self.type = type
        self.title = title

It owns type, the data for one row of the picker and has a title, the text to be displayed inside the picker view to represent the type.

Let us utilize the GenericRow<T> inside a GenericPickerDataSource<T> class.

class GenericPickerDataSource<T>: NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
    public var originalItems: [T]
    public var items: [GenericRow<T>]
    public var selected: (T) -> Void
  // snip

Declare three attributes:

  • originalItems, which stores a list of the original data items this data source will represent
  • items, which stores a list of GenericRow<T>
  • selected, a callback which is executed when the user selects one item from the picker

Now we implement the initializer our generic data source.

public init(withItems originalItems: [T], withRowTitle generateRowTitle: (T) -> String, didSelect selected: @escaping (T) -> Void) {
    self.originalItems = originalItems
    self.selected = selected
    self.items = originalItems.map {
        GenericRow<T>(type: $0, title: generateRowTitle($0))

It takes three arguments:

  • the original data items to be selectable
  • a function which generates a string based on the type of the data items
  • a callback function which is executed when the user selects an item

generateRowTitle() is used to create GenericRow<T> instances. It uses the result of the method as it's title, which will later be displayed to the user inside the picker view.

The last step for the implementation is to make GenericPickerDataSource<T> conform to the protocols.

func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 1
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return items.count
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    return items[row].title
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

UITextField Extensions

In order to further simplify the creation of a picker view, declare two extensions on UITextField.

extension UITextField {
    func setupPickerField<T>(withDataSource dataSource: GenericPickerDataSource<T>) {
        let pickerView = UIPickerView()
        self.inputView = pickerView
        pickerView.delegate = dataSource
        pickerView.dataSource = dataSource
    func addDoneToolbar(onDone: (target: Any, action: Selector)? = nil) {
        let onDone = onDone ?? (target: self, action: #selector(doneButtonTapped))
        let toolbar: UIToolbar = UIToolbar()
        toolbar.barStyle = .default
        toolbar.tintColor = UIColor.blue
        toolbar.items = [
            UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: self, action: nil),
            UIBarButtonItem(title: "Done", style: .done, target: onDone.target, action: onDone.action)
        self.inputAccessoryView = toolbar
    // Default actions:
    @objc private func doneButtonTapped() { self.resignFirstResponder() }

setupPickerField takes a GenericPickerDataSource and sets up the picker view for us. addDoneToolbar creates a toolbar above the input view of the text field and adds a "done" button to it which will close the view on click.

Use it

Now everything is ready to be used. Assume you want to have a picker where users need to select a country. First create a data object which holds the data:

struct Country {
    var id: Int
    var name: String

Create a GenericPickerDataSource<Country> from a list of countries. Specify how the text, which is displayed inside the picker view, is generated and the callback which is executed when the user selects a country.

class ViewController: UIViewController {
    @IBOutlet weak var countryTextField: UITextField!
    var dataSource: GenericPickerDataSource<Country>?
    override func viewDidLoad() {
        let countries =  [Country(id: 0, name: "Norway"),
                          Country(id:1, name: "Sweden"),
                          Country(id: 2, name: "Finland")]
        self.dataSource = GenericPickerDataSource<Country>(
            withItems: countries,
            withRowTitle: { (country) -> String in
                return country.name
            didSelect: { (country) in
                print("Country \(country) was selected !")
                self.countryTextField.text = country.name
        countryTextField.setupPickerField(withDataSource: dataSource!)

When you run the code and tap on the text field you can select the country of your choice.

Define File's Owner class


With an initial set up of a generic data source we have created an easy way of declaring picker views which can accept any kind of data throughout our application. This is especially helpful if your app is dealing with a lot of UIPickerViews.

One thing to note here, is that the current implementation only supports one section inside the picker. We can think about improving the implementation by supporting more than one section. But this is something we could further elaborate in the future.