Exploring Swift 2 Protocol Extensions

One of Swift 2's most exciting additions are protocol extensions. These allow you to add new methods to anything that implements a protocol. I thought it might be interesting to explore this with a practical example, generating random or repeating sequences from any collection. 

The use-case is quite simple. We have a collection and we would like to either select a certain number of random elements from it, or repeat it up to a certain number. We may like to do either of these, but manage our own stopping condition. 

It's now incredibly easy to inject the new methods into a protocol's implementers

extension CollectionType {
    func randomSequence()->ProxySequence<Self>{
        return ProxySequence(self, length: nil, random:true)
    }
    func repeatingSequence()->ProxySequence<Self>{
        return ProxySequence(self,length: nil)
    }
    func randomSequence(length  length:Int)->ProxySequence<Self>{
        return ProxySequence(self, length: length, random:true)
    }
    func repeatingSequence(length  length:Int)->ProxySequence<Self>{
        return ProxySequence(self,length: length)
    }
}

The four methods are quite simple (I could have used default parameters to reduce to just two, taking an optional length that defaulted to nil, but the semantics felt wrong. I either want to specify a length, or I don't and that should be obvious to someone reading the code). 

So now anything that implements CollectionType will have these methods (The characters of a String, arrays, sets, dictionaries or even something you've implemented yourself). 

I will include the implementation of ProxySequence and its associated GeneratorType here, but it's not really the focus of this post so read only if you care!

struct ProxySequence<C: CollectionType> : SequenceType{
    let contents : C
    let length   : Int?
    let random   : Bool
    init(_ contents:C, length:Int?, random:Bool = false){
        self.contents = contents
        self.length   = length
        self.random   = random
    }
    func generate() -> ProxySequenceGenerator<C> {
        return ProxySequenceGenerator<C>(contents, length: length, random: random)
    }
}
struct ProxySequenceGenerator<C : CollectionType> : GeneratorType{
    let elements : [C.Generator.Element]
    let length : Int?
    var index = 0
    let random : Bool
    init(_ contents:C, length:Int?, random:Bool){
        self.length = length
        self.random = random
        var elements = Array<C.Generator.Element>()
        for elementIndex in contents.enumerate(){
            elements.append(elementIndex.element)
        }
        self.elements = elements
    }
    mutating func next() -> C.Generator.Element? {
        if let length = length where index == length{
            return nil
        }
        defer{
            index++
        }
        if random{
            return elements[Int(arc4random()) % elements.count]
        } else {
            return elements[index % elements.count]
        }
    }
}

Now you can do some cool stuff... like generate random characters from a String

for character in "hello-world".characters.randomSequence(length: 5){
    character
}

Or random primes from an array

for prime in [1,2,3,5,7].randomSequence(length: 5){
    prime
}

Or a repeating sequence of strings from the elements in a dictionary

for even in [0,2,4,6,8].repeatingSequence(length: 10){
    even
}

What a wonderfully powerful and clean capability!

You can download the full playground here