Swift - Custom Type Generic Protocols

Introduction

Generics can apply to protocols.

Consider the following MyStackProtocol protocol:

protocol MyStackProtocol {
     typealias T
     func push(item:T)
     func pop() -> T!
     func peek(position:Int) -> T!
}

Here, the MyStackProtocol protocol specifies that any class that wants to implement a stack data structure needs to implement three methods:

  • push () Accepts an argument of type T
  • pop () Returns an item of type T
  • peek () Accepts an integer argument and returns an item of type T

The protocol does not dictate how elements in the stack are to be stored.

One implementation can use an array, while another can use a double-linked list, for example.

Because the protocol does not dictate the data type that the stack needs to deal with, it declares an associated type using the typealias keyword:

typealias T

The T is the placeholder for the actual data type that would be used by the implementer of this protocol.

When implementing the protocol, you need to implement the required methods declared in the protocol in your implementing class.

The following code snippet shows one example:


class MyOwnStack: MyStackProtocol {

    typealias T = String

    var elements = [String]()

    func push(item:String) {
        elements.append(item)
    }

    func pop() -> String! {
        if elements.count>0 {
            return elements.removeLast ()
        } else {
            return nil
        }
    }

    func peek(position:Int) -> String! {
        if position<0  || position>elements.count-1 {
            return nil
        } else {
            return elements[position]
        }
    }
}

Here, the MyOwnStack class conforms to the MyStackProtocol protocol.

Because you are implementing a stack to manipulate String types, you assign T to String:

typealias T = String

In fact, there is no need to explicitly declare the preceding statement; the type of T can be inferred from the implementation:

func push(item:String ) {    //type of item is  String 
    elements.append(item)
}

The MyOwnStack class can now be rewritten like this:

class MyOwnStack: MyStackProtocol {

    var elements = [String]()

    func push(item:String) {
       elements.append(item)
    }


    func pop() -> String! {
         if elements.count>0 {
             return elements.removeLast ()
         } else {
             return nil
         }
    }

    func peek(position:Int) -> String! {
         if position<0 || position>elements.count-1 {
             return nil
         } else {
             return elements[position]
         }
     }
}

You can use the MyOwnStack class as follows:

var myOwnStack = MyOwnStack ()
myOwnStack.push("Swift")
myOwnStack.push ("Hello")
print(myOwnStack.pop ())    //Hello
print(myOwnStack.pop())    //Swift 

Here is the generic implementation of the MyOwnStackProtocol protocol:

class MyOwnGenericStack<T> : MyStackProtocol {
     var elements = [T ]()

     func push(item:T ) {
         elements.append(item)
     }

     func pop() ->  T! {
         if elements.count>0 {
             return elements.removeLast()
         } else {
             return nil
         }
     }

     func peek(position:Int) -> T! {
         if position<0 || position>elements.count-1 {
             return nil
         } else {
             return elements[position]
         }
     }
}

You can now use the MyOwnGenericStack class as follows:

var myOwnGenericStack = MyOwnGenericStack< String > ()
myOwnGenericStack.push("Swift")
myOwnGenericStack.push("Hello")
print(myOwnGenericStack.pop())    //Hello
print(myOwnGenericStack.pop())    //Swift

Related Topic