An introduction to generics in Swift using its built-in types
Tags: ios, swift
Published: 2019-12-05 09:35:47

Whenever we write code, we want our code to be well-designed. We want it to be flexible, elegant and safe. We want to make sure that Swift’s type system and the compiler catch as many of our mistakes as possible. It’s especially interesting how Swift’s type system can help us avoid obvious errors. For example, Swift won’t allow you to assign an `Int` to a `String` property like this:

```swift
var anInt = 10
anInt = "Hello, World!"
```

The Swift compiler would show you an error that explains that you can’t assign a `String` to an `Int` and you’d understand this. If something is declared or inferred to be a certain type, it can’t hold any types other than the one it started out as.

It’s not always that simple though. Sometimes you need to write code where you really don’t know what type you’re working with. All you know is that you want to make sure that no matter what happens, that type cannot change. If this doesn’t sound familiar to you, or you’re wondering who in their right mind would ever want that, keep reading. This article is for you.

## Reverse engineering Array
A great example of an object that needs the flexibility that I described earlier is an array. Considering that arrays in Swift are created to hold objects of a specific type, whether it’s a concrete type or a protocol, array’s aren’t that different from the mistake I showed you earlier. Let’s adapt the example to arrays so you can see what I mean:

```swift
var arrayOfInt = [1, 2, 3]
arrayOfInt = ["one", "two", "three"]
```

If you try to run this code you will see an error that explains you can’t assign an object of `Array<String>` to `Array<Int>`. And this is exactly the kind of magic that you need generics for.

Arrays are created in such a way that they work with any type you throw at them. The only condition being that the array is homogenous, in other words, it can only contain objects of a single type.

So how is this defined in Swift? What does a generic object like `Array` look like? Instead of showing you the exact implementation from the Swift standard library, I will show you a simplified version of it:

```swift
public struct Array<Element> {
  // complicated code that we don’t care about right now
}
```

The interesting part here is between the angle brackets: `<Element>`. The type `Element` does not exist in the Swift standard library. It’s a made-up type that only exists in the context of arrays. It specifies a placeholder that’s used where the real, concrete type would normally be used.

Let’s build a little wrapper around `Array` that will help you make sense of this a little bit more. 

```swift
struct WrappedArray<OurElement> {
  private var array = Array<OurElement>()

  mutating func append(_ item: OurElement) {
    array.append(item)
  }

  func get(atIndex index: Int) -> OurElement {
    return array[index]
  }
}
```

Notice how instead of `Element`, we use the name `OurElement`. This is just to prove that `Element` really doesn’t exist in Swift. In the body of this struct, we create an array. We do this by using its fully written type `Array<OurElement>()`.  The same can be achieved using the following notation: `[OurElement]()`. The outcome is the same.

Next, in the `append` and `get` methods we accept and return `OurElement` respectively. We don’t know what `OurElement` will be. All we know is that the items in our array, the items we append to it and the items we retrieve from it, will all have the same type.

To use your simple array wrapper you might write something like this:

```swift
var myWrappedArray = WrappedArray<String>
myWrappedArray.append("Hello")
myWrappedArray.append("World")
let hello = myWrappedArray.get(atIndex: 0) // "Hello"
let world = myWrappedArray.get(atIndex: 1) // "World"
```

Neat stuff, right! Try adding an `Int`, or something else to `myWrappedArray`. Swift won’t let you, because you specified that `OurElement` can only ever be `String` for `myWrappedArray` by placing `String` between the angle brackets.

You can create wrapped arrays that hold other types by placing different types between the angle brackets. You can even use protocols instead of concrete types:

```swift
var codableArray = WrappedArray<Codable>
```

The above would allow you to add all kinds of `Codable` objects to `codableArray`. Note that if you try to retrieve them from the array using `get`, you will get a `Codable` object back, not the conforming type you might expect: 

```swift
var codableArray = WrappedArray<Codable>

let somethingCodable = Person()
codableArray.append(somethingCodable)
let item = codableArray.get(0) // item is Codable, not Person
```

The reason for this is that `get` returns `OurElement` and you specified `OurElement` to be `Codable`.

Similar to arrays, Swift has generic objects for `Set` (`Set<Element>`), `Dictionary` (`Dictionary<Key, Value>`) and many other objects. Keep in mind that whenever you see something between angle brackets, it’s a generic type, not a real type.

Before we look at an example of generics that you might be able to use in your own code someday, I want to show you that functions can also specify their own generic parameters. A good example of this is the `decode` method on `JSONDecoder`:

```swift
func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable {
  // decoding logic
}
```

If you’ve never used this method yourself, it would normally be called as follows:

