Mark Jarecki
Mark Jarecki

Reputation: 115

How do you work with multiple type parameters - Swift 2?

I am trying to write a generic mediator base class (Mediator) that registers peer classes to specific protocols (on peer class extensions), that are defined on instantiation.

Mediator and Peer subclasses are not supposed to know about each other. Their relationship is defined when all is wired up. This is to make each component modular.

If I use only ONE type parameter such as:

class Mediator<T, U> {
    private var peers = [T]()
    func registerPeer(peer: T) {
        self.peers.append(peer)
    }
}

Then one Peer registers correctly.

I want both T or U able to be appended to the peers[].

I am looking for a solution that only modifies Mediator's peers[] and registerPeer() to allow a mix or T or U.

// Mediator
class Mediator<T, U> {

// I want this to be an Array that can be T OR U
private var peers = Array<T|U>()

// I want peer: to be either T OR U
func registerPeer(peer: <T|U>) {

    self.peers.append(peer)
    }
}

class RootModuleMediator<T, U> : Mediator<T, U> {}

protocol RootModuleMediatorsIOInterface { func someFunction() }
extension RootModuleMediator : RootModuleMediatorsIOInterface { func  someFunction() { print("RootModuleMediator someFunction() printed")}}
protocol RootModuleMediatorsViewInterface { func aFunction() }
extension RootModuleMediator : RootModuleMediatorsViewInterface { func aFunction() { print("RootModuleMediator aFunction() printed")}}

// Peer
class Peer<T> {

    private var mediator : T?
    func registerMediator(mediator: T) { self.mediator = mediator }

}

// View Peer
class RootModuleView<T> : Peer<T> {}
protocol RootModuleViewsMediatorInterface { func someFunction() }
extension RootModuleView : RootModuleViewsMediatorInterface { func someFunction() { print("RootModuleView someFunction() printed") }}

// IO Peer
class RootModuleIO<T> : Peer<T> {}
protocol RootModuleIOsMediatorInterface { func someFunction() }
extension RootModuleIO : RootModuleIOsMediatorInterface { func someFunction() { print("RootModuleIO  someFunction() printed") }}

// Wiring components together
let rootModuleIO = RootModuleIO<RootModuleMediatorsIOInterface>()
let rootModuleView = RootModuleView<RootModuleMediatorsViewInterface>()

let rootModuleMediator = RootModuleMediator<RootModuleIOsMediatorInterface, RootModuleViewsMediatorInterface>()

rootModuleIO.registerMediator(rootModuleMediator)
rootModuleView.registerMediator(rootModuleMediator)

rootModuleMediator.registerPeer(rootModuleIO)
rootModuleMediator.registerPeer(rootModuleView)

// I want the following function to print "RootModuleIO  someFunction() printed"
rootModuleMediator.peers[0].someFunction()

Upvotes: 2

Views: 2078

Answers (3)

Mark Jarecki
Mark Jarecki

Reputation: 115

In the end, I could not get T or U type parameters to work with the array or function. Having instead opted for a solution that is more suited to my use case of loosely coupling components and making them communicate over defined interfaces.

// Mediator base
class Mediator<IOTypeParam, ViewTypeParam> {

    private var IO : IOTypeParam?
    private var view : ViewTypeParam?

    func registerIOPeer(peer: IOTypeParam) { self.IO = peer }
    func registerViewPeer(peer: ViewTypeParam) { self.view = peer }

}

// Mediator subclass
class RootModuleMediator<IOTypeParam, ViewTypeParam> : Mediator<IOTypeParam, ViewTypeParam> {}

protocol RootModuleMediatorsIOInterface { func someFunction() }
extension RootModuleMediator : RootModuleMediatorsIOInterface { func someFunction() { print("RootModuleMediator someFunction() printed")}}
protocol RootModuleMediatorsViewInterface { func aFunction() }
extension RootModuleMediator : RootModuleMediatorsViewInterface { func aFunction() { print("RootModuleMediator aFunction() printed")}}

// Peer base
class Peer<MediatorTypeParam> {

    private var mediator : MediatorTypeParam?
    func registerMediator(mediator: MediatorTypeParam) { self.mediator = mediator }

}

// View Peer
class RootModuleView<MediatorTypeParam> : Peer<MediatorTypeParam> {}
protocol RootModuleViewsMediatorInterface { func someFunction() }
extension RootModuleView : RootModuleViewsMediatorInterface { func someFunction() { print("RootModuleView someFunction() printed") }}

// IO Peer
class RootModuleIO<MediatorTypeParam> : Peer<MediatorTypeParam> {}
protocol RootModuleIOsMediatorInterface { func someFunction() }
extension RootModuleIO : RootModuleIOsMediatorInterface { func someFunction() { print("RootModuleIO  someFunction() printed") }}

// Instances
let rootModuleIO = RootModuleIO<RootModuleMediatorsIOInterface>()
let rootModuleView = RootModuleView<RootModuleMediatorsViewInterface>()
let rootModuleMediator = RootModuleMediator<RootModuleIOsMediatorInterface, RootModuleViewsMediatorInterface>()

// Interface registration
rootModuleIO.registerMediator(rootModuleMediator)
rootModuleView.registerMediator(rootModuleMediator)
rootModuleMediator.registerIOPeer(rootModuleIO)
rootModuleMediator.registerViewPeer(rootModuleView)

// Communication constrained to defined interfaces
rootModuleMediator.IO!.someFunction()
rootModuleMediator.view!.someFunction()
rootModuleIO.mediator!.someFunction()
rootModuleView.mediator!.aFunction()

Upvotes: 1

Mr Beardsley
Mr Beardsley

Reputation: 3863

It is possible to have generic types that conform to multiple protocols. It is done like this:

class Mediator<T where T: RootModuleMediatorsIOInterface, T: RootModuleIOsMediatorInterface> {

    private var peers = Array<T>()

    func registerPeer(peer: T) {

        self.peers.append(peer)
    }

    func exercisePeers() {
        for peer in peers {
            peer.someFunction()
            peer.someOtherFunction()
        }
    }
}

class RootModuleMediator : RootModuleMediatorsIOInterface, RootModuleIOsMediatorInterface {}


// ############ EXAMPLE PROTOCOL CONSTRAINED EXTENSIONS IMPLEMENTATION ############


protocol RootModuleMediatorsIOInterface { func someFunction() }

extension RootModuleMediatorsIOInterface { func someFunction() { print("Something")}}

protocol RootModuleIOsMediatorInterface { func someOtherFunction() }

extension RootModuleIOsMediatorInterface { func someOtherFunction() { print("Something else") }}

let root = RootModuleMediator()
let mediator = Mediator<RootModuleMediator>()

mediator.registerPeer(root)
mediator.exercisePeers()

I added an exercise function so you can see that you can call the functions implemented by the protocol extensions. I also simplified your protocol and extension definitions. The output is as you would expect:

Something
Something else

Upvotes: 1

0x416e746f6e
0x416e746f6e

Reputation: 10136

I do not see the rest of implementation to verify it will all work as expected, but did you mean something like this:

class Mediator<T, U> {
    private var peers = [(T, U)]()

    func registerPeer(peer: (T, U)) {
        self.peers.append(peer)
    }

    func registerPeer(left: T, _ right: U) {
        registerPeer((left, right))
    }

}

The missing step was to use tuples as the array elements.

Upvotes: 2

Related Questions