In our latest iOS project we are using several
UIPickerViews with static content in our UI. Historically we’d have used a Plist file or even hard coded values to store the data, along with an enum as the data is unlikely to change. This can feel like overkill for such a simple component.
Thankfully the new
CaseIterable protocol available in Swift 4.2 (Xcode 10) makes it simple to use enums as a data source for
UIPickerViews that don’t require an additional data source.
For example, using the following enum:
We can access the .allCases array on the enum to populate our
UIPickerView with the corresponding data, using the
This is great, but because we have around 5 of these
UIPickerView / enum types we would end up with several classes that do the same thing if we created a new class for each
UIPickerView. That seems a bit excessive.
Luckily, we can use Generics to create one class that we can use as a data source and populate with each different enum type.
First we create the struct that’s going to hold our enum data. This is a pretty basic struct that only has two properties: first the type, which is our generic property that will be used to hold our enum type; and then the title, which we store as a string.
As the type property is generic we can pass in the enum type when we initialise the struct. The aim is to represent each of our enums with a key / value pair that we can store in an array. We use the enum type as the key and the rawValue as the value. Since Swift enums conform to the
Hashable protocol, they can be used as the key in our array.
To do this, first we create an array of our new structs with the type set to our enum. Then we use Swifts map function to convert the enums to an array of GenericRows as below:
Next, we create the class that we will use as our
UIPickerViewDataSource. Again we use a generic type that we can set later on when we initialise the class:
Now we can initialise our
GenericPickerDataSource class with the
SortMenuOptions type, pass in the items array we created earlier and set this class as the
delegate properties on our
Using this method means we can easily add new picker views with an enum data source just by changing the generic type and creating a new instance of the
GenericPickerDataSource without the overhead of creating multiple new classes and repeating code.
Employing the Delegate Pattern
One of the advantages of using enums in this way means we can pass values easily to a delegate.
First, we create a new protocol that contains a method which we’ll use to pass an enum to the delegate. As we don’t yet know what enum type is yet we’ll need to use the
Next we add the delegate property and implement the
UIPickerView delegate method
didSelectRow on our data source class:
Now, once a user selects a row in our picker view we can notify the delegate of the user’s choice. In order to get the enum type on the delegate we can use a conditional cast to the correct enum type. If the cast is successful then we can use the enum to find the correct type, via a switch statement for example:
Using this method of populating
UIPickerView data has helped us to decrease the amount of classes in our codebase. It could also be easily transferred to
Apple docs: https://developer.apple.com/documentation/swift/caseiterable