8
\$\begingroup\$

I am trying to grasp the concept of delegates and protocols in Swift. So I have implemented my own PlayableMedia protocol with two concrete classes BlueRayMedia and DVDMedia like so:

protocol PlayableMedia {
    func play()
    func stop()
}

class BlueRayMedia:PlayableMedia {

    func play() {
        println("BlueRayMedia is playing")
    }

    func stop() {
        println("BlueRayMedia has stopped playing")
    }

}

class DVDMedia:PlayableMedia {

    func play() {
        println("DVD is playing")
    }

    func stop() {
        println("DVD has stopped playing")
    }


}

and then in my controller I use it like this:

class DVDPlayer {

    var media:PlayableMedia // delegate property

    init(media:PlayableMedia){
        self.media = media
    }

    func didStartPlaying() {
       media.play()
    }

    func didStopPlaying() {
        media.stop()
    }

}

Then I try to use it like this:

var dvdPlayer:DVDPlayer = DVDPlayer(media: BlueRayMedia())

dvdPlayer.didStartPlaying()

Is this use of delegate pattern correct? Can this be improved?

\$\endgroup\$
  • \$\begingroup\$ You current code doesn't really need protocols and delegates; you could (and should) do all of it with regular inheritance. Besides, it seems backwards that didStartPlaying gets called before the actual media actually gets played; if anything it should be opposite - and then delegates could come in handy. None of that is specific to Swift though; the same would apply if this was ObjC (or anything else, really). \$\endgroup\$ – Flambino Jul 1 '14 at 12:09
8
\$\begingroup\$

While this code is technically fine and will work, as Flambino has pointed out, it's rather pointless and with the fact that play() is called in didStartPlaying() it's a bit confusing. I know that's an implementation detail and doesn't have anything to do with the protocol necessarily, however, it does hint at a lack of understanding of protocols/delegates.

There's not a lot out there in the way of Swift code. However, there is a lot out there in the way of Objective-C code. And in Objective-C, there's heavy use of the protocol-delegate pattern as well.

The same Foundation protocol/delegate patterns exist exactly as they are in Objective-C also in Swift, it's just a different syntax. But the method names are all basically the same, and you can definitely pick up on the very distinct naming pattern and get the idea of how protocol/delegate patterns work.

The most common protocol/delegate pattern in iOS development is that of the UITableViewDelegate and UITableViewDataSource (something similar probably exists for NSTableView in OSX). Almost everyone who has done much of an iOS programming has at some point worked with a UITableView.

What actually makes a lot more sense in this case is for PlayableMedia to be a superclass. BlueRayMedia and DVDMedia should be subclasses of PlayableMedia... and these objects should be delegated.

What would make a lot of sense would be something like this:

protocol PlayableMediaDelegate {
    func shouldBeginPlaying() -> Bool
    func willBeginPlaying()
    func didBeginPlaying()

    func shouldStopPlaying() -> Bool
    func willStopPlaying()
    func didStopPlaying()
}

Now the media has a delegate property, which is simply an AnyObject that conforms to the PlayableMediaDelegate protocol.

Any object can call play() on the PlayableMedia object, and then the play method would actually look something like this:

func play() {
    if delegate.shouldBeginPlaying() {
        delegate.willBeginPlaying()
        println("PlayableMedia is now playing...")
        delegate.didBeginPlaying()
    }
}

And stop() would look something like this:

func stop() {
    if delegate.shouldStopPlaying() {
        delegate.willStopPlaying()
        println("PlayableMedia has stopped playing...")
        delegate.didStopPlaying()
    }
}

Where in both cases the println() code is replaced with code that would actually start/stop the playable media from playing.

The point here is with these delegate methods, we let ANY OBJECT delegate our playable media. The shouldBeginPlaying() and shouldStopPlaying() methods give our delegate a chance to prevent/allow that action from happening. If the delegate returns false from this method, the action isn't taken.

The other methods just let our delegate know about the life cycle of the media starting/stopping.

Some examples of things that you might want to do in these delegate methods...

In shouldBeginPlaying(), you may check the media's age restrictions and decide not to play the media. You may also check the device battery life, etc. Any condition in which you might not want to play the media, your delegate can just return false from this method to prevent the action from happening.

