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.