Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Delegates in Swift 2 Introduction to Delegates Implementing a Delegate

Confusing...

It became confusing to me when you assigned a class to a variable

race.delegate = tracker

Also the ability to call for a function that is outside horse race scope and inside the tracker scope

    override func start() {
        delegate?.raceDidStart()
    }

I guess you were talking about composition here. But the effort to understand composition just drove my attention away from the delegate subject.

I imagine would be good to advise your students to learn a bit more about composition, as you did with protocols, before taking this class.

1 Answer

Not sure where the confusion lies, is it perhaps with the choice of stored property name "delegate"? Would it help if the stored prop name was changed to something like "tracker"? So the code would become:

class Race {
    var laps: Int = 0
    var tracker: RaceDelegate?

    ....methods (snip)....
}

class HorseRace: Race {
    ....init_code (snip)....

    override func start() {
        tracker?.raceDidStart()
    }

    override func updateProgress() {
        laps += 1
        tracker?.raceStatus(laps, first: Horse())
    }

    override func end() {
        tracker?.raceDidEnd(Horse())
    }
}

let participants: [Horse] = [ Horse(), Horse(), Horse() ]
let race = HorseRace(laps: 4, horses: participants)

let theTracker = Tracker()
race.tracker = theTracker

This is also perfectly valid code. Basically, what we're doing is assigning an instance of Tracker to the horseRace's tracker stored property. This works because the tracker stored property on Race class excepts any type that conforms to the RaceDelegate protocol, which our Tracker class does i.e. class Tracker: RaceDelegate { }

Here's a more fleshed out example I came up with as a gist, based on the example from the video. Copy 'n' paste it into a Playground and experiment with it https://gist.github.com/Sironfoot/8ad03351009c9c6b85d3

Hope this helps!

class Horse {
    func giddyUp() {}
}
class Race {
    var laps: Int = 0
    var delegate: Tracker?

    func start() {}
    func updateProgress() {}
    func end() {}
}
class HorseRace: Race {
    let participants: [Horse]
    init(laps: Int, horses: [Horse]) {
        self.participants = horses
        super.init()
        self.laps = laps
    }

    override func start() {
        delegate?.raceDidStart()
    }

    override func updateProgress() {
        laps += 1
        delegate?.raceStatus(laps, first: Horse())
    }
    override func end() {
        delegate?.raceDidEnd(Horse())
    }
}
class Tracker {
    func raceDidStart() {
        println("Tracker notified that the race started!")
    }

    func raceStatus(lapNumber: Int, first: AnyObject) {
        println("Tracker notified that race status has been updated! Current lap: \(lapNumber) with first place: \(first)")
    }

    func raceDidEnd(winner: AnyObject) {
        println("Tracker notified that the race ended! The winner is: \(winner)")
    }

}

This class example would work without protocols, but not without composition.

Some of my frustration comes from the fact that there is a complicated correlation of composition and protocols in here (for someone with not so much experience) while the focus of the class is delegation.

Unless there is a rule on programming that says delegation is not truly delegation if designed without protocols and composition I would not approach a delegate class like that.

Guilherme Kunzler: the problem is now that Race class is tightly coupled to Tracker, which is bad coding practice, an anti-pattern. Say for instance if you wanted to Tweet the results of your horse race, you could implement a TweetTracker class like so:

class TweetTracker: RaceDelegate { }

...or maybe a BlogTracker that blogged the results...

class BlogTracker: RaceDelegate { }

You could also have a TextMessageTracker that alerted people via text message who had signed up. These implementations are going to be completely different, and you don't want to embed that functionality into your Race class, or tightly couple it to a class that does (my Race class now does Tweeting and Blogging and Text Messaging...WTF!?), that violates the Single Responsibility Principle. Your Race class should handle racing and nothing more, and delegate out the tracking to another class.

Furthermore, the Race class should NOT care how that tracking is implemented, only that the 'thing' doing the tracking (TweetTracker, BlogTracker etc.) implements the RaceDelegate protocol.

It means if was going to add a TweetTracker I would have to do

class Race {
    var laps: Int = 0
    var delegate: Tracker?
    var delegete2: Tweet?

and then update all the methods in race to call the methods on TweetTracker

Yes that makes sense. By doing what you said and what the example highlights I am not changing anything inside Race, but mostly making these "modifications" while creating and manipulating the objects.

That makes sense... thanks for the explanation.