Protocol is a set of methods and properties for a particular task to which classes, structure, or enumeration can be conformed.
Working with protocols and delegates
Getting ready
The syntax of protocol goes like this:
protocol ProtocolName{ // List of properties and methods goes here.... }
The keyword protocol followed by the protocol name and curly braces are the building blocks of any protocol you need to write. Classes, structures, or enumeration can then conform to it like this:
class SampleClass: ProtocolName{ }
After class name, you type colon and the super class name that this class extend from if any, followed by a list of protocols that you want to conform to with a comma separation.
How to do it...
- Create a new playground file in Xcode called Protocols as usual.
- Complete the following example using the following protocol:
protocol VehicleProtocol{ // properties var name: String {set get} // settable and gettable var canFly: Bool {get} // gettable only (readonly) // instance methods func numberOfWheels() ->Int func move() func stop() // class method staticfuncpopularBrands() -> [String] } class Bicycle: VehicleProtocol{ var name: String var canFly: Bool{ return false } init(name: String){ self.name = name } func numberOfWheels() -> Int { return 2 } func move() { // move logic goes here } func stop() { // stop logic goes here } static func popularBrands() -> [String] { return ["Giant", "GT", "Marin", "Trek", "Merida", "Specialized"] } } class Car: VehicleProtocol{ var name: String var canFly: Bool{ return false } init(name: String){ self.name = name } funcnumberOfWheels() ->Int { return 4 } func move() { // move logic goes here } func stop() { // stop logic goes here } static func popularBrands() -> [String] { return ["Audi", "BMW", "Honda", "Dodge", "Lamborghini", "Lexus"] } } let bicycle1 = Bicycle(name: "Merida 204") bicycle1.numberOfWheels() // 2 let car1 = Car(name: "Honda Civic") car1.canFly // false Bicycle.popularBrands() // Class function // ["Giant", "GT", "Marin", "Trek", "Merida", "Specialized"] Car.popularBrands() // ["Audi", "BMW", "Honda", "Dodge", "Lamborghini", "Lexus"]
How it works...
We started by defining VehicleProtocol that has a list of properties and functions that every vehicle should have. In properties, we have two types of properties: name, which is marked as {get set}, and canFly, which is marked as {get}. When you mark a property {get set}, it means it's gettable and settable, whereas {get} means it only gettable, in other words, it's a read-only property. Then, we added four methods, out of which three methods-numberOfWheels(), move(), and stop()-are instance methods. The last one-popularBrands()- marked as static is a type method. Types methods can be called directly with type name, and there is no need to have instance to call it.
Then, we created two new classes, Bicycle and Car, which conform to VehicleProtocol, and each one will have different implementations.
There's more...
We have already covered the most important parts of protocols and how to use it, but still they have more features, and there are many things that can be done with it. We will try here to mention them one by one to see when and how we can use them.
Mutating methods
Swift allows you mark protocol methods as mutating when it's necessary for these methods to mutate (modify) the instance value itself. This is applicable only in structures and enumerations; we call them value types. Consider this example of using mutating:
protocol Togglable{ mutating func toggle() } enum Switch: Togglable{ case ON case OFF mutating func toggle() { switch self { case .ON: self = OFF default: self = ON } } }
The Switch enum implements the method toggle, as it's defined in the protocol Togglable. Inside toggle(), we could update self-value as function marked as mutating.
Delegation
Delegation is the most commonly used design pattern in iOS. In delegation, you enable types to delegate some of its responsibilities or functions to another instance of another type. To create this design pattern, we use protocols that will contain the list of responsibilities or functions to be delegated. We usually use delegation when you want to respond to actions or retrieve or get information from other sources without needing to know the type of that sources, except that they conform to that protocol. Let's take a look at an example of how to create use delegate:
@objc protocol DownloadManagerDelegate { func didDownloadFile(fileURL: String, fileData: NSData) func didFailToDownloadFile(fileURL: String, error: NSError) } class DownloadManager{ weak var delegate: DownloadManagerDelegate! func downloadFileAtURL(url: String){ // send request to download file // check response and success or failure if let delegate = self.delegate { delegate.didDownloadFile(url, fileData: NSData()) } } } class ViewController: UIViewController, DownloadManagerDelegate{ func startDownload(){ letdownloadManager = DownloadManager() downloadManager.delegate = self } func didDownloadFile(fileURL: String, fileData: NSData) { // present file here } func didFailToDownloadFile(fileURL: String, error: NSError) { // Show error message } }
The protocol DownloadManagerDelegate contains methods that would be called once the specific actions happen to inform the class that conforms to that protocol. The DownloadManager class performs the download tasks asynchronously and informs the delegate with success or failure after it's completed. DownloadManager doesn't need to know which object will use it or any information about it. The only thing it cares about is that the class should conform to the delegate protocol, and that's it.
Class-only protocols
We mentioned before that classes, structures, and enumerations could adopt protocols. The difference among them is that classes are reference types, whereas structures and enumerations are value types. If you find yourself having some specific actions that will be done only via reference types, mark it as class only. To do so, just mark it as follows:
protocol ClassOnlyProtocol: class{ // class only properties and methods go here }
Add a colon : and the class keyword to mark your protocol as class only.
Checking protocol conformance
It would be very useful to check whether an object conforms to a specific protocol or not. It's very useful when you have a list of objects, and only some of them conform to specific protocol. To check for protocol conformance, do the following:
class Rocket{ } var movingObjects = [Bicycle(name: "B1"), Car(name:"C1"), Rocket()] for item in movingObjects{ if let vehicle = item as? VehicleProtocol{ print("Found vehcile with name \(vehicle.name)") vehicle.move() } else{ print("Not a vehcile") } }
We created a list of objects, and some of them conform to VehicleProtocol that we created earlier. Inside the for-loop we casted each item to VehicleProtocol inside if statement; the cast will succeed only if this item already conforms to that protocol.
Optional requirements
You see that when you list your properties and methods in a protocol, the type that conforms to that protocol should adopt to all properties and methods. Skipping one of them will lead to a compiler error. Some protocols may contain methods or properties that are not necessary to implement, especially with delegates. Some delegate methods are meant to notify you something that you don't care about. In that case, you can mark these methods as optional. The keyword optional can be added before properties and methods to mark them as optional. Another thing, the protocol that has optional stuff should be marked with @Objc. Take a look at the following example:
@objc protocol DownloadManagerDelegate { func didDownloadFile(fileURL: String, fileData: NSData) optional func didFailToDownloadFile(fileURL: String, error: NSError) }
It's the new version of DownloadManagerDelegate, which marks didFailToDownloadFile method as optional.