Protocol, mutation and instance variable declaration with protocol type in Swift

Yesterday I discovered something interesting at work whilst I was trying to declare an instance variable that conforms to a protocol in Swift. Basically, I tried declaring within my class a new required instance that conforms to a new protocol I created. What I did was the following:

protocol MyProtocol {
var sharedInstance: Any? { get set }

As you can see above MyProtocol declares a new variable which is a shared instance for any object, essentially a singleton (I know, I know, the anti-pattern). My singleton will be managed via a container thus every time this protocol is resolved the same instance will be shared with the class or struct requiring such instance and I don’t want to have a reference to the container for proper inversion of control which I am big fan of since I was introduced to it in my former job.

Next I proceeded to create an implementation that conforms to that protocol and the one that would be normally used in production.

final class DefaultMyProtocol: MyProtocol {
var sharedInstance: Any? = nil

Then I declared the new dependency in the class that required setting the sharedInstance in MyProtocol. The sharedInstance will be set based on GET Request to my server, and depending on the server response I might or might not set the sharedInstance variable.

final class MyProtocolSharedInstanceSetter {

private let myProtocol: MyProtocol

init(myProtocol: MyProtocol, myApi: MyApi) {
self.myProtocol = myProtocol
myApi.getRequest(onCompletion: { [weak self] in myProtocol.sharedInstance = $0 }

I ran Build and got the following error by the compiler:

Cannot assign to property: 'myProtocol' is a 'let' constant

Now my head was starting to challenge assumption, even the most basic ones. But then I remembered how bad the messaging from the Swift compiler still is, and realised that a protocol can be implemented by classes, structs and enums thus the MyProtocolSharedInstanceSetter class holding a reference to MyProtocol does not know whether the instance is a struct or a class, but I do; it’s a class not a struct or a enum therefore my instance does not mutate and that is the whole point of the singleton.


Class-Only Protocols

You can limit protocol adoption to class types (and not structures or enumerations) by adding the class keyword to a protocol’s inheritance list. The class keyword must always appear first in a protocol’s inheritance list, before any inherited protocols

Jump to the Class-Only Protocols here for more information.

By limiting MyProtocol to only be implemented by class types I did the following:

protocol MyProtocol: class {
var sharedInstance: Any? { get set }

Notice I added the class keyword as the first in the inheritance list. And that it folks!

P.S. I know my blog has been quite empty by I am taking measures to keep sharing what I keep learning on my day to day.

Originally published at Anurag Ajwani.

Senior iOS Engineer @ Onfido. Writing weekly blogs on iOS and programming. Follow me to stay tuned!

Senior iOS Engineer @ Onfido. Writing weekly blogs on iOS and programming. Follow me to stay tuned!