When to Use Protocol in Swift

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

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

Proper way to set up a protocol in Swift 3

I followed silentBob answer, and it was great except that it didn't work for me. I needed to set FlickrAPIRequests class as a singleton, so here is what I did:

protocol FlickrAPIRequestDelegate{

func showFlickrPhoto(photo: UIImage) }

class FlickrAPIRequests{

private static let _instance = FlickrAPIRequests()

static var Instance: FlickrAPIRequests{
return _instance
}

var flickrDelegate: FlickrAPIRequestDelegate? = nil

// Make the Flickr API call
func flickrAPIRequest(_ params: [String: AnyObject], page: Int){

Then in the view controller when I press the search button:

        // Set flickrDelegate protocol to self
FlickrAPIRequests.Instance.flickrDelegate = self

// Call the method for api requests
FlickrAPIRequests.Instance.flickrAPIRequest(paramsDictionary as [String : AnyObject], page: 0)

And Here is the view controller's extension to conform to the protocol:

 extension FlickrViewController: FlickrAPIRequestDelegate{ 

func showFlickrPhoto(photo: UIImage){

self.imgView.image = photo
self.prepFiltersAndViews()
self.dismissAlert()
}

}

When the api method returns a photo I call the protocol method in the main thread:

// Perform this code in the main UI
DispatchQueue.main.async { [unowned self] in
let img = UIImage(data: photoData as Data)
self.flickrDelegate?.showFlickrPhoto(photo: img!)
self.flickrDelegate?.setPhotoTitle(photoTitle: photoTitle)
}

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.

Swift - implement protocol with generic method for different T types

The way you have it written, by conforming to the protocol Client, you have to implement a function get, with a generic, unconstrained argument T. In the example implementations provided, you added a type constraint to the generic parameter T, which does not match the function in the protocol.

There's more than one way you can approach a solution to this problem. Keeping in mind that you said all entities will conform to Mockable, the solution that requires the least change to the code you provided is to use protocol composition to enforce that all parameters T conform to both Decodable and Mockable.

protocol Client {
func get<T: Decodable & Mockable>(_ url: String) -> Promise<T>
}

In your clients, you would implement that function exactly as written.

Now, in your MockClient, you can call T.mock(), and in your real implementations, you can treat T as Decodable as required. Of course, this now requires that even your mock arguments conform to Decodable, but I would assume your mock arguments will be fairly lightweight and thus this wouldn't be a problem.

How does protocol extension work in Swift?

I don't think there is even a need to inherit from NSObject if you are making a BaseClass to be inherited by other classes.

You can simply add classTag in the BaseClass itself, i.e.

class BaseClass {
var classTag: String {
return String(describing: type(of: self))
}
}

class SubClass: BaseClass {
func someFunc() {
print(self.classTag)
}
}

Another option can be to use protocol and protocol extension and provide the default definition of classTag, i.e.

protocol SomeProtocol {
var classTag: String { get }
}

extension SomeProtocol {
var classTag: String {
return String(describing: type(of: self))
}
}

Then, you can conform SomeProtocol to the classes wherever required, i.e.

class SubClass: SomeProtocol {
func someFunc() {
print(self.classTag)
}
}

In any case, inheriting from NSObject is unnecessary since you don't need any NSObject specific functionality for that.



Related Topics



Leave a reply



Submit