```swift
let result = try? decoder.decode(SomeType.self, from: data) // result is SomeType
```

Let’s pick apart the method signature for decode a bit:

* `func decode<T>`: the `decode` method specifies that it uses a generic object called `T`. Again, `T` is only a placeholder for whatever the real type will be, just like `Element` and `OurElement` were in earlier examples.
* `(_ type: T.Type, from data: Data)`: one of the arguments here is `T.Type`. This means that we must call this method and specify the type we want to decode data into. In the example, I used `SomeType.self`. When you call the method with `SomeType.self` without explicitly specifying `T`, Swift can infer that `T` will now be `SomeType`.
* `throws -> T`: This bit marks `decode` as throwing and it specifies that `decode` will return `T`. In the example, `T` was inferred to be `SomeType`.
* ` where T : Decodable`: this last bit of `decode`'s method signature applies a constraint to `T`. We can make `T` whatever we want, as long as that object conforms to `Decodable`. So in our example, we’re only allowed to use `SomeType` as the type of `T` if `SomeType` is decodable.

Take another look at the method signature of `decode` and let it all sink in for a moment. We’re going to build our own struct in a moment that will put everything together so if it doesn’t make sense yet, I hope it does after the next section.

## Applying generics in your code
You have seen how `Array` and `JSONDecoder.decode` use generics. Let’s build something relatively simple that applies your newfound logic using an example that I have run into many times over the years.

Imagine you’re building an app that shows items in table views. And because you like to abstract things and separate concerns, you have taken some of your `UITableViewDataSource` logic and you’ve split that into a view model and the data source logic itself. Yes, I said view model and no, we’re not going to talk about architecture. View models are just a nice way to practice building something with generics for now.

In your app you might have a couple of lists that expose their data in similar ways and heavily simplified, your code might look like this:

```swift
struct ProductsViewModel {
  private var items: [Products]
  var numberOfItems: Int { items.count }
  
  func item(at indexPath: IndexPath) -> Products {
    return items[indexPath.row]
  }
}

struct FavoritesViewModel {
  private var items: [Favorite]
  var numberOfItems: Int { items.count }
  
  func item(at indexPath: IndexPath) -> Favorite {
    return items[indexPath.row]
  }
}
```

This code is really repetitive, isn’t it? Both view models have similar property and method names, the only real difference is the type of the objects they operate on. Look back to our `WrappedArray` example. Can you figure out how to use generics to make these view models less repetitive?

If not, that’s okay. Here’s the solution:

```swift
struct ListViewModel<Item> {
  private var items: [Item]
  var numberOfItems: Int { items.count }
  
  func item(at indexPath: IndexPath) -> Item {
    return item[indexPath.row]
  }
}
```

Neat, right! And instead of the following code:

```swift
let viewModel = FavoritesViewModel()
```

You can now write:

```swift
let viewModel = ListViewModel<Favorite>()
```

The changes in your code are minimal, but you’ve removed code duplication which is great! Less code duplication means fewer surface areas for those nasty bugs to land on.

One downside of the approach is that you can now use any type of object as `Item`, not just `Favorite` and `Product`. Let’s fix this by introducing a simple protocol and constraining `ListViewModel` so it only accepts valid list items as `Item`:

```swift
protocol ListItem {}
extension Favorite: ListItem {}
extension Product: ListItem {}

struct ListViewModel<Item> where Item: ListItem {
  // implementation
}
```

Of course, you can decide to add certain requirements to your `ListItem` protocol but for our current purposes, an empty protocol and some extensions do the trick. Similar to how `decode` was constrained to only accept `Decodable` types for `T`, we have now constrained `ListViewModel` to only allow types that conform to `ListItem` as `Item`. 

> **Note**  
> Sometimes the `where` is moved into the angle brackets: `struct ListViewModel<Item: ListItem>` the resulting code functions exactly the same and there are no differences in how Swift compiles either notation.

## In summary
In this blog post, you learned where the need for generics come from by looking at type safety in Swift and how `Array` makes sure it only contains items of a single type. You created a wrapper around `Array` to experiment with generics and saw that generics are placeholders for types that are filled in at a later time. Next, you saw that functions can also have generic parameters and that they can be constrained to limit the types that can be used to fill in the generic.

To tie it all together you saw how you can use generics and generic constraints to clean up some duplicated view model code that you actually might have in your projects.

All in all, generics are not easy. It’s okay if you have to come back to this post every now and then to refresh your memory. Eventually, you’ll get the hang of it! If you have questions, remarks or just want to reach out to me, you can find me on [Twitter](https://twitter.com/donnywals).