Working with Generic Programming in Swift

Working with Generic Programming in Swift
COMMENTS (0)
Tweet

Hi guys, in this post we’ll look at generic programming and functions that you can use when coding in Swift, and how useful they can be. Let’s get right to it.

 What is generic programming??

For those of you not familiar with the concept, generic programming is a style of programming in which algorithms are written in terms of types to-be specified –later, that are instantiated when needed, for specific types provided as parameters. This approach, pioneered by ML (general-purpose functional programming language) in 1970, enables you to write common functions or types that differ only in the set of types on which they operate when used, thereby reducing duplication.

Generics in Swift

Generics are primarily used avoid duplication and to provide abstraction. Swift also provides certain generic features that allow you to write flexible and reusable functions and types. In fact, Swift’s own standard libraries are built with generics code. For example, Swift’s ‘Array’ and ‘Dictionary’ types belong to generic collections. Any arrays you create using these generic arrays and dictionaries, can be defined to hold ‘Int’values and ‘String’ values, or any other types.

Let’s consider the example of the swapInt(_: _:): function, as shown below.

func swapInt(inout a: Int, inout b: Int) {
    let temp = a
    a = b
    b = temp
}

var numb1 = 100
var numb2 = 200

print("Before Swapping values are: \(numb1) and \(numb2)")
exchange(&numb1, &numb2)
print("After Swapping values are: \(numb1) and \(numb2)")

 

If you run the above code you’ll get the following result:

Before Swapping values are: 100 and 200 
After Swapping values are: 200 and 100

 

As you can see, the swapInt(_:_:) method is particularly useful, as it swaps the original value of b into a, and the original value of a into b. However, it can only be used with Int  values. If you want to do more with the swapInt(_:_:) method, like swapping two String values or two Doubles, then you’ll have to write more functions, such as swapString(_:_:) and swapDouble(_:_:)as shown below:

func swapString(inout a: String, inout b: String) {
    let temp = a
    a = b
    b = temp
}

 

func swapDouble(inout a: Double, inout b: Double) {
    let temp = a
    a = b
    b = temp
}

 

As you can see, only the function signatures of swapDouble(_:_:), swapString(_:_:)and swapInt(_:_:) are different, and they share identical bodies. So if you’re using these functions in combination, you not only end up with two functions, you also end up with repeated code inside them. A more useful approach would be to write a single function that can swap two values of any type. That’s where generic functions come in.

Generic Functions

Generic functions can work with any type. Given below is the generic version of the swapInt(_:_:) function that we used above, which is called swapValues(_:_:)

func swapValues<T>(inout a: T, inout b: T) {
    let temp = a
    a = b
    b = temp
}

 

As you can see, there is a slight difference in both functions’ signature

func swapValues<T>(inout a: T, inout b: T);
func swapInt(inout a: Int, inout b: Int);

 

In the generic function’s signature, T is used as a placeholder type name instead of the actual type (Int, String or Double etc). The actual Type to use in place of T will be determined each time the swapValues(_:_:) function is called. The other difference is the type parameter.

Note
Type parameters are used specify and name a placeholder type, and are written immediately after the function’s name between a pair of matching angle brackets (such as <T >). There can be more than one type parameter names in single generic function separated by commas.

 

The swapValues(_:_:) function can now be called in the same way as swapInts(_:_:), but both parameters’ types should be the same. That way each time swapValues(_:_:) is called, the type to use for T is inferred from the types of values passed to the function.

Generic Types

Swift also allows you to define your own generic types. These are custom classes, structures and enumerations that can work with any type, in the same manner as Array and Dictionary.

Lets consider the example of the queue function (as depicted in the snippet below).

A queue is a data structure similar to a list or stack where you can add new values only at the end of the queue (known as en-queuing) and take values values only from from the top of the queue (de-queuing).

struct Queue<Element> {

    private var elements = [Element]()
    
    
    mutating func enqueue(newElement: Element) {
        elements.append(newElement)
    }
    
    mutating func dequeue() -> Element? {
        guard !elements.isEmpty else {
            return nil
        }
        return elements.removeAtIndex(0)
    }
    
}

var q = Queue<Int>()

q.enqueue(4)
q.enqueue(2)

q.dequeue()
q.dequeue()
q.dequeue()
q.dequeue()

 

In the above code, Queueis a generic type over Element instead of the actual type  (Int, String and Double etc.). Element here defines a placeholder name for “some type Element” to be provided later on. Since it is a generic type, you can use the Queue to store any data type. In the above example for instance, we’re using the Queue to store integers (Int).

Type Constraints

Lets look at an example of a sorting method. The following method will sort the data and return the middle value after applying sorting:

func mid<T>(array: [T]) -> T {
    return array.sort()[(array.count - 1) / 2]
}

 

This code is not compliable however. The problem is the sort() method used, which requires an array with elements should be Comparable. By changing function declaration to the snippet show below, will solve the problem:

func mid<T: Comparable>(array: [T]) -> T {
    return array.sort()[(array.count - 1) / 2]
}

 

What we’ve done above is simply used a type constraint. You write type constraints by placing a single class or protocol constraint after a type parameter’s name, separated by a colon, as a part of the type parameter list.

 

By adding type constraints, you are imposing a restriction that the data type used in a generic function should implement the type constraints protocol.

Conclusion

In the above post I showed you to how to write reusable methods using generic programming in Swift and how you can create custom types using generics in Swift. Hope you find it useful.

References

CALL

USA408 365 4638

VISIT

1301 Shoreway Road, Suite 160,

Belmont, CA 94002

Contact us

Whether you are a large enterprise looking to augment your teams with experts resources or an SME looking to scale your business or a startup looking to build something.
We are your digital growth partner.

Tel: +1 408 365 4638
Support: +1 (408) 512 1812