Swift Generics

Swift Generics

A powerful way to write flexible, reusable code

Swift generics are a powerful way to write flexible, reusable code that avoids duplication. Understanding generics allows you to craft elegant solutions to complex problems. In this article, I'll cover the basics of generics in Swift and how you can start to apply them.

Introduction to Generics

Generics allow you to write code that does not get specific about underlying data types. This enables more flexibility and reusability. Generics have secretly been spread all over Swift. For example, the array is secretly living a double life as a Generic data structure. You can use arrays to store integers, strings, objects, etc. Without generics, Apple would have needed to write separate implementations of arrays for every supported type - including the types you would have created.

Generics save you time and keep your code DRY (Don't Repeat Yourself). Let's look at how they work.

Generic Structures

You can make custom structures generic to handle different value types.
For example - let's implement a classic stack data structure with Generics.

struct Stack<Element> {
  var items = [Element]() 

  mutating func push(_ item: Element) {
    items.append(item)  
  }

  mutating func pop() -> Element {
    return items.removeLast()  
  }
}

This Stack structure has an array of Elements - which can be any type. It also handles two operations: push and pop generically for any Element type passed to it. You can then reuse this stack without modification for types such as integers, strings, etc.

// A Stack of Ints - pushing and 
var intStack = Stack<Int>()

intStack.push(1)
intStack.push(2)
intStack.push(3)
print("Before pop: \(intStack)") 
// outputs: Before pop: Stack<Int>(items: [1, 2, 3])
intStack.pop()
// outputs: After pop:  Stack<Int>(items: [1, 2])

Generic Functions

Functions can also be written generically and reused:

func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
  for (index, value) in array.enumerated() {
    if value == valueToFind {
       return index 
    }
  } 
  return nil
}

This function finds the index of a value in an array, comparing elements with Equatable. It works for any type that conforms to Equatable without needing specific implementations.

Associated Types

When defining a protocol, it's sometimes useful to declare one or more associated types as part of the protocol's definition.

An associated type gives a placeholder name to a type that's used as part of the protocol. The actual type to use for that associated type isn't specified until the protocol is adopted. Associated types are specified with the associatedtype keyword.

protocol Container {
  associatedtype Item 
  func append(_ item: Item)
  var count: Int {get}
}

Here Container says a conforming type will specify its Item type. This allows writing generic container behavior that differing data types can reuse and conform to.

In Summary

Generics lead to code that is more flexible, reusable, and DRY. Mastering generics will make you a more efficient Swift programmer able to elegantly solve problems.

Hope that helped you get started with Generics - Stay Swifty!

Did you find this article valuable?

Support becomingiOS by becoming a sponsor. Any amount is appreciated!