Matej Ukmar
Matej Ukmar

Reputation: 2245

What is wrong with my template/generic Swift initializer/constructor?

I created a SlidingNavigationController where I wanted to have an initializer that takes three parameters. All three parameters should be UIViewControllers but they need to confirm to my SlidingIconProtocol. So I wrote code like this (simplified version):

struct SlidingItem {
    var bigIconView: UIView
    var smallIconView: UIView
}

protocol SlidingIconProtocol {
    var slidingItem: SlidingItem { get set }
}

class SlidingNavigationController: UIViewController {

    init<T:UIViewController where T:SlidingIconProtocol>(centralVC: T, leftVC: T, rightVC: T) {
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

class CentralVC: UIViewController, SlidingIconProtocol {
    var slidingItem = SlidingItem(bigIconView: UIView(), smallIconView: UIView())
}

class LeftVC: UIViewController, SlidingIconProtocol {
    var slidingItem = SlidingItem(bigIconView: UIView(), smallIconView: UIView())
}

class RightVC: UIViewController, SlidingIconProtocol {
    var slidingItem = SlidingItem(bigIconView: UIView(), smallIconView: UIView())
}


let myVC = SlidingNavigationController(centralVC: CentralVC(), leftVC: LeftVC(), rightVC: RightVC())

The problem is that Swift fails to compile on the last line of code with: "Cannot invoke initializer for type 'SlidingNavigationController' with an argument list of type '(centralVC: CentralVC, leftVC: LeftVC, rightVC: RightVC)'"

Not sure why this does not work, since even Swift/Xcode completion is giving me option to use this initializer. And all passed parameter confirm to SlidingIconProtocol.

Does anyone know what is wrong with the code and what is the right way in Swift to achieve the same (is it possible at all) ?

Upvotes: 0

Views: 608

Answers (1)

Midhun MP
Midhun MP

Reputation: 107201

You can't use template like that way. In your code:

init<T:UIViewController where T:SlidingIconProtocol>(centralVC: T, leftVC: T, rightVC: T)
{
    super.init(nibName: nil, bundle: nil)
}

T represents a class that is a subclass of UIViewController and implements SlidingIconProtocol. So when you call:

let myVC = SlidingNavigationController(centralVC: CentralVC(), leftVC: LeftVC(), rightVC: RightVC())

The T is assumed as CentralVC (first parameter), and the init method will be represented as:

init< CentralVC:UIViewController where CentralVC:SlidingIconProtocol>(centralVC: CentralVC, leftVC: CentralVC, rightVC: CentralVC)
{
    super.init(nibName: nil, bundle: nil)
}

But you are passing different class object as the second and third parameter. And it will throw error. In your class the following code is valid:

let myVC = SlidingNavigationController(centralVC: CentralVC(), leftVC: CentralVC(), rightVC: CentralVC())

Because all the passed arguments are object of same class (CentralVC). So fixing the issue, you need to implement the init method in the following way:

init<T1:UIViewController, T2:UIViewController, T3:UIViewController where T1:SlidingIconProtocol, T2:SlidingIconProtocol, T3:SlidingIconProtocol>(centralVC: T1, leftVC: T2, rightVC: T3)
{
    super.init(nibName: nil, bundle: nil)
}

Upvotes: 1

Related Questions