Generics in Swift



Last time we looked at error handling in Swift. In this article we will look at Generics and how they can make use of one set of types to define a new set of types. Let’s start with a simple Car struct. Car is a concrete type.

struct Car {
    var make: String
}

On the other hand, we create the struct Owner as a generic type and T is its type parameter:

struct Owner<T> {}

Finally, driver is a specialization of the generic type.

let driver = Owner<Car>()

Having generics allows us to declare array in various ways:

let array1 = []
let array2 = [Int]()
let array3 = Array<Int>()

While array1 is a NSArray, both array2 and array3 are representing an Array of type Int, so their syntax is equivalent.

Let’s look at how the type parameter can be exploited further:

struct Owner<T> {
    var name: String
    var firstOwner: T
    var reuseOwner: T
}

We can have the same type parameter used also as a struct member. Then creating an Owner becomes more interesting:

let me = Owner(name: "John", firstOwner: Car(make: "Audi"), reuseOwner: Car(make: "BMW"))

Obviously, generics have type inferral:

let array4 = Array(arrayLiteral: 1, 2, 3)   

In this case array4 becomes an Array<Int> by type inference.

For dictionaries there is more information offered. A dictionary is a struct defined as Dictionary<Key : Hashable, Value>. What follows after : is either a supertype or a protocol the key conforms to, and it is named type constraint. Dictionaries also have type inferral:

let ints: [Int: String] = [10: "ten"]
let ages = ["John": 39]

In this case, the ages dictionary is inferred to be of <String, Int> type.

As you might have expected, generics work well on optionals too:

enum Optional<T> {
    case None
    case Some(T)
}

So in this case we have the Optional type that can either be of Some type, or nil. Then we can test which of the two cases we need to deal with, and react accordingly:

let employed = Optional<String>.Some("programmer")
let unemployed = Optional<String>.None
if let job = employed {
    print(employed)
} else {
    print(unemployed)
}

Finally, generics can be used with functions as well. You can notice the type parameters are the same as the function arguments:

func concatenate<A, B>(a: A, _ b: B) -> String {
    return "\(a)\(b)"
}

concatenate("$", 5)

The above function call will print out as you expected:

$5

The source code is posted on Github, as usual.

Until next time!