horseshoe7
horseshoe7

Reputation: 2817

How to call a static method of a generic type conforming to a protocol in Swift

Say I have:

@objc public protocol InteractivelyNameable: Nameable {

    static func alertViewForNaming(_ existingObject: Nameable?,
                               context: NSManagedObjectContext,
                               completion:@escaping ((_ success: Bool, _ object: Nameable?, _ didCancel: Bool, _ error: Error?) -> Void)) -> UIAlertController?
}

And I have a generic view controller that manages various types (generic type is .fetchableObjectType... basically NSManagedObject.self.. well, a subclass of it). I need to check if a specific object type conforms to the protocol, and if so, invoke it.

something like:

    // valid swift code
    if self.dataSource.fetchableObjectType is InteractivelyNameable {

        // not valid swift code
        if let alert = (self.dataSource.fetchableObjectType as! InteractivelyNameable).alertViewForNaming(....) { // ... do stuff }
    }

Upvotes: 1

Views: 2699

Answers (2)

OhadM
OhadM

Reputation: 4803

The static method you wrote isn't generic but protocol as type parameter. Basically, when you use as a protocol type parameter and not the generic form you force the compiler to use the dynamic dispatcher, ergo, Objective-C.

What you need to do in order to use the statically type dispatcher (Swift):

static func alertViewForNaming<T : Nameable>(_ existingObject: T,
                                         context: NSManagedObjectContext,
                                        completion:@escaping ((_ success: Bool, _ object: T, _ didCancel: Bool, _ error: Error?) -> Void)) -> UIAlertController?

This is, a generic type constraint method and in this case, it's protocol type constraint AKA Nameable.

You invoke the static method as follows:

let test : ObjectThatConformsToNameableProtocol = InteractivelyNameable.alertViewForNaming.....

This way, the compiler can infer the type for the generic type method which in this case, ObjectThatConformsToNameableProtocol.

I didn't test the code but it's important to understand the difference between generics and protocol type parameter.

Upvotes: 0

horseshoe7
horseshoe7

Reputation: 2817

To cast a Type to a Protocol at a "Class Level", you can use the .Type property of the protocol itself.

if let type = self.dataSource.fetchableObjectType as? InteractivelyNameable.Type {
   if let alert = type.alertViewForNaming(nil, context: self.dataSource.managedObjectContext, completion: completion) {

       // this code finds itself inside a UIViewController subclass...
       self.present(alert, animated: true, completion: nil)
          return
   }
}

Summary in Generic Form:

    if let myConformingObject = someObject as? InteractivelyNameable {

        // invoke instance methods...
        myConformingObject.someInstanceMethodDefinedInProtocol()

        // invoke class methods
        type(of: myConformingObject).someClassMethodDefinedInProtocol()
    }

    // i.e. someTypeParameter = NSManagedObject.Type
    if let conformingType = someTypeParameter as? InteractivelyNameable.Type {
        conformingType.someClassMethodDefinedInProtocol()
    }

Upvotes: 4

Related Questions