Class-Only Protocols in Swift

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.

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.

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.

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]?) { }
}

Swift protocol to only implemented by specific classes

You could add a required method that you only extend for the appropriate classes.

for example:

protocol PeopleProtocol
{
var conformsToPeopleProtocol:Bool { get }
}

extension PeopleProtocol where Self:People
{
var conformsToPeopleProtocol:Bool {return true}
}

class People
{}

class Neighbours:People
{}

extension Neighbours:PeopleProtocol // this works
{}

class Doctors:People,PeopleProtocol // this also works
{}

class Dogs:PeopleProtocol // this will not compile
{}

This could easily be circumvented by a programmer who would want to, but at least it will let the compiler warn you if you try to apply the protocol to other classes.

How to make protocol conformed by only required classes?

I am not sure if below code is what you need, do let me know if that’s what you were looking for, else I will be happy to remove my answer.

protocol A:BaseViewController  {
func execute()
}

protocol B:A {
func confirm()
}

class BaseViewController: UIViewController {

}

class AnotherVC: B {

}

In above code compiler will give error saying-:

'A' requires that 'AnotherVC' inherit from ‘BaseViewController'

Once you inherit AnotherVC from BaseViewController, it will give another error saying-:

Type 'AnotherVC' does not conform to protocol ‘A'

Once you confirm the implementations errors will be resolved-:

class AnotherVC:BaseViewController, B {
func confirm() {

}

func execute() {

}

}

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.


Related Topics



Leave a reply



Submit