Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)''
like image 251
Cody Weaver Avatar asked Oct 16 '25 22:10

Cody Weaver


1 Answers

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.

like image 89
Hamish Avatar answered Oct 19 '25 12:10

Hamish



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!