Class Only Protocols In Swift 4

The delegation pattern is common in many of Apple’s Cocoa API’s. When using it with Swift you create a class-only protocol that the delegate adopts. What I had not noticed was a subtle change in the way you declare a class-only protocol in Swift 4.

Class-Only Protocols

If you want a recap on using the delegate pattern see Quick Guide to Swift Delegates. Since Swift 4 the preferred way to declare a class-only protocol is to use AnyObject rather than class. So instead of writing this:

protocol DetailViewControllerDelegate: class {
  func didFinishTask(sender: DetailViewController)
}

You write this in Swift 4:

protocol DetailViewControllerDelegate: AnyObject {
  func didFinishTask(sender: DetailViewController)
}

The effect is the same. The delegate that adopts the protocol must be a class.

class MasterViewController: UIViewController {
  weak var delegate: DetailViewControllerDelegate?
}

extension MasterViewController: DetailViewControllerDelegate {
  func didFinishTask(sender: DetailViewController) { ... }
}

The compiler catches attempts to use a value type like a struct as the delegate:

// Error
struct MyDelegate: DetailViewControllerDelegate { ... }

Non-class type ‘MyDelegate’ cannot conform to class protocol ‘DetailViewControllerDelegate’

What Changed?

The Swift Programming Language guide has this say on Class-Only Protocols:

You can limit protocol adoption to class types (and not structures or enumerations) by adding the AnyObject protocol to a protocol’s inheritance list.

My thanks to @mmendoza27 for pointing this out to me. The Swift Evolution proposal SE-0156 - Class and Subytpe existentials covers the change:

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes.

Prior to Swift 4 there were some subtle differences between using class and AnyObject but that is no longer the case. I don’t know if AnyObject makes the intent any clearer than class but the result is the same:

The protocol can only be adopted by a class (reference semantics). It cannot be adopted by a struct or enum (value semantics).

What Should I Do

Get into the habit of using AnyObject when creating class-only protocols. Using class is not yet deprecated but I assume it will be in a future version of Swift.

Further Reading