Protocols and associatedtypes.

One of the most promising feature in Swift is associatedtype. Prior to Swift 2.2, the keyword used to define a placeholder type inside protocol was typealias. But this was very confusing as typealias in protocols means something else than in other places.

Check out this proposal in swift evolution for more details.

What is an associatedtype?

An associated type gives a placeholder name to a type that is used as part of the protocol. The actual type to use for that associated type is not specified until the protocol is adopted. Associated types are specified with the associatedtype keyword.

Lets see an example.

protocol AutoMobileServiceCenter {
    associatedtype VehicleType
    func wash(vehicle: VehicleType)
    func changeEngineOil(vehicle: VehicleType)
    }

//A struct representing GeneralMotors cars. 
struct GeneralMotors {}
//A struct representing Volkswagen cars.
struct Volkswagen {}

In above example protocol, we defined an associatedtype VehicleType. It means that its just a placeholder type and the actual type will be provided by the conforming entity.

Lets extend our example to see how can we provide an actual type by conforming to AutoMobileServiceCenter protocol.

struct GeneralMotorsServiceCenter: AutoMobileServiceCenter {
    typealias VehicleType = GeneralMotors
    func wash(vehicle: VehicleType) {
        
    }
    func changeEngineOil(vehicle: VehicleType) {
    
    }
}

In above snippet, we created a GeneralMotorsServiceCenter, which conforms to AutoMobileServiceCenter. It has fulfilled the requirement of providing an actual type to assocaitedtype by declaring a typealias as GeneralMotors.

The actual type can also be inferred from other functions of the protocol like below example.

struct VolkswagenServiceCenter: AutoMobileServiceCenter {
    func wash(vehicle: Volkswagen) {
        
    }
    func changeEngineOil(vehicle: Volkswagen) {
    
    }
}

In above example, the actual type of VehicleType can be inferred as Volkswagen from the parameters of wash and changeEngineOil functions.

Note: If we return different types in both the functions, then complier will throw an error.

We can also tell an associatedtype to conform to a different protocol.

Like in above example, we wouldn’t want someone to pass a Tree for servicing.

Lets create another protocol to restrict users to pass only vehicles.

protocol Vehicle {}

protocol AutoMobileServiceCenter {
    associatedtype VehicleType: Vehicle
    func wash(vehicle: VehicleType)
    func changeEngineOil(vehicle: VehicleType)
}

This way, we are making sure that all service center should pass only vehciles for servicing. Compiler will complain if the actual type does not confrom to Vehicle.

We need to modify our structs as below.

struct GeneralMotors: Vehicle {}
struct Volkswagen: Vehicle {}

This way we can restrict the type of an associatedtype.

Caveat: Having a protocol constraint in associatedtype has one caveat. We can not fulfill associatedtype requirement with a some protocol when associatedtype has a protocol constraint.

Consider below example, which looks fine but does not compile.

protocol Vehicle {}

protocol AutoMobileServiceCenter {
    associatedtype VehicleType: Vehicle
    func wash(vehicle: VehicleType)
    func changeEngineOil(vehicle: VehicleType)
}

protocol Car: Vehicle {}

protocol Bike: Vehicle {}

struct GeneralMotors: Car {}

struct Volkswagen: Car {}

struct GenericCarSericeCenter: AutoMobileServiceCenter {
    func wash(vehicle: Car)
    func changeEngineOil(vehicle: Car)
}

Even though Car protocol conforms to Vehicle, the compilers throws an error

Inferred type 'Car' (by matching requirement 'wash') is invalid: does not conform to 'Vehicle'

If we remove Vehicle constraint from associatetype in AutoMobileServiceCenter, then using Car as actual type compiles with no issues.

I have also filed a radar for this issue.

Lastly, we can also use generics to fulfill associatedtype requirement. We will now see how can we ultimately make a generic car serivce center. :)

protocol Vehicle {}

protocol AutoMobileServiceCenter {
    associatedtype VehicleType: Vehicle
    func wash(vehicle: VehicleType)
    func changeEngineOil(vehicle: VehicleType)
}

protocol Car: Vehicle {}

protocol Bike: Vehicle {}

struct GeneralMotors: Car {}

struct Volkswagen: Car {}

struct GenericCarSericeCenter<T>: AutoMobileServiceCenter where T: Car {
    func wash(vehicle: T)
    func changeEngineOil(vehicle: T)
}

Hope we now have good understanding of associatedtype. Many people, including myself, find it bit confusing but its a very powerful technique in protocol oreinted design.

In next blog, we will try to build UITableView’s generic datasource which can be re-used for different dataset using the concept of associatedtype.

Happy Swifting.🃏

Written on December 10, 2016