Kubba
Kubba

Reputation: 3438

Returning Generic.Type for later use with class methods

Is it possible to return a type of generic that conforms to protocol for later use with class functions using Swift 1.2? Take a look:

protocol SomeProtocol
{
    static func start(kind: Kind)
}

class A: SomeProtocol
{
    class func start(kind: Kind)
    {
        print("A started")
    }
}

class B: SomeProtocol
{
    class func start(kind: Kind)
    {
        print("B started")
    }
}


enum Kind {
    case Akind
    case Bkind

    private func classKind<T: SomeProtocol>() -> T.Type
    {
        switch self {
            case .Akind: return A.self
            case .Bkind: return B.self
        }
    }

    func doSomething() {
        self.classKind().start(self)
    }
} 

I tried various methods but every of them ended with some errors. Currently I got 'A' is not a subtype of 'T' in classKind method (same for 'B') and cannot invoke 'start' with an argument list of type '(Kind)' in doSomething. I'm sure I'm pretty close but can't solve it...

Upvotes: 1

Views: 133

Answers (1)

ABakerSmith
ABakerSmith

Reputation: 22979

If you're using Swift 2, to achieve what you want you only need to change:

private func classKind<T: SomeProtocol>() -> T.Type { ... }

to

private func classKind() -> SomeProtocol.Type { ... }

Now back to the not-working code to see where the errors were coming from. You don't need to make the changes I'm now detailing, this is just to explain the errors.

First examine your doSomething method:

func doSomething() {
    self.classKind().start(self) 
    // Error: Argument for generic parameter 'T' could not be inferred.
    // 
    // (I'm using Xcode 7 b6, which may explain the differing error messages)
}

For the type returned by classKind to be inferred, you'd have to do:

let type: A.Type = self.classKind() // Or you could use `B.Type`.
type.start(self) 

Which obviously defeats the point of your goal, since you have to specify the type you want.


Secondly, the errors in classKind:

private func classKind<T: SomeProtocol>() -> T.Type
{
    switch self {
    case .Akind: return A.self 
    // Cannot convert return expression of type 'A.Type' to return type 'T.Type'. 

    case .Bkind: return B.self
    // Cannot convert return expression of type 'B.Type' to return type 'T.Type'.
    }
}

To see why this doesn't work consider the following example, in which I have another type that conforms to SomeProtocol:

struct C: SomeProtocol { ... }

Then in doSomething:

func doSomething() {
    let type: C.Type = self.classKind()
    type.start(self)
}

The errors you're getting can now be read as: Cannot convert return expression of type 'A.Type'/'B.Type' to return type 'C.Type'.

Upvotes: 1

Related Questions