How Generics Are The Most Powerful Feature in Swift

Posted By : Anmol Aggarwal | 10-Jun-2020

Discover how generics are the most powerful feature in Swift programming language. Initially, they can be befuddling! Many individuals experience difficulty in understanding and utilizing them, particularly application developers. Generics are generally suitable for libraries, frameworks, and SDKs. But in this blog, we are going to investigate how generics work, and what you can do with them.

 

As you already know that in Swift they have a strong type system. When a variable is declared to be a String, you can't simply assign an Int  type value to it. Strictness is usually worth it, as it keeps you from making programming errors. But imagine a scenario in which you need to work with information types that aren't so strict. How about we take a look at an example. Suppose you've made a function that adds one number to another. Like this:

 

func additions(a: Int, b: Int) -> Int
{
    return a + b
}

let result = additions(a: 40, b: 101)
print(result)
// Output: 141

The function in the example above takes two parameters an and b of type Int and returns an addition of two numbers as a value of type Int. The + operator adds the two numbers and returns the outcome. Imagine a scenario in which you need your function to likewise include other number types, for example, Float and Double. The function above can't take any other number type except Int! Due to which you need to compose another function like shown below:

func additions(a: Double, b: Double) -> Double
{
    return a + b
}

 

As you can observe that we have repeated are code rather than reusing it, which is not an ideal thing in programming. So is it possible to make our code reusable? Yes, That's where we utilise the most powerful of Swift,  i.e. "Generics"

 

Generic Functions And Placeholder Types

With generics, you can compose clear, flexible, and reusable code. It helps you to avoid composing a similar code twice, rather it lets you write more generic code and that too in an effective manner.
How about we take the first additions(a:b:) function, and transform it into a "Generic Function". Let's see the example below:

func additions<T: Numeric>(a: T, b: T) -> T
{
    return a + b
}

In this we can see:

  • Rather than Int, the parameters and return type of the function is of type T. This is known as a placeholder type. 
  • The placeholder type T is characterized by <T: Numeric>. This discloses to Swift that T isn't a real type, but a placeholder inside the additions(a:b:) function.

 

The placeholder T doesn't determine what the type of T precisely is, only that the two parameters a and b, and the function return value, should be a similar type T. The input data and result of the function need to have a similar type. The generic additions(a:b:) function would now be able to work with any kind, as long as it fits in with Numeric (more on that later). The capacity can include IntDoubleFloat, etc. It's reusable and adaptable, without unnecessarily repeating code.

let a = additions(a: 40, b: 101)
print(a)
// Output: 141

let b = additions(a: 0.95, b: 0.37)
print(b)
// Output: 1.32

let c = additions(a: Double.pi, b: Double.pi)
print(c)
// Output: 6.28318530717959

 

Generic Type Constraints

The <T: Numeric> sentence structure adds a type of limitation to the placeholder. It characterizes that T needs to fit in with the convention Numeric. We should take a look at another example. Here's a generic function that can find the position of the value in the given array:

func findPosition<T: Equatable>(of foundItem: T, in items: [T]) -> Int?
{
    for (index, item) in items.enumerated()
    {
        if item == foundItem {
            return index
        }
    }
    return nil
}

The function scans for foundItem in the given items array, by comparing each element of the array in a loop. At the point when a match is discovered, it returns the position(index) of the item. The function returns nil when it can't discover the item that was being searched, so the return type of findPosition(of:in:) is Int. As you can see, in the function declaration we have used the placeholder type T. It tells Swift that this function can search any element in an array, as long as the foundItem and item in the cluster are of a similar type. 


The Equatable protocol, as its name infers, is a protocol that declares the == work. This function is used to decide whether two given values are equivalent or not.
Below example illustrates how to utilise the above example:

let names = ["Shinigami", "Ani", "Light", "Zachie", "Barney"]

if let result = findPosition(of: "Light", in: names) {
    print(result)
}

// Output: 3

 

Swift provides us with a couple of these essential protocols: 

  • "Equatable" for values that can be equivalent, or not equivalent 
  • "Comparable" for values that can be compared, similar to a > b 
  • "Hashable" for values that can be "hashed", which is a unique number representation of that value (frequently utilized for dictionary keys) 
  • "CustomStringConvertible" for values that can be represented as a string, a useful protocol for rapidly transforming custom items into printable strings 
  • "Numeric" and "SignedNumeric" for values that number, as 42 and 3.1415 
  • "Strideable" for values that can offset and estimated, similar to sequences, steps, and ranges.

 

Conclusion

So far we've seen generic functions which uses placeholder values to freely determine the input data and result of the function, and protocols to constrain those type. Furthermore, obviously, you can define your own protocols that generic placeholder types need to comply with. In further articles, we will get familiar with generics, protocols, and associated types.

About Author

Author Image
Anmol Aggarwal

Anmol is in mobile team, She is hard working developer and dedicated towards her work.

Request for Proposal

Name is required

Comment is required

Sending message..