Why Use Class Only Protocols in Swift

Why do Swift Class-Only Protocols need AnyObject Inheritance?

When you write a protocol, you define an interface for the types which adopt, or conform to the protocol:

// Every Greeter has a greeting message
// and a way for it to present that message.
protocol Greeter {
var greeting: String { get }
func greet()
}

extension Greeter {
// Default implementation for the `greet` method.
func greet() {
print(self.greeting)
}
}

This gives consistent behavior to every type which conforms and lets you treat them all in the same way regardless of what they are. For instance, a type can store a value bound to a protocol type:

struct Store {
// The name of the store.
var name: String

// The address of the store.
var address: String

// An entity that greets customers when they walk in.
var greeter: Greeter
}

In this case, Store.greeter can be set to any value or object which conforms to the Greeter protocol:

struct Bell: Greeter {
let greeting = "Ding!"
}

class Person: Greeter {
let greeting: String

init(name: String) {
greeting = "Welcome! My name is \(name)."
}
}

var store = Store(name: "My Shop", address: "123 Main St.", greeter: Bell())
store.greeter.greet() // => "Ding!"

store.greeter = Person(name: "Itai")
store.greeter.greet() // => "Welcome! My name is Itai."

By default, a protocol can be adopted by both value types and object types, i.e., both structs and classes. This is a good default because in many cases, there's no reason to restrict who can conform to a protocol.

However, there is one case where this matters. By default, Store owns its greeter property, i.e., when you assign an object to store.greeter, it is retained. This is normally fine, except when you have a potentially circular relationship (for example, if Person has a reference to the Store they work at).

Normally, to break a potential circular chain like this up, you would make the variable weak:

struct Store {
// ...
weak var greeter: Greeter?
// ^ error: 'weak' must not be applied to non-class-bound 'Greeter'; consider adding a protocol conformance that has a class bound
}

If you try this, though, you'll get the above error. This is because weak only makes sense for objects, not for structs (structs can only be owned because they are value types — every owner just makes a copy of a struct, so there is no one central value to retain or release). In this case, to ensure a weak reference, you must ensure that Greeter an only be an object type.

The way to do this is to constrain the protocol to only allow classes to conform by using AnyObject as a constraint:

protocol Greeter: AnyObject {
// ...
}

Now, Bell can't conform to Greeter any more (error: non-class type 'Bell' cannot conform to class protocol 'Greeter') but you are allowed to have weak var greeter: Greeter? as a variable.


So, when should you restrict your protocol to be "class-bound" (using AnyObject)? Well, only when you really have to. The one benefit for class-bound protocols is to allow weak references to them, and the main use-case for that is with delegate references to prevent circular retains.

For instance, UITableView has a weak delegate property because the delegate object is usually the view controller which owns the UITableView object itself. If the view controller retains the table view, and the table view retains the controller, neither can be released automatically.

So, in the general case, you don't need to worry about the AnyObject constraint unless you really do only want classes to conform to the protocol.

Why use class only protocols in Swift?

One use case:

  • You have a "delegate" protocol and someone wants to have a weak property of that protocol type. weak can only be used on reference types; therefore, the protocol must be class-only.

Class-Only Protocols in Swift

Swift 4 allows you to combine types, so you can have your protocol and then create, for example, a type alias to combine it with a specific class requirement.

For (a contrived) example:

typealias PresentableVC = UIViewController & Presentable

For the presented code:

The problem is that you're trying to limit to specific classes and Swift can't do that (at the moment anyway). You can only limit to classes and inherit from other protocols. Your syntax is for protocol inheritance but you're trying to use it as a class limitation.

Note that the purpose of class protocols is:

Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics.

When to use `protocol` and `protocol: class` in Swift?

Swift 4 version

AnyObject added to a protocol definition like this

protocol FilterViewControllerDelegate: AnyObject  {
func didSearch(parameters:[String: String]?)
}

means that only a class will be able to conform to that protocol.

So given this

protocol FilterViewControllerDelegate: AnyObject  {
func didSearch(parameters:[String: String]?)
}

You will be able to write this

class Foo: FilterViewControllerDelegate {
func didSearch(parameters:[String: String]?) { }
}

but NOT this

struct Foo: FilterViewControllerDelegate {
func didSearch(parameters:[String: String]?) { }
}

Swift 3 version

:class added to a protocol definition like this

protocol FilterViewControllerDelegate: class  {
func didSearch(Parameters:[String: String]?)
}

means that only a class will be able to conform to that protocol.

So given this

protocol FilterViewControllerDelegate: class  {
func didSearch(Parameters:[String: String]?)
}

