Swift – Use generics [T] with closures

Let’s make an example to understand better the scope of this tutorial.

You want to:

  • increment all numbers in array by 1
  • double all numbers in array
  • check if the numbers in array are even or odd
  • multiply all numbers in array
  • more and more…

How many functions should you create to do this?

ONE using generics, of course, 4 without generics.

But what are generics? (reference)

Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner.

Generics are one of the most powerful features of Swift, and much of the Swift standard library is built with generic code. In fact, you’ve been using generics throughout the Language Guide, even if you didn’t realize it. For example, Swift’s Array and Dictionarytypes are both generic collections. You can create an array that holds Int values, or an array that holds String values, or indeed an array for any other type that can be created in Swift. Similarly, you can create a dictionary to store values of any specified type, and there are no limitations on what that type can be.


Let’s put some verbose code:

Increment an array element by 1
func increment(array: [Int]) -> [Int] {
    var result: [Int] = []
    for x in array {
        result.append(x + 1)
    }
    return result
}
Multiply an array element by 2
func double(array: [Int]) -> [Int] {
    var result: [Int] = []
    for x in array {
        result.append(x * 2)
    }
    return result
}
Multiply an array element by ITSELF
func multiply(array: [Int]) -> [Int] {
    var result: [Int] = []
    for x in array {
        result.append(x * x)
    }
    return result
}
Check is an element of array is even/odd
func isItemEven(array: [Int]) -> [Bool] {
    var result: [Bool] = []
    for x in array {
        result.append(x % 2 == 0)
    }
    return result
}

So this is the point. You basically need 4 different functions to play with this INT array. Most of the code is duplicated, except for the formula.

print( increment(array: [1,2,3,4,5]) )
[2, 3, 4, 5, 6]

print( double(array: [1,2,3,4,5]) )
[2, 4, 6, 8, 10]

print( multiply(array: [1,2,3,4,5]) )
[1, 4, 9, 16, 25]

print( isItemEven(array: [1,2,3,4,5]) )
[false, true, false, true, false]

How can we improve these methods using generics?

We can create a method called “compute<T>” that do the job:

func compute<T>( array: [Int], transform: (Int) -> T) -> [T] {
    var result: [T] = []
    for x in array {
        result.append(transform(x))
    }
    return result
}

This method accepts as input parameters an array of INT (what we want) and a closure (that make the magic). Returns an array of [T], generic type.

How to use?

We can create a simple method (just to make stuff ordered…) that increment numbers calling our newcompute function passing the formula:

func incrementNumbers(array: [Int]) -> [Int] {
    return compute(array: array, transform: { $0 + 1 })
}

This method add 1 to the $0 (that is the current element of array) and return the incremented value.

What about others? Same thing as before:

func multiply(array: [Int]) -> [Int] {
    return compute(array: array, transform: { $0 * $0 })
}

The cool thing is that  you can use the same function also for boolean for instance:

func isItemEven(array: [Int]) -> [Bool] {
    return compute(array: array, transform: { $0 % 2 == 0 })
}

without changing anything and complicate your life!

Cool right?

Test it in playground:

print( compute(array: [1,2,3,4,5], transform: { $0 + 1 }) )
[2, 3, 4, 5, 6]

print( compute(array: [1,2,3,4,5], transform: { $0 * 2 }) )
[2, 4, 6, 8, 10]

print( compute(array: [1,2,3,4,5], transform: { $0 * $0 }) )
[1, 4, 9, 16, 25]

print( compute(array: [1,2,3,4,5], transform: { $0 % 2 == 0 }) )
[false, true, false, true, false]

You can customize what you want, this is just an example.

 

thank you.

 

Alberto Pasca

Software engineer @ Pirelli & C. S.p.A. with a strong passion for mobile  development, security, and connected things.