Skip to content

Using Generics in Swift

Swift, Generics2 min read

Generics are one of the most powerful features of the Swift programming language, allowing you to write code that is both flexible and highly reusable. In this article, we'll explore what generics are, why they are useful, and how to use them in your Swift projects.

What are Generics?

Generics are a way to write code that is generic or abstracted over types. This means that you can write code once, and then use it with any type that you like, as long as that type meets certain requirements.

The basic idea behind generics is that you can define functions, classes, or structures with placeholders for types that will be determined later. For example, you might write a function that works with integers, but instead of using the Int type directly, you would use a placeholder type such as T. The actual type used for T would be determined when the function is called.

Here's an example of a generic function that takes two arguments of any type and returns a tuple containing both values:

1func combine<T, U>(_ first: T, _ second: U) -> (T, U) {
2 return (first, second)

In this example, we've defined a function called combine that takes two arguments of any type (T and U). The function returns a tuple containing both values.

We can call this function with any types that we like, as long as they are compatible with the placeholders T and U. For example:

1let result = combine("hello", 42) // result is ("hello", 42)

In this case, the types String and Int are used for T and U, respectively, and the resulting tuple contains both values.

Why are Generics Useful?

So why would you want to use generics in your Swift code? The main reason is that it allows you to write code that is more flexible and reusable.

By using generic types and functions, you can create components that work with different types of data without having to write separate code for each specific type. This not only saves you time writing code, but it also makes your code easier to maintain and less prone to errors.

For example, let's say that you need to write a function that finds the largest element in an array of numbers. You could write a separate function for each type of number you might encounter:

1func findLargest(_ array: [Int]) -> Int {
2 var largest = array[0]
3 for element in array {
4 if element > largest {
5 largest = element
6 }
7 }
8 return largest
11func findLargest(_ array: [Double]) -> Double {
12 var largest = array[0]
13 for element in array {
14 if element > largest {
15 largest = element
16 }
17 }
18 return largest

This code works, but it's not very efficient or maintainable. Instead, you could use a generic function to achieve the same result with much less code:

1func findLargest<T: Comparable>(_ array: [T]) -> T {
2 var largest = array[0]
3 for element in array {
4 if element > largest {
5 largest = element
6 }
7 }
8 return largest

In this version of the function, we've used a generic type parameter T that must conform to the Comparable protocol. This ensures that our function can only be called with types that can be compared to each other.

Now we can use this function with any type that conforms to Comparable, such as Int, Double, or even custom types that implement the Comparable protocol.

1let ints = [1, 2, 3, 4, 5]
2let doubles = [3.14, 2.71, 1.41]
3let result1 = findLargest(ints) // result1 is 5
4let result2 = findLargest(doubles) // result2 is 3.14