You will be able to write this

class Foo: FilterViewControllerDelegate {
func didSearch(Parameters:[String: String]?) { }
}

but NOT this

struct Foo: FilterViewControllerDelegate {
func didSearch(Parameters:[String: String]?) { }
}

Why protocol is better than class in swift?

Lets take a downloading example.

You have a Base class FileDownloadModel, and have 3 subclasses AudioFileDownloadModel, VideoFileDownloadModel, and ImageDownloadModel.

You have a DownloadManager that takes a FileDownloadModel input and uses this model's urlToDownload property to download the file.

Later down the line you are told that there is one more model coming but it's of type UserDownloadModel which is a subclass of User, and not FileDownloadModel.

See now it becomes difficult to handle this scenario where you will have to change a lot of code to incorporate downloading methods.

How protocol oriented programming will help you here:

  1. Create a protocol named DownloadingFileProtocol and add
    methods and properties that you need for downloading a file. eg. urlToDownload,
    pathToSave, extension etc.
  2. Implement the same protocol in FileDownloadModel and
    UserDownloadModel. The benefit here is that you don't have to change a
    lot of code in UserDownloadModel. You will just implement the
    methods from the DownloadingFileProtocol.
  3. If a new entity comes down the line again, you will not
    change any code. Rather, you'll just implement the protocol methods.
  4. And now your DownloadManager can take a
    DownloadingFileProtocol as input instead of a specific model. As well, you can now make any model "downloadable" by having it adopt this protocol.

Why Swift protocol conforming values are treated as Value types by default?

Your protocol SampleProtocol could be adopted by a class or a struct. Swift is using the behavior of the value type which is the more restrictive type until you tell it that the protocol will only be used by a class reference type.

Add conformance to AnyObject to your protocol to get reference type behavior:

protocol SampleProtocol: AnyObject {
var message: String? { get set }
}

See The Swift 5.1 Programming Guide - Class-Only Protocols for more details.

The guide notes:

Use a class-only protocol when the behavior defined by that protocol’s
requirements assumes or requires that a conforming type has reference
semantics rather than value semantics.


Historical note: Using class keyword:

Prior to Swift 4.0, this was written using the class keyword:

protocol SampleProtocol: class {
var message: String? { get set }
}

This still works for the time being, but it is currently just a type alias for AnyObject and will likely be removed in a later version of Swift.

what is the difference protocol's `: AnyObject` between `none`?

AnyObject requires that conforming types be classes, rather than structs or enums. See Class-Only Protocols for details.

Classes provide reference semantics, rather than value semantics, which you may require for your protocol. For example, if you want to observe an object, that only makes sense for a reference type (you can't observe changes to a value type, since value types are copied when modified). For more details on that see Structures and Classes, and particularly the sections explaining value and reference types.

There is no value type equivalent of AnyObject. You can't require value semantics for a protocol. This can lead to some headaches. As an example of the difference, consider the following:

protocol Incrementing {
var value: Int { get set }
}

extension Incrementing {
func incremented() -> Self {
var new = self // Value types copy, reference types reference
new.value += 1
return new
}
}

struct S: Incrementing { var value: Int = 0 }

class C: Incrementing { var value: Int = 0 }

var s = S()
s.incremented().value // 1
s.value // 0

var c = C()
c.incremented().value // 1
c.value // 1 (!!!)

This code makes sense for a struct, but is buggy for a class. There's no real fix for that in Swift today.

When to use protocol in Swift

Protocols have more than one use case, but think of it this way:

  • For classes, protocols provide a lightweight inheritance hierarchy separate from the class hierarchy. Given a class Animal and its subclasses Cat, Dog, Bird, and Insect, how would you specify that only Bird and Insect share the fly method?

  • For structs, protocols provide an inheritance hierarchy that would otherwise be missing entirely! A struct has no superstruct. So, given a struct Bird and a struct Insect, how would you specify that they share a fly method?

Now, you could answer that Bird and Insect just happen to have fly methods and that's the end of the story. But that won't do when you need to speak of the set "all types that have a fly method". And you do need to speak of that set when you want the compiler to be able to send the fly method to an object on the grounds that it has a fly method.

The solution is a protocol:

protocol Flier {
func fly()
}

Now Flier is a type. An instance of any type that conforms to Flier can be used where a Flier is expected, and the compiler will let you tell any Flier to fly, so the problem is solved:

var myFlier : Flier = Bird() // if Bird conforms to Flier
myFlier.fly() // legal


Related Topics



Leave a reply



Submit