patternswiftMinor
Is this a correct use of using protocols and delegate pattern in Swift?
Viewed 0 times
thisswiftdelegateusingcorrectandusepatternprotocols
Problem
I am trying to grasp the concept of delegates and protocols in Swift. So I have implemented my own
and then in my controller I use it like this:
Then I try to use it like this:
Is this use of delegate pattern correct? Can this be improved?
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?
Solution
While this code is technically fine and will work, as Flambino has pointed out, it's rather pointless and with the fact that
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
What actually makes a lot more sense in this case is for
What would make a lot of sense would be something like this:
Now the media has a
Any object can call
And
Where in both cases the
The point here is with these delegate methods, we let ANY OBJECT delegate our playable media. The
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
In
In
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.
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.
Code Snippets
protocol PlayableMediaDelegate {
func shouldBeginPlaying() -> Bool
func willBeginPlaying()
func didBeginPlaying()
func shouldStopPlaying() -> Bool
func willStopPlaying()
func didStopPlaying()
}func play() {
if delegate.shouldBeginPlaying() {
delegate.willBeginPlaying()
println("PlayableMedia is now playing...")
delegate.didBeginPlaying()
}
}func stop() {
if delegate.shouldStopPlaying() {
delegate.willStopPlaying()
println("PlayableMedia has stopped playing...")
delegate.didStopPlaying()
}
}Context
StackExchange Code Review Q#55775, answer score: 8
Revisions (0)
No revisions yet.