We have previously discussed generic functions on this blog in the post Generic Functions in Swift. There is one more aspect to generics in Swift that we have not covered yet, Generic types. Generic types let you write a named type (class, enumeration, or structure) that can accommodate different types in its implementation.
Now, that just sounds silly right there, why would you want to handle other types in your type? Well, the main and most predominant reason is for collections. The best example of this, is the built in Array and Dictionary types. The shorthand is a lot more readable, and is the recommended way to create Arrays and Dictionaries, but really, [Int] and [String : Int] are actually Array<Int> and Dictionary<String, Int> under the hood. See those angle brackets? Yep, those are the same kind of angle brackets used in generic functions.
Avoid Repetition by using Generic Types
Let’s say we want a type that can store an array of Integers, and then lets us read one element of that array at random, one way to do that would be like this:
struct RandomIntBag { let bag: [Int] func get() -> Int { //Random index less than length of array let randomIndex = Int(arc4random_uniform(UInt32(bag.count))) return bag[randomIndex] } }
Now, this code does have one major flaw. If you give it an empty array, and try to use the get method, it will crash. However, adding the safety code to this muddies up the example. Really, the get method should probably return an optional Int, but for now, let’s just dictate that you must never give this an empty array.
Anyway, that definitely gets the job done. We generate the random number with a C function named arc4random_uniform. Unfortunately it takes and returns a UInt32, so we have to do a few conversions to let it work with our array index and count. It will return a random number less than the argument given, in this case a UInt32 version of the bag’s count property (which is natively a Swift Int). We then just return whatever is at that random index that we just generated.
Now, what if we wanted similar functionality for a Swift String? Well we would have to make a RandomStringBag, of course! It would look like this:
struct RandomStringBag { let bag: [String] func get() -> String { //Random index less than length of array let randomIndex = Int(arc4random_uniform(UInt32(bag.count))) return bag[randomIndex] } }
Now… that looks awfully similar to our RandomIntBag, doesn’t it? To further illustrate that, these are the only lines that have changed inside their respective structure definitions:
let bag: [Int] let bag: [String] func get() -> Int { func get() -> String {
Only the type of the array and the return type of the get method actually changed. They both hold Swift Arrays underneath, so all of the code inside of the get method is completely the same between them. Looks like a great time to use generics!
As such, behold the RandomBag:
struct RandomBag<T> { let bag: [T] func get() -> T { //Random index less than length of array let randomIndex = Int(arc4random_uniform(UInt32(bag.count))) return bag[randomIndex] } }
Now you just create a randomBag with an array of whatever type you want, and then get your random value from the get method:
let test = RandomBag(bag: [7,891,894,121,5189,514,42]) test.get() //Output: 891 test.get() //Output: 42
Of course, since this is random, obviously the values you get out will probably be different than this example.
Now that this is a generic type, we could even throw custom classes in there. Maybe you’re writing an RPG game, and each area the player is in has a set of monsters that they would battle during a random encounter. You just give it an array of that custom Monster type, complete with whatever information you need for that battle.
Conclusion
While Generic Types are very useful, since they are the very basis of Swift’s Array and Dictionary type, I personally am having trouble figuring out a use for them outside of implementing other collection types.
Apple’s iBook uses them to create a Stack type collection (which is basically an array where you can only append or read new data from the top, so it is Last-In-First-Out, or LIFO), as well as a Collection protocol to explain other aspects of the use of Generics in Swift. A queue data type could also make sense as well, which is similar to the stack, but you can add elements to only one end, and remove them from the other, like the line many of us wait in to get our new iPhones, so this is First-In-First-Out, or FIFO).
Nonetheless though, those are both collections. One non-collection use for generic types I’ve found is creating a Factory type that can generate a new object of whatever type you specify to the generic type. Especially with the addition of Failable Initializers in Swift 1.1, Swift is moving away from the use of Factory types, so I decided not to show an example of that.
If you need a collection type that is not available in vanilla Swift like the Stack or the Queue, or if you just have a completely novel type of collection that you need, Swift’s Generic Types are the tool for the job.
I hope you found this article helpful. If you did, please don’t hesitate to share this post on Twitter or your social media of choice, every share helps. Of course, if you have any questions, don’t hesitate to contact me on the Contact Page, or on Twitter @CodingExplorer, and I’ll see what I can do. Thanks!