I wanted to write a bit about Generics in Swift, but I realized that some of the major powers of Generics require the use of protocols, so I felt I should start by talking about them first.
In Swift, protocols are basically a named contract that your classes can conform to. If your class says it conforms to Equatable, then it better fulfill all of the required functionality to make it equatable. They are rather similar to interfaces in C# or Java. They list off the function prototypes and variable declarations, as well as stating whether they are required or optional, but don’t actually do anything. It is up to class, structs, or enumerations that claim to conform to the protocol to actually provide the functionality.
Defining a Protocol
This is pretty easy. It is very much like defining a class in Swift, except you write protocol instead, observe:
protocol Vehicle { //Functions and properties prototypes go here. }
Then you just fill it with what you want.
Protocol Adoption by classes, structs, and enumerations
In Swift, when you declare your class, if you want it to have a superclass, you follow the class name with a colon, and then the name of that superclass. If you want it then to conform to some protocols, you then follow that superclass with a comma, and then the name of the protocol. In my opinion, this makes it less obvious what is the superclass and what is a protocol. Nonetheless though, the current way to adopt a protocol is shown below:
class UserCar: PlayableCharacterSuperclass, Vehicle { //Function and property implementations go here. }
Declaring Requirements
Now that we have the protocol’s shell made, we now need to write what that protocol requires. These can be either properties or methods.
Protocol Properties
Firstly, properties in protocols must always be variables, and cannot be constants. This doesn’t mean that they must all be writeable, but as far as the declaration, they must be var in the protocol. In Swift 1.1, constants must have their values given when they are created, so this makes sense (because protocols cannot implement that, only give it a name, type, and list of capabilities). Swift 1.2 has changed some of these rules about constants, but does not seem to have changed this requirement for protocols so far. To determine whether it is writeable or not, you append either { get set } or { get }, as shown below:
protocol Vehicle { var numberOfWheels: Int { get } //readonly var color: UIColor { get set } //read-write }
This does not necessarily mean they have to be var inside the class that implements them though. It just has to be able to do what is requested. If you want numberOfWheels to be settable inside the class, then you can use a var as it requests here. However, if it is just a constant defined only once, a let constant can be used in the actual class, since it fulfills the “get” capability.
The “color” property is { get set }, so it must be settable and thus must be a var in the class that conforms to this protocol.
Protocol Methods
As mentioned before, to define a method in a protocol you just define its prototype (just the first line with the name, parameters, and return types). For a simple method, you would define it like this:
func renderImageOfCar() -> UIImage
There is a caveat though. Classes are reference types. When you assign a reference type to a new variable, you just pass a reference to that instance of it, so if you change the first variable that instance was assigned to, you will change what both variables are connected to. When you assign a value type to a new variable, the whole thing is copied to put there. Structs and enumerations are value types.
If you want to actually change member variables of a struct, you must precede a function with the keyword “mutating”. If you want your protocol to work on value AND reference types when mutating instance variables, you must add mutating to the protocol’s method prototype.
mutating func drive()
You do not need mutating before functions that mutate member variables in classes. While you must write mutating in your protocol if you want it to work with classes, structs, and enumerations, you do not actually have to write mutating before the function’s actual implementation if you are writing a class. You still do for the structs and enumerations though.
Required and Optional Elements in Protocols
First things first, by default in Swift, anything in a protocol is required. If you just do what you’ve seen earlier in this article, you will have made those properties and methods required.
You have 2 choices to make them optional, both have pros and cons.
Irrelevant of which way you do it though, you can call them via Swift’s optional chaining, since the whole point is the user may not have implemented them. You have to know if they are valid in some fashion, so you can treat either way like a Swift optional to know.
Objective-C style Protocol Optional Requirements
This is the reimplementation of the classic way to make a protocol requirement optional. You have to do 2 things: precede your protocol declaration with @objc, and precede your function or variable declaration with optional like so:
@objc protocol driveableProtocol { @objc optional var unimportantNumber: Int { get set } }
When it is declared like this, if your class implements the DriveableProtocol, it does not have to mention unimportantNumber anywhere. It can if you think it should, but it does not have to. There are some caveats to this though. I did say if your class implements this on purpose. If you use @objc, it MUST be a class, you cannot use an @objc protocol for enumerations or structs in Swift.
This is not the only place you ever use @objc. I don’t want to get into it much here, but it generally tells the compiler that what you declared is available to use in Objective-C code. Unfortunately though you have to use it in protocols for optional requirements even if you don’t touch Objective-C.
Swift style Protocol Optional Requirements
Since you have to treat these optional requirements with Swift’s optional syntax anyway…. why not use Swift optionals? If you do it this way, you do not need to use the @objc at the beginning, so you can still use your protocol for structs and enumerations. The downside is that your class/struct/enumeration must implement it, but can just declare it as an optional and just leave it set to nil.
You would just create your required optional requirements (sounds weird to say, but accurate in this case) in this way:
protocol somethingProtocol { var someString: String? { get set } func doSomething(inputA: Int) -> String? }
In the case of the function, you do have to implement it, but you can just have it return nil. If you have a function without return values, you can just leave the function body blank when implementing it. It is inelegant, but it is another way to have optional functionality in protocols, that lets you just stay in the land of Swift, and not have to break its functionality with structs and enumerations.
Conclusion
There are definitely more aspects about protocol in Swift to talk about, but I felt these were the most important. With this you can create them, implement them, and make some requirements optional. They are quite useful when you want to decouple your code from requiring a specific class. If you want to compare different values, you could have multiple methods like compareStrings, compareInts, compareDoubles, or you could just use the “==” operator. The Equatable protocol requires that the class implement that operator.
I hope you found this article helpful. If you did, please don’t hesitate to share this post on Twitter or your social media of choice, every share helps. Of course, if you have any questions, don’t hesitate to contact me on the Contact Page, or on Twitter @CodingExplorer, and I’ll see what I can do. Thanks!