Cody Weaver Cody Weaver - 3 months ago 8
Swift Question

Swift Generic function call another Generic function

I am using Swift 2.2 on XCode 7.3.1 and trying to call a

Generic
function from another
Generic
function.

Code

class Thing1 {
let variable: SomeProtocol
init<A: SomeProtocol>(variable: A) {
self.variable = variable
self.add1(self.variable)
}

func add1<A: SomeProtocol>(stuff: A) {
let thing: Thing2 = Thing2()
thing.add2(stuff)
}

}

class Thing2 {
func add2<A: SomeProtocol>(stuff: A) {

}
}

protocol SomeProtocol { }


add1("a") // Cannot invoke 'add1' with an argument list of type '(String)'
add1(4) // Cannot invoke 'add1' with an argument list of type '(Int)'


I get the error.

'Cannot invoke add with an argument of list type '(Whatever type I used to call the function)''

Answer

The problem is that abstract types in Swift don't necessarily conform to themselves – therefore you cannot use a SomeProtocol typed thing as a concrete typed thing that conforms to SomeProtocol (which is what your add1 generic function expects as an argument).

The simplest solution in your case therefore is just to use the generic variable argument, rather than the variable property, as because it's a generic, it's typed as a concrete thing that conforms to SomeProtocol, which can therefore be passed into your add1 function:

init<A: SomeProtocol>(variable: A) {
    self.variable = variable
    add1(variable)
}

However in order to prevent these kind of issues later down the line, you may want to consider making your class generic, assuming that your variable property should be of constant type throughout the lifetime of a given Thing1 instance:

class Thing1<A:SomeProtocol> {

    let variable: A

    init(variable: A) {
        self.variable = variable
        add1(variable)
    }

    func add1(stuff: A) {
        let thing = Thing2()
        thing.add2(stuff)
    }
}

Or, you could refactor your code to use the abstract type SomeProtocol, which will allow you to work with any type that conforms to SomeProtocol (e.g allowing you to mix different Thing1 instances with different variable types in an array):

class Thing1 {

    let variable: SomeProtocol

    init(variable: SomeProtocol) {
        self.variable = variable
        add1(variable)
    }

    func add1(stuff: SomeProtocol) {
        let thing = Thing2()
        thing.add2(stuff)
    }
}

class Thing2 {
    func add2(stuff: SomeProtocol) {

    }
}

Although you should always be aware of the extra costs that come with using abstract types, see this great WWDC talk for more info.

Comments