In willStopPlaying(), the delegate may want to save the current position in the media so that it can be resumed later. The PlayableMedia class probably shouldn't necessarily do keep track of where it left of last time.

In willBeginPlaying(), you might want to call some methods on the PlayableMedia object to finish setting it up for getting played. Perhaps moving to the point in which was last left off.


The main idea here is that we're writing a class that we want to allow delegation by any sort of unknown object. What you shouldn't do when designing a protocol is start by writing the class you intend to use as the delegate, then write a protocol to match that class, then write the class that needs to be delegated by the first class--that's backwards.

\$\endgroup\$
  • \$\begingroup\$ I knew I was missing something essential in my implementation and you put your finger right on it. Could you please take a few seconds and expand your code example to include classes that adopt the protocol? \$\endgroup\$ – Amit Erandole Jul 2 '14 at 15:18
  • \$\begingroup\$ This afternoon perhaps if I have a chance, I can. Although, the best way to really understand delegate/protocol is to work with a UITableView in iOS. It's probably the most commonly used Foundation protocol/delegate pattern in iOS. Here, the protocol and the class to be delegated is already written. You're just writing a class that conforms to the defined protocol. \$\endgroup\$ – nhgrif Jul 2 '14 at 17:51
  • 1
    \$\begingroup\$ And in the case of UITableView, a series of methods in the datasource protocol are sent out every time you call the reloadData method on the table, meanwhile various methods in the delegate protocol are sent out when the user interacts with the tableview in various ways. \$\endgroup\$ – nhgrif Jul 2 '14 at 17:54
  • \$\begingroup\$ @AmitErandole github.com/nhgrif/SQLConnect/blob/master/SQLConnect/… That's an example of a class conforming to a protocol. I realize it's Objective-C, but the only difference between Objective-C and Swift here is going to be syntax. The method naming scheme is going to be the same. \$\endgroup\$ – nhgrif Jul 5 '14 at 12:02
  • \$\begingroup\$ I want to note that, as coding pattern, it'd be nicer to have delegate variable as an optional. e.g., var delegate:SomeDelegate? We know that it can be nil sometimes. \$\endgroup\$ – sang Jul 28 '14 at 1:44
2
\$\begingroup\$

This is a straight-forward protocol conformance. Whether this is really a delegation pattern is perhaps a little bit more subjective, but again, seems to adhere to Apple's view (https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/Delegation.html):

Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object—the delegate—and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. The delegate may respond to the message by updating the appearance or state of itself or other objects in the application, and in some cases it can return a value that affects how an impending event is handled. The main value of delegation is that it allows you to easily customize the behavior of several objects in one central object.

In such a simple example there is really little (nothing?) to improve in my opinion.

\$\endgroup\$
  • 1
    \$\begingroup\$ To add to @Flambino comment: the idea behind the delegation pattern is that inheritance is not required / not possible. I'd argue that in Amit's example delegation actually, conceptually makes more sense that inheritance: it allows the player to interact with multitude of media. I do agree though that method names are wrong. "willBeginPlaying" probably makes more sense... and call the method before rather than after. "didBeginPlaying" would be another method called afterwards. \$\endgroup\$ – ivanhoe1982 Jul 1 '14 at 12:16
  • 2
    \$\begingroup\$ (You might as well update your answer instead of commenting.) It's a fair point; protocols could well be used for this. But I'd also argue that - conceptually - some sort of "media" is a prerequisite for the player to be of any use. So that media might as well inherit from a common (but perhaps abstract) class with some known functionality. But this is all academic. Again: Either way works. \$\endgroup\$ – Flambino Jul 1 '14 at 13:53
  • 2
    \$\begingroup\$ As an aside: Swift's design is heavily based on protocols, I believe they even use "protocol-based language" in one of the WWDC talks. This, together with the extension mechanism, can very well decrease the reliance on inheritance in the Cocoa world even further. 'Delegation' as a pattern becomes somewhat irrelevant and fades into the background, as protocols and generics enforce this style of coupling in the first place anyway. \$\endgroup\$ – ivanhoe1982 Jul 1 '14 at 14:54
  • \$\begingroup\$ Good point; my experience with Swift is still very limited \$\endgroup\$ – Flambino Jul 1 '14 at 15:33
  • \$\begingroup\$ good point about the method name \$\endgroup\$ – Amit Erandole Jul 1 '14 at 16:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.