grago
grago

Reputation: 131

Implement delegate using generic protocol in Swift

I'm trying to create a delegate protocol that implements a function which passes an array of a generic type. I've tried several combinations but none of them seem to do the trick.

This is the most approximate thing i've reached to. This is the protocol:

     protocol APIControllerProtocol {

          typealias T

          func didReceiveAPIResults(results: [T])
     }

And this is the the delegator object:

    class APIController<U:APIControllerProtocol> {

         typealias ElementType = U
         var delegate: ElementType?

         init(delegate: ElementType){

             self.delegate = delegate

         }

         func getAPIResults(){

              // Perform some action before delegation
              // "results" is an Array of dictionaries got from NSJSONSerialization

              self.delegate?.didReceiveAPIResults(results.map{dict in Album(json:dict)})

         }

     }

However, the last line get this error: "Album is not convertible to U.T"

"Album" is the model object used to return the results.

What am i doing wrong?

EDIT:

Following Mike S advice, i've made the protocol method didReceiveAPIResults a generic function, and specified what T is in the delegate. However, when receiving and assigning the argument of type T to a property in the delegate, i get the error: "T is not identical to T"

class TestDelegate: APIControllerProtocol {
    typealias T = Album
    var albums:[T] = [T]()

    func didReceiveAPIResults<T>(results: [T]) {
        // ...

        self.albums = results //ERROR: "T is not identical to T"
    }
}

Upvotes: 3

Views: 3494

Answers (1)

Mike S
Mike S

Reputation: 42345

Your didReceiveAPIResults declaration in APIControllerProtocol needs to be a generic function so that the generic type T is passed along to it correctly.

protocol APIControllerProtocol {
    typealias T

    func didReceiveAPIResults<T>(results: [T])
}

Note: This means your delegate definition will need to define what T is:

class TestDelegate: APIControllerProtocol {
    typealias T = Album

    func didReceiveAPIResults<T>(results: [T]) {
        // ...
    }
}

Update: While the code above does get rid of the original error, it turns out that it acts more like a workaround and doesn't really address the root of the problem.

The real issue seems to be that the compiler is having trouble reconciling what U.T is with no ambiguity. That's actually easy enough to fix though, we just need to give it a more precise definition (note the where clause in the APIController definition):

protocol APIControllerProtocol {
    typealias T
    func didReceiveAPIResults(results: [T])
}

class APIController<U:APIControllerProtocol where U.T == Album> {
    typealias ElementType = U
    // ...
}

Note: I removed the <T> that I added to the function in the protocol previously; that's not needed anymore and will end up causing problems later.

With that, the TestDelegate class works as expected (you don't even need the typealias anymore):

class TestDelegate: APIControllerProtocol {
    var albums: [Album]? = nil

    func didReceiveAPIResults(results: [Album]) {
        albums = results
    }
}

Upvotes: 1

Related Questions