Saturday, April 11, 2015

Generics Functions in Swift

For developers that have used the Java or C# programming languages, Generics in Swift should look pretty familiar because Swift’s implementation is very similar to those languages.  For other developers Generics may seem a bit foreign at first.

Before we look at how to use Generics in Swift, let’s take a look at the problem that they are designed to solve.  Lets say that we wanted to write a function that swapped the values of two Int types.  We could very easily write the function like this:

func swapInts (inout x: Int, inout y: Int) {
    let tmp = x
    x = y
    y = tmp
}

Now lets say that we also have a need to swap the values of two String types.  We could then write another function like this:

func swapString (inout x: String, inout y: String) {
    let tmp = x
    x = y
    y = tmp
}

Having two functions like this isn’t too bad but what if we also had a need two swap two UInt, Double and Boolean types as well?  This could get out of hand very quickly.   The worse part is we are duplicating most of the code each time we add a new swap function.  This is where Generics can help us.

Generics allow us to write flexible and reusable code that avoids the duplication that we saw in the previous examples.  Now lets look at how we could write one function with Generics that would allow us to swap the values of any data type. 

func swapGeneric<T> (inout x: T, inout y: T) {
    let tmp = x
    x = y
    y = tmp
}

The swapGeneric() function looks pretty similar to the other two swap functions that we saw earlier except for the capital T.  The capital T is used as a placeholder and lets Swift know that we will be defining the type, associated with the placeholder, at run time.  When we define a Generic function we include the placeholder between two angled brackets (<>).  We can then use that placeholder in place of any type definition within the function, the function’s definition or as the return type of the function.

There is nothing special about the capital T that we used in our example.  We can actually use any valid identifier but generally we use a capital T or a capital E.
We would use the genericSwap() function like this:

var a = 1.1
var b = 2.2
swapGeneric(&a, &b)
println("a = \(a)  b = \(b)")


var c = "One"
var d = "two"
swapGeneric (&c, &d)
println("c = \(c) d = \(d)")

In these examples, we see that we can swap two Double types or two String types with the same swapGeneric() function.  We could expanded these examples to include any data type that we want.
We are also not limited to using just a single generic type.  Lets look at an example of where we use two generic types.

func printValue<T,E> (value1: T, value2: E) {

    println("value1:  \(value1)")
    println("value2:  \(value2)")
}

In this function we have two generic types defined, the T and the E.  Here are a couple of examples of how we could use this function.

printValue(“hello”, 1234)
printValue(3.1415, “bye”)

We can see that we can include different data types within the parameters that we are sending to the printValue() function.

In this post, we briefly looked at how we could use Generics within a function definition to create a function whose parameter types can change at runtime as needed.  This is one of the most popular uses for Generics and is also the basis of Swift’s collection types (Array, Dictionary).  For a more in-depth discussion of Generics and how to create Generic and Associated types, check out my book Mastering Swift published by Packt Publishing.


No comments:

Post a Comment