Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Protocol Type as parameter in Swift

In objective-C we can (by importing the language's runtime header file) do the following:

//Pass a service (usually an object) and ANY protocol
- (void)registerService:(id)service forProtocol:(Protocol *)protocol
{
    //Grab the protocol's name (that's why we import runtime.h, it contains the protocol_getname mehod)
    NSString *protocolName = [NSString stringWithUTF8String:protocol_getName(protocol)];

    //If the object we passed does not conform to the protocol, inform and break
    if (![service conformsToProtocol:protocol])
    {
        NSLog(@"Service: %@ does not conform to protocol: %@", service, protocolName);
        return;
    }

    //Else add service in a collection (array, dictionary) for later use
    self.services[protocolName] = service;
}

I use this in obj-C as a "poor man's IOC container", a simple registry used for injecting dependencies.

//The interested party uses this method to obtain the dependency it needs by asking for the object that is registered as responsible for conforming to the Protocol parameter
- (id)serviceForProtocol:(Protocol *)protocol
{
    id result;

    NSString *protocolName = [NSString stringWithUTF8String:protocol_getName(protocol)];

    //Look for the service that conforms to the protocol in the registry dictionary,
    result = self.services[protocolName];

    //if there is no object meeting the criteria, inform/alert
    if (result == nil)
    {
        NSLog(@"No class registered for protocol: %@", protocolName);
    }

    //and return the result
    return result;
}

Trying to replicate this behaviour in Swift, I found out that we don't have access to the language's equivalent "runtime" API like we do in obj-C (yet), and understandably so since swift is a work in progress and giving people this kind of access is undoubtedly risky.

But this also means that we can't use Protocol in the same way anymore i.e. in the sense of ANY protocol.

The first possible workaround that comes to mind is a mix of generics, Any and where, but that feels too much for something that used to be simple.

So, my question is: What are some proposed solutions for passing around Protocol (as in ANY Protocol) in Swift?

EDIT: I had some success using the Metatype Type introduced in Swift which makes sense in terms of language design but also does not (yet) provide the ability to provide a "String" representation of a Metatype that could be used as a key in a dictionary.

This is of course a feature that could be added as the language matures.

like image 832
joakim Avatar asked Aug 13 '14 15:08

joakim


People also ask

Is protocol a type in Swift?

Protocols as TypesProtocol is a type. You can use it in many places like: As a parameter type or return type in a function, method, or initializer. As the type of a constant, variable, or property.

What is type parameter in Swift?

A type parameter is simply the name of a placeholder type (for example, T , U , V , Key , Value , and so on). You have access to the type parameters (and any of their associated types) in the rest of the type, function, or initializer declaration, including in the signature of the function or initializer.

How do you declare a protocol in Swift?

Custom types state that they adopt a particular protocol by placing the protocol's name after the type's name, separated by a colon, as part of their definition. Multiple protocols can be listed, and are separated by commas: struct SomeStructure: FirstProtocol, AnotherProtocol { // structure definition goes here.

How many protocols can a Swift class adopt?

Since classes, structures and, enums can conform to more than one protocol, they can take the default implementation of multiple protocols.


1 Answers

What have you tried?

Does something like this not work:

import Foundation

func registerService(service: NSObjectProtocol, forProtocol prot: Protocol) {
  let protocolName = NSStringFromProtocol(prot)

  if (!service.conformsToProtocol(prot)) {
    println("Service: \(service) does not conform to protocol: \(protocolName)")
    return
  }

  //...
}
like image 74
newacct Avatar answered Sep 17 '22 15:09

newacct