8

I want to define a variable which of the type UIViewController that conforms to P1.

Question:

  • Should I modify the protocol or the v1 type ?
  • How should I do this ?

Code:

protocol P1 {

    func f1();
}


var v1 : P1 //But needs to be a UIViewController which conforms to `P1`
user1046037
  • 16,755
  • 12
  • 92
  • 138
  • 1
    Possible duplicate of [In Swift, how can I declare a variable of a specific type that conforms to one or more protocols?](http://stackoverflow.com/questions/26401778/in-swift-how-can-i-declare-a-variable-of-a-specific-type-that-conforms-to-one-o) – Wain Feb 17 '16 at 10:08

4 Answers4

21

In Swift 4 you can use the new & sign to make a variable that is conforming a protocol AND a class, the syntax is like this:

let vc: UIViewController & P1
Jeroen Bakker
  • 2,142
  • 19
  • 22
13

As of Swift 4:

Thanks to SE-0156:

The proposal keeps the existing & syntax but allows one of the elements to be either AnyObject or of class type. The equivalent to the above Objective-C types would look like this:

AnyObject & Protocol1 & Protocol2
Base & Protocol

As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

.. we can now declare:

var v1 : UIViewController & P1 // UIViewController which conforms to `P1`

Prior Swift 4:

There is no way to directly do that. You have to either declare v1 as UIViewController or as P1, and then use casting (as) in order to get to the other functionality.

Closest you can get what you want is with generics. For example:

func testGeneric<T: UIViewController where T: P1>(input: T) {
    input.f1()
}

For example, if your case is to have a delegate which is both controller by inheritance and a delegate by protocol implementation, then you can go along these lines:

protocol Delegate: class { 
    func doDelegate()
}

class Controller { 
    func doController() {
        print("Controller")
    }
}

class ConcreteController: Controller, Delegate {
    func doDelegate() {
        print("Delegate")
    }
}

class View {
    private weak var controller: Controller? = nil
    private weak var delegate: Delegate? = nil
    
    func setDelegateController<T: Controller where T: Delegate>(delegateController: T?) {
        controller = delegateController
        delegate = delegateController
    }
    
    func test() {
        controller?.doController()
        delegate?.doDelegate()
    }
}

And to test this out:

let view = View()
let controller = ConcreteController()

view.setDelegateController(controller)

view.test()  // This will print:
             // Controller
             // Delegate
Community
  • 1
  • 1
0x416e746f6e
  • 9,872
  • 5
  • 40
  • 68
1

You can create another empty protocol, constrain UIViewController to that protocol, and then use the protocol<> directive to combine the two protocols into a single type:

protocol UIViewControllerType {} // empty
protocol P1 { ... }

// Make sure only UIViewController adopts UIViewControllerType
extension UIViewController: UIViewControllerType {}

extension UITableViewController: P1 {}

// This works:
var v1: protocol<UIViewControllerType, P1> = UITableViewController()

// This doesn't:
var v2: protocol<UIViewControllerType, P1> = UIViewController()
Ole Begemann
  • 135,006
  • 31
  • 278
  • 256
  • There is a problem with this approach. Now I wouldn't be able to invoke `UIViewController` methods using `v1` (without casting). I wanted this for delegation mainly. – user1046037 Feb 17 '16 at 10:48
  • 1
    That's true. You could add the methods from `UIViewController` that you want to use to the `UIViewControllerType` protocol. But I agree it's not pretty. – Ole Begemann Feb 17 '16 at 10:55
  • That's a another possibility. True. – user1046037 Feb 17 '16 at 11:03
0

What about this way:

protocol P1 {
    func f1()
    func getVC() -> UIViewController
}

class MyVC : UIViewController, P1 {

    func f1() {
        // do stuff
    }

    func getVC() -> UIViewController {
        return self
    }
}

var v1 : P1 = MyVC()

v1.getVC() // do UIViewController related things
MuHAOS
  • 1,108
  • 9
  • 8
  • Swift 4 solution which is marked as answered is the most elegant. Once swift 4 is released, things would be easier. – user1046037 Jul 13 '17 at 12:28