ink ink - 2 months ago 14
Swift Question

Casting to a specified generic function parameter

Let's say I have a

Commander
object that executes commands. The return type is not always the same and changes according to the command.

I'd like to be able to use a function that forwards a command to the Commander, tests if the result is of a certain type (passed as a parameter), before calling a success closure if the cast succeeded, and a failure closure otherwise.

I've tried using generic parameters with something like this:

func postCommand<T>(command: String, expectedResponseType: T, success: T -> Void, failure: (NSError?) -> Void) {
Commander.execute(command, completion: { (response: AnyObject?) in
guard let content = response as? T else {
failure(nil)
return
}
success(content)
})
}


Calling it that way

self.postCommand("command", expectedResponseType: [String: AnyObject], success: { (content: [String: AnyObject]) in
print("Success")
}) { (error: NSError?) in
print("Failure")
}


But I get an error from the compiler:

Cannot convert value of type '([String : AnyObject]) -> Void' to expected argument type '_ -> Void'


If I try to do the cast like this:

guard let content = response as? expectedResponseType


the compiler complains that
expectedResponseType
is not a type.

I can't figure out how to do that. Is it even possible?

Answer

The problem is not with the casting, but with the type of the expectedResponseType: parameter.

If you wish to pass a type to a function, you need to use the metatype type as the argument type. In this case, the expectedResponseType: parameter of your function should be of type T.Type – allowing you to pass in a type to define T.

func postCommand<T>(_ command: String, expectedResponseType: T.Type, success: (T) -> Void, failure: (NSError?) -> Void) {
    // ...
}

You'll also need to use the postfix .self in order to refer to the actual type of whatever you pass into the expectedResponseType: parameter:

self.postCommand("command", expectedResponseType: [String: AnyObject].self, success: { content in
    print("Success")
}) { error in
    print("Failure")
}

Although you should note that the type of T can be inferred directly from the success closure you pass to the function:

func postCommand<T>(_ command: String, success: (T) -> Void, failure: (NSError?) -> Void) {
    // ...
}

self.postCommand("command", success: { (content: [String: AnyObject]) in
    print("Success")
}) { error in
    print("Failure")
}