65

I'd like to return a UIViewController conforming to MyProtocol from a method, so I'm using the method signature:

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {

First thing I don't understand: if myMethod returns e.g. a MyViewController which has to following signature, I have to force cast it:

class MyViewController: UIViewController, MyProtocol

I cannot simply return MyViewController() but I need to cast it like this: return MyViewController() as! T - why is this necessary?

And the second thing: how can I use this method somewhere? I cannot simply say

let x = myMethod() as? UIViewController

as I get the error

Generic parameter 'T' could not be inferred

How can I achieve something like this? If I cast it to MyViewController it works, but I would like to avoid that of course.

EDIT: Example

class MyViewController : UIViewController, MyProtocol {
}

protocol MyProtocol {
}

func myMethod<T>() -> T where T : UIViewController, T : MyProtocol {
    return MyViewController() as! T // why is the cast necessary?
}

ok, I do get one part, but why is the cast to T necessary? MyViewController is a subclass of UIViewController and conforms to the protocol, so no cast should be necessary, right?

swalkner
  • 16,679
  • 31
  • 123
  • 210
  • What exactly are you trying to achieve here? Why are you using generics in the first place? – Alexander Aug 17 '16 at 14:23
  • I would like to work with a type that's a `UIViewController` and conforms to the specific protocol; I do have different classes which conform to this rules, therefore I do not want to use a specific type. – swalkner Aug 17 '16 at 14:28
  • don't you want to accept an answer? – mfaani Jan 21 '17 at 20:34

4 Answers4

45
func myMethod<T where T : UIViewController, T : MyProtocol>() -> T

This declaration says: There exists a function called myMethod, such that myMethod returns some specific T where T is a subtype of UIViewController and also MyProtocol. This does not say what type T actually is, and it does not say that there is only one such myMethod. There can be many if there are many type that are both subclasses of UIViewController and conform to MyProtocol. Every one of those types creates a new version of myMethod (really a new solution to the assertion myMethod makes, that such a function does exist).

This is not the same thing as:

func myMethod() -> UIViewController

That says: The function myMethod returns any subtype of UIViewController.

There is no way in Swift to express "any type that is a subclass of UIViewController and is a subtype of MyProtocol." You can only discuss a specific type that meets that criterial. Swift can't combine classes and protocols this way; it's just a current limitation of the language, not a deep design issue.

The specific versus any is the issue. There are many functions that satisfy your myMethod declaration. Every T you can plug in that conforms to the rules would be a candidate. So when you say myMethod(), the compiler doesn't know which specific T you mean.

(I was going to expand this answer to provide it in less type-theory, more "how do you do it in code" terms, but donnywals already has an excellent version of that.)

* To your edited question *

func myMethod<T>() -> T where T : UIViewController, T : MyProtocol {
    return MyViewController() as! T // why is the cast necessary?
}

T is a specific type decided by the caller. It is not "any type that conforms" it is "some specific, concrete type that conforms." Consider the case that you called:

let vc: SomeOtherViewController = myMethod()

In this case, T is SomeOtherViewController. MyViewController is not that type, so what you're doing with the as! cast is dangerous.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
18

In a method like this, returning T means you have to return T. If you return MyViewController, the return type should be MyViewController. T is a generic type that will take the form of whatever the Swift compiler can infer it to be.

So, with your method signature, a simple implementation of the protocol and method could look like this.

protocol MyProtocol {
    var name: String { get set }
}

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {
    var vc = T()
    vc.name = "Hello, world"
    return vc
}

So, considering your usage example:

let x = myMethod()

How would the compiler know what the concrete type of T is? There is nothing giving it a hint of MyViewController. The only thing we know is that whatever T is, it should be MyViewController or a subclass of it. And it should conform to MyProtocol. But this does not provide information about what the type of T should be.

The only place where the compiler can infer what we want T to be is through the return value. All the code between <> are constraints for what T is allowed to be. -> T is the only place where T is seen outside of the constraints. So if we can somehow tell the compiler what we want myMethod to return, we have given it enough information to infer T.

Your typecast works but I agree that it's not very pretty. A much prettier way for the compiler to infer T is this.

let vc: MyViewController = myMethod()

By specifying the type of vc, the compiler understands that we want myMethod to return a MyViewController. So now T's type can be inferred and if we return T, we actually return MyViewController.

donnywals
  • 7,241
  • 1
  • 19
  • 27
  • @donnywals how about on generic method parameters, not the return type? How do you infer something like that? – Axel Sep 06 '16 at 10:24
  • I guess this means you are limited in how you nest calls when returning generics. Like function A -> B -> C where B & C both return generics. In my specific case I'm calling an API and want to pass back a Decodable. The C call returned this, but I needed to layer in security so I created a B call as a wrapper which got the bearertoken and passed it off to C. Only way to do this was to return C directly from B. – lcj Oct 09 '22 at 12:05
4

As some pointed out in the comments, there is no apparent reason for myMethod to be generic. The argument for doing so is: (quoting from your comment)

I would like to work with a type that's a UIViewController and conforms to the specific protocol;

Lets call that type ViewControllerAndMyprotocol,

I do have different classes which conform to this rules, therefore I do not want to use a specific type

However myMethod signature already constrains the type ViewControllerAndMyprotocol i.e. caller is bound to receive a UIViewController and not any of the "different classes which conform to this rules".

The flexibility on what concrete types could be ViewControllerAndMyprotocol, including MyViewController is why there is type ambiguity in the statement let x = myMethod() requiring casting: let x = myMethod() as? UIViewController

You can avoid the casting by changing myMethod signature as such:

typealias ViewControllerAndMyprotocol = UIViewController & MyProtocol

func myMethod() -> ViewControllerAndMyprotocol {
   return MyViewController()
}

The statement let x = myMethod() will not require casting and will be of type ViewControllerAndMyprotocol which is also a UIViewController.

Lukas
  • 3,423
  • 2
  • 14
  • 26
1

Very Easy Way Your Function: you just need to add ? mark with the model object

func decodeDataToObject<T: Codable>(data : Data?)->T?{

}

Calling Method

let model: ModelDataClass? = APIs.decodeDataToObject(data: response.data!)

Example for understanding: you can see i have use ? with the model name after declaring the object

let model: ModelDataClass?
Shakeel Ahmed
  • 5,361
  • 1
  • 43
  • 34