100

I want this protocol:

protocol AddsMoreCommands {
     /* ... */
}

only to be adopted by classes that inherit from the class UIViewController. This page tells me I can specify that it is only adopted by a class (as opposed to a struct) by writing

protocol AddsMoreCommands: class {
}

but I cannot see how to require that it is only adopted by a particular class. That page later talks about adding where clauses to protocol extensions to check conformance but I cannot see how to adapt that either.

extension AddsMoreCommands where /* what */ {
}

Is there a way to do this? Thanks!

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
emrys57
  • 6,679
  • 3
  • 39
  • 49

4 Answers4

122
protocol AddsMoreCommands: class {
    // Code
}

extension AddsMoreCommands where Self: UIViewController {
    // Code
}
ntoonio
  • 3,024
  • 4
  • 24
  • 28
Witterquick
  • 6,048
  • 3
  • 26
  • 50
  • 4
    I so nearly had it... I wrote `self` instead of `Self` :-( Thank you very much, that works fine! – emrys57 Feb 01 '16 at 16:03
  • For me, this causes some syntactic strangeness when I use this in conjunction with casting. – Chris Prince Aug 03 '17 at 18:29
  • 3
    This won't work if you need to include a property in the protocol, as extensions cannot contain stored properties. – shim Jan 12 '18 at 19:15
  • 1
    It can have stored propertied you only need to use this: objc_getAssociatedObject(self, &KeyName) as? PropertyType – Michał Ziobro Apr 23 '18 at 10:25
  • This also requires casting when a let/var declaration has type `AddsMoreCommands` but a method you pass it to expects a `UIViewController` – GoatInTheMachine Jun 28 '18 at 11:30
  • 1
    The docs changed to using `AnyObject` instead of `class` -> https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID281. But both versions are behaving the same for now without deprecation warnings. – heyfrank Jul 12 '18 at 13:43
  • Is it just a fact of Swift syntax that you cannot add overrides in to these? E.g., `override var prefersStatusBarHidden`? – Chris Prince Jul 24 '18 at 22:04
92

This can also be achieved without an extension:

protocol AddsMoreCommands: UIViewController {
    // code
}

Which is exactly the same as:

protocol AddsMoreCommands where Self: UIViewController {
   // code
}

I usually use the first option, IIRC I read a while ago on the Swift docs that that is the recomended way when the constraint is Self, if is other constraint such as associate types is when where can be used.

Notice that since UIViewController is a class too, this protocol can be implemented for weak properties such as delegates.

EDITED 2021/01/05: Previous posted solution had a warning, I have removed it and use this one which does not produce any warning.

rgkobashi
  • 2,551
  • 19
  • 25
  • 6
    How coincidental is it that I clicked on a two year old question and find a perfect solution posted one hour ago – Oscar Apeland Oct 26 '17 at 09:30
  • Xcode 9.1 is now giving me a warning about this saying: Redundant layout constraint 'Self' : 'AnyObject'. Layout constraint constraint 'Self' : 'AnyObject' implied here. Changing my code to the format of the accepted answer seems to be more gooder. – Zig Nov 01 '17 at 19:34
  • 3
    As of Xcode 9.1 class-only protocols now use `AnyObject` instead of `class`. `protocol AddsMoreCommands: AnyObject where Self: UIViewController { // code }` – dodgio Nov 07 '17 at 00:47
  • @dodgio still getting same warning using `AnyObject` – rgkobashi Nov 07 '17 at 00:59
  • Yes, the warning still shows. Here's the [Swift bug](https://bugs.swift.org/browse/SR-6265) for it. The compiler is complaining (cryptically) that the code is specifying "AnyObject" twice on the protocol. The bug lists why that's a bad warning. – dodgio Nov 07 '17 at 01:15
  • I'm using `protocol Foo where Self: Bar`, any reason why you aren't using this pattern? – Laffen Nov 13 '17 at 11:42
  • Protocols can be adopted by struct, enum and classes. But for example, if your protocol is a DataSource or Delegate, it needs to be _weak_ to avoid a retain circle, and in order to make the property _weak_ you need to use `: class`. When using `: class` your protocol can be implemented JUST on classes. In this case I used `: class` since the accepted answer is using it. – rgkobashi Nov 13 '17 at 13:41
  • @rgkobashi there's no need to use `class` since you're already restricting conformance to `UIViewController` sub_classes_. `protocol RatingControllerDelegate where Self: UIViewController` compiles just fine and produces no warnings in Xcode9.1+. – Dávid Pásztor May 31 '18 at 09:51
  • 1
    @DávidPásztor you are right, however if you want to use it on a structural pattern such as delegation, to be able to make the property weak it is necessary to add ´class´ explicitly :) – rgkobashi May 31 '18 at 13:16
  • Is this saying that anything that AddsMoreCommands MUST be a UIViewController, or that all the requirements of that protocol only have to be implemented by UIViewControllers? i.e. if you have var addsMore: AddsMoreCommands, can the compiler assume that it is dealing with a UIViewController that implements that protocol. My experience says the compiler can't do that. – Giles Aug 03 '18 at 10:14
  • @Giles I am not sure what you mean with the first question but this is more like the second one. All the requirements of this protocol MUST be implemented by UIViewControllers. If you have addsMore: AddsMoreCommands the compiler cannot assume that it is dealing with a UIViewController, that is one of the good things about POP, you remove those kind of restrictions. However the compiler will know when you are trying to implement AddsMoreCommands on a non-UIViewController. – rgkobashi Aug 03 '18 at 13:56
51

Because of an issue in the previous answer I ended up with this declaration:

protocol AddsMoreCommands where Self : UIViewController { 
    // protocol stuff here  
}

no warnings in Xcode 9.1

shim
  • 9,289
  • 12
  • 69
  • 108
Massmaker
  • 698
  • 5
  • 7
  • 5
    Correct me if I'm wrong, but the issue with this over the solution above (which is generating a warning in Xcode 9.1 and upwards), is that you can't declare the delegate as weak? – Kyle Goslan Apr 03 '18 at 10:08
  • Also, when I use this solution with Swift 4.1, I need to cast properties of the `AddsMoreCommands` to `UIViewController` which I wanted to avoid... – heyfrank Jul 12 '18 at 13:48
  • 9
    To avoid the type cast, you could do this: `typealias AddsMoreCommandsViewController = UIViewController & AddsMoreCommands` – plu Jul 21 '18 at 20:00
39

Now in Swift 5 you can achieve this by:

protocol AddsMoreCommands: UIViewController {
     /* ... */
}

Quite handy.

Wojciech Kulik
  • 7,823
  • 6
  • 41
  • 67
  • What is the difference between these two? extension AddsMoreCommands where Self: UIViewController { // Code } AND protocol AddsMoreCommands: UIViewController { /* ... */ } Thank you in advanced. – Rahul Patel Dec 17 '20 at 16:29
  • I think you meant `protocol` instead of `extension`, if that is the case, someone already [answered it](https://stackoverflow.com/a/57001123/8483739) – rgkobashi Feb 16 '21 at 06:00