Passing protocol type in Swift parameter

In objective-C, we can (by importing the language 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 name (that 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 person IOC container", a simple registry used for dependency injection.

 //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 behavior in Swift, I found that we do not have access to the language equivalent of the runtime API, as in obj-C (for now), and it’s understandable, since swift is an incomplete work and providing people with such access, undoubtedly fraught with risk.

But it also means that we cannot use the protocol in the same way anymore, that is, in the sense of ANY protocol.

The first possible workaround that comes to mind is a combination of generics , Any and where , but this is too much for something that was simple.

So my question is: what are some of the suggested solutions for transferring around the Protocol (as in ANY protocol ) in Swift?

EDIT:. I had some success using the Metatype type introduced in Swift, which makes sense from the point of view of language design, but also does not yet provide the ability to represent a "String" metatype that can be used as a key in a dictionary.

This, of course, is a function that can be added as the language matures.

+5
source share
2 answers

What have you tried?

Something like this does 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 } //... } 
+1
source

I tried for a long time in different ways (pointers, Protocol ), and the only solution without @objc and pure Swift that I found was closing. Then you need to use closure to use the protocol and return a value

 protocol Proto { } protocol Proto2 { } class Foo: Proto { } class Bar: Proto, Proto2 { } class Baz: Proto2 { } class Qux { } func printConforms(classList: [AnyClass], protoCond: (AnyClass) -> Any?) { for i in classList { print(i, terminator: " -> ") if protoCond(i) != nil { print("is subscriber") } else { print("NOT IS subscriber") } } } let myClasses: [AnyClass] = [Foo.self, Bar.self, Baz.self, Qux.self] printConforms(classList: myClasses, protoCond: { $0 as? Proto.Type }) 

A more complete example: https://gist.github.com/brunomacabeusbr/eea343bb9119b96eed3393e41dcda0c9

Edit

Another best solution is to use generics, for example:

 protocol Proto { } class Foo: Proto { } class Bar: Proto { } class Baz { } func filter<T>(classes: [AnyClass], byConformanceTo: T.Type) -> [AnyClass] { return classes.filter { $0 is T } } filter(classes: [Foo.self, Bar.self, Baz.self], byConformanceTo: Proto.Type.self) // return [Foo.self, Bar.self] 
0
source

Source: https://habr.com/ru/post/1200238/


All Articles